Compare commits
35 Commits
Author | SHA1 | Date | |
---|---|---|---|
371e6fa24c | |||
1d9b63cc6a | |||
795539bc82 | |||
dd2e851e95 | |||
2ca70eb9a0 | |||
6575952432 | |||
9a28ba72b1 | |||
34a9922b57 | |||
4df22eb867 | |||
f241f88558 | |||
90455a05e6 | |||
edc76883db | |||
427b7d06b5 | |||
331c07807f | |||
a772b073ec | |||
870d9599cc | |||
2dbbc9bc05 | |||
72634c80f4 | |||
bebd8eb822 | |||
f4b74e9ce1 | |||
4e19b36ad7 | |||
b16923a902 | |||
7e58b21f3d | |||
4fbc978e73 | |||
1a45dc8df8 | |||
f037fcba9a | |||
59a0c7cfd8 | |||
6f50b9bdb0 | |||
f11d663df7 | |||
19a949d0bf | |||
feec5ef7b3 | |||
9864675a0b | |||
06bff0159c | |||
04ed8c1f83 | |||
ad8d5b9b56 |
18
.github/dependabot.yml
vendored
18
.github/dependabot.yml
vendored
@ -13,7 +13,7 @@ updates:
|
||||
|
||||
- package-ecosystem: nuget
|
||||
directory: /
|
||||
open-pull-requests-limit: 5
|
||||
open-pull-requests-limit: 10
|
||||
schedule:
|
||||
interval: daily
|
||||
labels:
|
||||
@ -22,3 +22,19 @@ updates:
|
||||
- marysaka
|
||||
commit-message:
|
||||
prefix: nuget
|
||||
groups:
|
||||
Avalonia:
|
||||
patterns:
|
||||
- "*Avalonia*"
|
||||
Silk.NET:
|
||||
patterns:
|
||||
- "Silk.NET*"
|
||||
OpenTK:
|
||||
patterns:
|
||||
- "OpenTK*"
|
||||
SixLabors:
|
||||
patterns:
|
||||
- "SixLabors*"
|
||||
NUnit:
|
||||
patterns:
|
||||
- "NUnit*"
|
||||
|
42
.github/labeler.yml
vendored
42
.github/labeler.yml
vendored
@ -1,33 +1,35 @@
|
||||
audio: 'src/Ryujinx.Audio*/**'
|
||||
audio:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: 'src/Ryujinx.Audio*/**'
|
||||
|
||||
cpu:
|
||||
- 'src/ARMeilleure/**'
|
||||
- 'src/Ryujinx.Cpu/**'
|
||||
- 'src/Ryujinx.Memory/**'
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: ['src/ARMeilleure/**', 'src/Ryujinx.Cpu/**', 'src/Ryujinx.Memory/**']
|
||||
|
||||
gpu:
|
||||
- 'src/Ryujinx.Graphics.*/**'
|
||||
- 'src/Spv.Generator/**'
|
||||
- 'src/Ryujinx.ShaderTools/**'
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: ['src/Ryujinx.Graphics.*/**', 'src/Spv.Generator/**', 'src/Ryujinx.ShaderTools/**']
|
||||
|
||||
'graphics-backend:opengl':
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: 'src/Ryujinx.Graphics.OpenGL/**'
|
||||
|
||||
'graphics-backend:opengl': 'src/Ryujinx.Graphics.OpenGL/**'
|
||||
'graphics-backend:vulkan':
|
||||
- 'src/Ryujinx.Graphics.Vulkan/**'
|
||||
- 'src/Spv.Generator/**'
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: ['src/Ryujinx.Graphics.Vulkan/**', 'src/Spv.Generator/**']
|
||||
|
||||
gui:
|
||||
- 'src/Ryujinx/**'
|
||||
- 'src/Ryujinx.Ui.Common/**'
|
||||
- 'src/Ryujinx.Ui.LocaleGenerator/**'
|
||||
- 'src/Ryujinx.Ava/**'
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: ['src/Ryujinx/**', 'src/Ryujinx.Ui.Common/**', 'src/Ryujinx.Ui.LocaleGenerator/**', 'src/Ryujinx.Ava/**']
|
||||
|
||||
horizon:
|
||||
- 'src/Ryujinx.HLE/**'
|
||||
- 'src/Ryujinx.Horizon*/**'
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: ['src/Ryujinx.HLE/**', 'src/Ryujinx.Horizon/**']
|
||||
|
||||
kernel: 'src/Ryujinx.HLE/HOS/Kernel/**'
|
||||
kernel:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: 'src/Ryujinx.HLE/HOS/Kernel/**'
|
||||
|
||||
infra:
|
||||
- '.github/**'
|
||||
- 'distribution/**'
|
||||
- 'Directory.Packages.props'
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: ['.github/**', 'distribution/**', 'Directory.Packages.props']
|
||||
|
10
.github/workflows/build.yml
vendored
10
.github/workflows/build.yml
vendored
@ -79,21 +79,21 @@ jobs:
|
||||
if: github.event_name == 'pull_request' && matrix.os == 'ubuntu-latest'
|
||||
|
||||
- name: Upload Ryujinx artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
||||
path: publish
|
||||
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||
|
||||
- name: Upload Ryujinx.Headless.SDL2 artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
||||
path: publish_sdl2_headless
|
||||
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||
|
||||
- name: Upload Ryujinx.Ava artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
||||
path: publish_ava
|
||||
@ -144,14 +144,14 @@ jobs:
|
||||
./distribution/macos/create_macos_build_headless.sh . publish_tmp_headless publish_headless ./distribution/macos/entitlements.xml "${{ env.RYUJINX_BASE_VERSION }}" "${{ steps.git_short_hash.outputs.result }}" "${{ matrix.configuration }}" "-p:ExtraDefineConstants=DISABLE_UPDATER"
|
||||
|
||||
- name: Upload Ryujinx.Ava artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal
|
||||
path: "publish_ava/*.tar.gz"
|
||||
if: github.event_name == 'pull_request'
|
||||
|
||||
- name: Upload Ryujinx.Headless.SDL2 artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal
|
||||
path: "publish_headless/*.tar.gz"
|
||||
|
2
.github/workflows/checks.yml
vendored
2
.github/workflows/checks.yml
vendored
@ -63,7 +63,7 @@ jobs:
|
||||
|
||||
- name: Upload report
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: dotnet-format
|
||||
path: ./*-report.json
|
||||
|
2
.github/workflows/nightly_pr_comment.yml
vendored
2
.github/workflows/nightly_pr_comment.yml
vendored
@ -11,7 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }}
|
||||
steps:
|
||||
- uses: actions/github-script@v7
|
||||
- uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const {owner, repo} = context.repo;
|
||||
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@ -34,7 +34,7 @@ jobs:
|
||||
shell: bash
|
||||
|
||||
- name: Create tag
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
github.rest.git.createRef({
|
||||
|
@ -3,34 +3,35 @@
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="Avalonia" Version="11.0.5" />
|
||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.0.5" />
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="11.0.5" />
|
||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.0.5" />
|
||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.5" />
|
||||
<PackageVersion Include="Avalonia.Svg" Version="11.0.0.3" />
|
||||
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0.3" />
|
||||
<PackageVersion Include="Avalonia" Version="11.0.7" />
|
||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.0.7" />
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="11.0.7" />
|
||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.0.7" />
|
||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.7" />
|
||||
<PackageVersion Include="Avalonia.Svg" Version="11.0.0.10" />
|
||||
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0.10" />
|
||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||
<PackageVersion Include="Concentus" Version="1.1.7" />
|
||||
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
|
||||
<PackageVersion Include="DynamicData" Version="7.14.2" />
|
||||
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.4" />
|
||||
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" />
|
||||
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
||||
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
||||
<PackageVersion Include="jp2masa.Avalonia.Flexbox" Version="0.3.0-beta.4" />
|
||||
<PackageVersion Include="LibHac" Version="0.19.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.7.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
|
||||
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.2.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
|
||||
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.0" />
|
||||
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||
<PackageVersion Include="NetCoreServer" Version="7.0.0" />
|
||||
<PackageVersion Include="NUnit" Version="3.13.3" />
|
||||
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
|
||||
<PackageVersion Include="OpenTK.Core" Version="4.8.1" />
|
||||
<PackageVersion Include="OpenTK.Graphics" Version="4.8.1" />
|
||||
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.8.1" />
|
||||
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.8.1" />
|
||||
<PackageVersion Include="OpenTK.Core" Version="4.8.2" />
|
||||
<PackageVersion Include="OpenTK.Graphics" Version="4.8.2" />
|
||||
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.8.2" />
|
||||
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.8.2" />
|
||||
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build13" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||
@ -46,9 +47,8 @@
|
||||
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
|
||||
<PackageVersion Include="SPB" Version="0.0.4-build28" />
|
||||
<PackageVersion Include="System.Drawing.Common" Version="8.0.0" />
|
||||
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="7.0.3" />
|
||||
<PackageVersion Include="System.IO.Hashing" Version="8.0.0" />
|
||||
<PackageVersion Include="System.Management" Version="8.0.0" />
|
||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
@ -10,14 +10,25 @@
|
||||
<string>Ryujinx</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>Ryujinx.icns</string>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>nca</string>
|
||||
<string>nro</string>
|
||||
<string>nso</string>
|
||||
<string>nsp</string>
|
||||
<string>xci</string>
|
||||
</array>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>nca</string>
|
||||
<string>nro</string>
|
||||
<string>nso</string>
|
||||
<string>nsp</string>
|
||||
<string>xci</string>
|
||||
</array>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Nintendo Switch File</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Default</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.ryujinx.Ryujinx</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
|
8
distribution/macos/shortcut-launch-script.sh
Normal file
8
distribution/macos/shortcut-launch-script.sh
Normal file
@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
launch_arch="$(uname -m)"
|
||||
if [ "$(sysctl -in sysctl.proc_translated)" = "1" ]
|
||||
then
|
||||
launch_arch="arm64"
|
||||
fi
|
||||
|
||||
arch -$launch_arch {0} {1}
|
@ -9,7 +9,7 @@ namespace ARMeilleure.Common
|
||||
/// Represents a table of guest address to a value.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntry">Type of the value</typeparam>
|
||||
unsafe class AddressTable<TEntry> : IDisposable where TEntry : unmanaged
|
||||
public unsafe class AddressTable<TEntry> : IDisposable where TEntry : unmanaged
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a level in an <see cref="AddressTable{TEntry}"/>.
|
||||
|
@ -517,7 +517,10 @@ namespace ARMeilleure.Decoders
|
||||
SetA64("0x00111100>>>xxx100111xxxxxxxxxx", InstName.Sqrshrn_V, InstEmit.Sqrshrn_V, OpCodeSimdShImm.Create);
|
||||
SetA64("0111111100>>>xxx100011xxxxxxxxxx", InstName.Sqrshrun_S, InstEmit.Sqrshrun_S, OpCodeSimdShImm.Create);
|
||||
SetA64("0x10111100>>>xxx100011xxxxxxxxxx", InstName.Sqrshrun_V, InstEmit.Sqrshrun_V, OpCodeSimdShImm.Create);
|
||||
SetA64("010111110>>>>xxx011101xxxxxxxxxx", InstName.Sqshl_Si, InstEmit.Sqshl_Si, OpCodeSimdShImm.Create);
|
||||
SetA64("0>001110<<1xxxxx010011xxxxxxxxxx", InstName.Sqshl_V, InstEmit.Sqshl_V, OpCodeSimdReg.Create);
|
||||
SetA64("0000111100>>>xxx011101xxxxxxxxxx", InstName.Sqshl_Vi, InstEmit.Sqshl_Vi, OpCodeSimdShImm.Create);
|
||||
SetA64("010011110>>>>xxx011101xxxxxxxxxx", InstName.Sqshl_Vi, InstEmit.Sqshl_Vi, OpCodeSimdShImm.Create);
|
||||
SetA64("0101111100>>>xxx100101xxxxxxxxxx", InstName.Sqshrn_S, InstEmit.Sqshrn_S, OpCodeSimdShImm.Create);
|
||||
SetA64("0x00111100>>>xxx100101xxxxxxxxxx", InstName.Sqshrn_V, InstEmit.Sqshrn_V, OpCodeSimdShImm.Create);
|
||||
SetA64("0111111100>>>xxx100001xxxxxxxxxx", InstName.Sqshrun_S, InstEmit.Sqshrun_S, OpCodeSimdShImm.Create);
|
||||
|
@ -116,7 +116,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else if (shift >= eSize)
|
||||
{
|
||||
if ((op.RegisterSize == RegisterSize.Simd64))
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
Operand res = context.VectorZeroUpper64(GetVec(op.Rd));
|
||||
|
||||
@ -359,6 +359,16 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
}
|
||||
|
||||
public static void Sqshl_Si(ArmEmitterContext context)
|
||||
{
|
||||
EmitShlImmOp(context, signedDst: true, ShlRegFlags.Signed | ShlRegFlags.Scalar | ShlRegFlags.Saturating);
|
||||
}
|
||||
|
||||
public static void Sqshl_Vi(ArmEmitterContext context)
|
||||
{
|
||||
EmitShlImmOp(context, signedDst: true, ShlRegFlags.Signed | ShlRegFlags.Saturating);
|
||||
}
|
||||
|
||||
public static void Sqshrn_S(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseAdvSimd)
|
||||
@ -1593,6 +1603,99 @@ namespace ARMeilleure.Instructions
|
||||
Saturating = 1 << 3,
|
||||
}
|
||||
|
||||
private static void EmitShlImmOp(ArmEmitterContext context, bool signedDst, ShlRegFlags flags = ShlRegFlags.None)
|
||||
{
|
||||
bool scalar = flags.HasFlag(ShlRegFlags.Scalar);
|
||||
bool signed = flags.HasFlag(ShlRegFlags.Signed);
|
||||
bool saturating = flags.HasFlag(ShlRegFlags.Saturating);
|
||||
|
||||
OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp;
|
||||
|
||||
Operand res = context.VectorZero();
|
||||
|
||||
int elems = !scalar ? op.GetBytesCount() >> op.Size : 1;
|
||||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
Operand ne = EmitVectorExtract(context, op.Rn, index, op.Size, signed);
|
||||
|
||||
Operand e = !saturating
|
||||
? EmitShlImm(context, ne, GetImmShl(op), op.Size)
|
||||
: EmitShlImmSatQ(context, ne, GetImmShl(op), op.Size, signed, signedDst);
|
||||
|
||||
res = EmitVectorInsert(context, res, e, index, op.Size);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
|
||||
private static Operand EmitShlImm(ArmEmitterContext context, Operand op, int shiftLsB, int size)
|
||||
{
|
||||
int eSize = 8 << size;
|
||||
|
||||
Debug.Assert(op.Type == OperandType.I64);
|
||||
Debug.Assert(eSize == 8 || eSize == 16 || eSize == 32 || eSize == 64);
|
||||
|
||||
Operand res = context.AllocateLocal(OperandType.I64);
|
||||
|
||||
if (shiftLsB >= eSize)
|
||||
{
|
||||
Operand shl = context.ShiftLeft(op, Const(shiftLsB));
|
||||
context.Copy(res, shl);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand zeroL = Const(0L);
|
||||
context.Copy(res, zeroL);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private static Operand EmitShlImmSatQ(ArmEmitterContext context, Operand op, int shiftLsB, int size, bool signedSrc, bool signedDst)
|
||||
{
|
||||
int eSize = 8 << size;
|
||||
|
||||
Debug.Assert(op.Type == OperandType.I64);
|
||||
Debug.Assert(eSize == 8 || eSize == 16 || eSize == 32 || eSize == 64);
|
||||
|
||||
Operand lblEnd = Label();
|
||||
|
||||
Operand res = context.Copy(context.AllocateLocal(OperandType.I64), op);
|
||||
|
||||
if (shiftLsB >= eSize)
|
||||
{
|
||||
context.Copy(res, signedSrc
|
||||
? EmitSignedSignSatQ(context, op, size)
|
||||
: EmitUnsignedSignSatQ(context, op, size));
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand shl = context.ShiftLeft(op, Const(shiftLsB));
|
||||
if (eSize == 64)
|
||||
{
|
||||
Operand sarOrShr = signedSrc
|
||||
? context.ShiftRightSI(shl, Const(shiftLsB))
|
||||
: context.ShiftRightUI(shl, Const(shiftLsB));
|
||||
context.Copy(res, shl);
|
||||
context.BranchIf(lblEnd, sarOrShr, op, Comparison.Equal);
|
||||
context.Copy(res, signedSrc
|
||||
? EmitSignedSignSatQ(context, op, size)
|
||||
: EmitUnsignedSignSatQ(context, op, size));
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Copy(res, signedSrc
|
||||
? EmitSignedSrcSatQ(context, shl, size, signedDst)
|
||||
: EmitUnsignedSrcSatQ(context, shl, size, signedDst));
|
||||
}
|
||||
}
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private static void EmitShlRegOp(ArmEmitterContext context, ShlRegFlags flags = ShlRegFlags.None)
|
||||
{
|
||||
bool scalar = flags.HasFlag(ShlRegFlags.Scalar);
|
||||
|
@ -384,7 +384,9 @@ namespace ARMeilleure.Instructions
|
||||
Sqrshrn_V,
|
||||
Sqrshrun_S,
|
||||
Sqrshrun_V,
|
||||
Sqshl_Si,
|
||||
Sqshl_V,
|
||||
Sqshl_Vi,
|
||||
Sqshrn_S,
|
||||
Sqshrn_V,
|
||||
Sqshrun_S,
|
||||
|
@ -4,7 +4,5 @@ namespace ARMeilleure.Memory
|
||||
{
|
||||
IJitMemoryBlock Allocate(ulong size);
|
||||
IJitMemoryBlock Reserve(ulong size);
|
||||
|
||||
ulong GetPageSize();
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ namespace ARMeilleure.Memory
|
||||
|
||||
void Commit(ulong offset, ulong size);
|
||||
|
||||
void MapAsRw(ulong offset, ulong size);
|
||||
void MapAsRx(ulong offset, ulong size);
|
||||
void MapAsRwx(ulong offset, ulong size);
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ namespace ARMeilleure.Memory
|
||||
HostMappedUnsafe,
|
||||
}
|
||||
|
||||
static class MemoryManagerTypeExtensions
|
||||
public static class MemoryManagerTypeExtensions
|
||||
{
|
||||
public static bool IsHostMapped(this MemoryManagerType type)
|
||||
{
|
||||
|
@ -2,7 +2,7 @@ using System;
|
||||
|
||||
namespace ARMeilleure.Memory
|
||||
{
|
||||
class ReservedRegion
|
||||
public class ReservedRegion
|
||||
{
|
||||
public const int DefaultGranularity = 65536; // Mapping granularity in Windows.
|
||||
|
||||
|
@ -5,7 +5,7 @@ using System.Runtime.Versioning;
|
||||
namespace ARMeilleure.Native
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
internal static partial class JitSupportDarwin
|
||||
static partial class JitSupportDarwin
|
||||
{
|
||||
[LibraryImport("libarmeilleure-jitsupport", EntryPoint = "armeilleure_jit_memcpy")]
|
||||
public static partial void Copy(IntPtr dst, IntPtr src, ulong n);
|
||||
|
@ -1,63 +1,14 @@
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Memory;
|
||||
using ARMeilleure.Translation;
|
||||
using ARMeilleure.Translation.Cache;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Signal
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
struct SignalHandlerRange
|
||||
public static class NativeSignalHandlerGenerator
|
||||
{
|
||||
public int IsActive;
|
||||
public nuint RangeAddress;
|
||||
public nuint RangeEndAddress;
|
||||
public IntPtr ActionPointer;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
struct SignalHandlerConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// The byte offset of the faulting address in the SigInfo or ExceptionRecord struct.
|
||||
/// </summary>
|
||||
public int StructAddressOffset;
|
||||
|
||||
/// <summary>
|
||||
/// The byte offset of the write flag in the SigInfo or ExceptionRecord struct.
|
||||
/// </summary>
|
||||
public int StructWriteOffset;
|
||||
|
||||
/// <summary>
|
||||
/// The sigaction handler that was registered before this one. (unix only)
|
||||
/// </summary>
|
||||
public nuint UnixOldSigaction;
|
||||
|
||||
/// <summary>
|
||||
/// The type of the previous sigaction. True for the 3 argument variant. (unix only)
|
||||
/// </summary>
|
||||
public int UnixOldSigaction3Arg;
|
||||
|
||||
public SignalHandlerRange Range0;
|
||||
public SignalHandlerRange Range1;
|
||||
public SignalHandlerRange Range2;
|
||||
public SignalHandlerRange Range3;
|
||||
public SignalHandlerRange Range4;
|
||||
public SignalHandlerRange Range5;
|
||||
public SignalHandlerRange Range6;
|
||||
public SignalHandlerRange Range7;
|
||||
}
|
||||
|
||||
public static class NativeSignalHandler
|
||||
{
|
||||
private delegate void UnixExceptionHandler(int sig, IntPtr info, IntPtr ucontext);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
private delegate int VectoredExceptionHandler(IntPtr exceptionInfo);
|
||||
|
||||
private const int MaxTrackedRanges = 8;
|
||||
public const int MaxTrackedRanges = 8;
|
||||
|
||||
private const int StructAddressOffset = 0;
|
||||
private const int StructWriteOffset = 4;
|
||||
@ -70,125 +21,10 @@ namespace ARMeilleure.Signal
|
||||
|
||||
private const uint EXCEPTION_ACCESS_VIOLATION = 0xc0000005;
|
||||
|
||||
private static ulong _pageSize;
|
||||
private static ulong _pageMask;
|
||||
|
||||
private static readonly IntPtr _handlerConfig;
|
||||
private static IntPtr _signalHandlerPtr;
|
||||
private static IntPtr _signalHandlerHandle;
|
||||
|
||||
private static readonly object _lock = new();
|
||||
private static bool _initialized;
|
||||
|
||||
static NativeSignalHandler()
|
||||
private static Operand EmitGenericRegionCheck(EmitterContext context, IntPtr signalStructPtr, Operand faultAddress, Operand isWrite, int rangeStructSize, ulong pageSize)
|
||||
{
|
||||
_handlerConfig = Marshal.AllocHGlobal(Unsafe.SizeOf<SignalHandlerConfig>());
|
||||
ref SignalHandlerConfig config = ref GetConfigRef();
|
||||
ulong pageMask = pageSize - 1;
|
||||
|
||||
config = new SignalHandlerConfig();
|
||||
}
|
||||
|
||||
public static void Initialize(IJitMemoryAllocator allocator)
|
||||
{
|
||||
JitCache.Initialize(allocator);
|
||||
}
|
||||
|
||||
public static void InitializeSignalHandler(ulong pageSize, Func<IntPtr, IntPtr, IntPtr> customSignalHandlerFactory = null)
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_pageSize = pageSize;
|
||||
_pageMask = pageSize - 1;
|
||||
|
||||
ref SignalHandlerConfig config = ref GetConfigRef();
|
||||
|
||||
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||
{
|
||||
_signalHandlerPtr = Marshal.GetFunctionPointerForDelegate(GenerateUnixSignalHandler(_handlerConfig));
|
||||
|
||||
if (customSignalHandlerFactory != null)
|
||||
{
|
||||
_signalHandlerPtr = customSignalHandlerFactory(UnixSignalHandlerRegistration.GetSegfaultExceptionHandler().sa_handler, _signalHandlerPtr);
|
||||
}
|
||||
|
||||
var old = UnixSignalHandlerRegistration.RegisterExceptionHandler(_signalHandlerPtr);
|
||||
|
||||
config.UnixOldSigaction = (nuint)(ulong)old.sa_handler;
|
||||
config.UnixOldSigaction3Arg = old.sa_flags & 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
config.StructAddressOffset = 40; // ExceptionInformation1
|
||||
config.StructWriteOffset = 32; // ExceptionInformation0
|
||||
|
||||
_signalHandlerPtr = Marshal.GetFunctionPointerForDelegate(GenerateWindowsSignalHandler(_handlerConfig));
|
||||
|
||||
if (customSignalHandlerFactory != null)
|
||||
{
|
||||
_signalHandlerPtr = customSignalHandlerFactory(IntPtr.Zero, _signalHandlerPtr);
|
||||
}
|
||||
|
||||
_signalHandlerHandle = WindowsSignalHandlerRegistration.RegisterExceptionHandler(_signalHandlerPtr);
|
||||
}
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static unsafe ref SignalHandlerConfig GetConfigRef()
|
||||
{
|
||||
return ref Unsafe.AsRef<SignalHandlerConfig>((void*)_handlerConfig);
|
||||
}
|
||||
|
||||
public static unsafe bool AddTrackedRegion(nuint address, nuint endAddress, IntPtr action)
|
||||
{
|
||||
var ranges = &((SignalHandlerConfig*)_handlerConfig)->Range0;
|
||||
|
||||
for (int i = 0; i < MaxTrackedRanges; i++)
|
||||
{
|
||||
if (ranges[i].IsActive == 0)
|
||||
{
|
||||
ranges[i].RangeAddress = address;
|
||||
ranges[i].RangeEndAddress = endAddress;
|
||||
ranges[i].ActionPointer = action;
|
||||
ranges[i].IsActive = 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static unsafe bool RemoveTrackedRegion(nuint address)
|
||||
{
|
||||
var ranges = &((SignalHandlerConfig*)_handlerConfig)->Range0;
|
||||
|
||||
for (int i = 0; i < MaxTrackedRanges; i++)
|
||||
{
|
||||
if (ranges[i].IsActive == 1 && ranges[i].RangeAddress == address)
|
||||
{
|
||||
ranges[i].IsActive = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Operand EmitGenericRegionCheck(EmitterContext context, IntPtr signalStructPtr, Operand faultAddress, Operand isWrite)
|
||||
{
|
||||
Operand inRegionLocal = context.AllocateLocal(OperandType.I32);
|
||||
context.Copy(inRegionLocal, Const(0));
|
||||
|
||||
@ -196,7 +32,7 @@ namespace ARMeilleure.Signal
|
||||
|
||||
for (int i = 0; i < MaxTrackedRanges; i++)
|
||||
{
|
||||
ulong rangeBaseOffset = (ulong)(RangeOffset + i * Unsafe.SizeOf<SignalHandlerRange>());
|
||||
ulong rangeBaseOffset = (ulong)(RangeOffset + i * rangeStructSize);
|
||||
|
||||
Operand nextLabel = Label();
|
||||
|
||||
@ -210,13 +46,12 @@ namespace ARMeilleure.Signal
|
||||
// Is the fault address within this tracked region?
|
||||
Operand inRange = context.BitwiseAnd(
|
||||
context.ICompare(faultAddress, rangeAddress, Comparison.GreaterOrEqualUI),
|
||||
context.ICompare(faultAddress, rangeEndAddress, Comparison.LessUI)
|
||||
);
|
||||
context.ICompare(faultAddress, rangeEndAddress, Comparison.LessUI));
|
||||
|
||||
// Only call tracking if in range.
|
||||
context.BranchIfFalse(nextLabel, inRange, BasicBlockFrequency.Cold);
|
||||
|
||||
Operand offset = context.BitwiseAnd(context.Subtract(faultAddress, rangeAddress), Const(~_pageMask));
|
||||
Operand offset = context.BitwiseAnd(context.Subtract(faultAddress, rangeAddress), Const(~pageMask));
|
||||
|
||||
// Call the tracking action, with the pointer's relative offset to the base address.
|
||||
Operand trackingActionPtr = context.Load(OperandType.I64, Const((ulong)signalStructPtr + rangeBaseOffset + 20));
|
||||
@ -227,7 +62,7 @@ namespace ARMeilleure.Signal
|
||||
|
||||
// Tracking action should be non-null to call it, otherwise assume false return.
|
||||
context.BranchIfFalse(skipActionLabel, trackingActionPtr);
|
||||
Operand result = context.Call(trackingActionPtr, OperandType.I32, offset, Const(_pageSize), isWrite);
|
||||
Operand result = context.Call(trackingActionPtr, OperandType.I32, offset, Const(pageSize), isWrite);
|
||||
context.Copy(inRegionLocal, result);
|
||||
|
||||
context.MarkLabel(skipActionLabel);
|
||||
@ -269,8 +104,7 @@ namespace ARMeilleure.Signal
|
||||
Operand esr = context.Load(OperandType.I64, context.Add(ctxPtr, Const(EsrOffset)));
|
||||
return context.BitwiseAnd(esr, Const(0x40ul));
|
||||
}
|
||||
|
||||
if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
|
||||
else if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
|
||||
{
|
||||
const ulong ErrOffset = 4; // __es.__err
|
||||
Operand err = context.Load(OperandType.I64, context.Add(ctxPtr, Const(ErrOffset)));
|
||||
@ -310,8 +144,7 @@ namespace ARMeilleure.Signal
|
||||
Operand esr = context.Load(OperandType.I64, context.Add(auxPtr, Const(8ul)));
|
||||
return context.BitwiseAnd(esr, Const(0x40ul));
|
||||
}
|
||||
|
||||
if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
|
||||
else if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
|
||||
{
|
||||
const int ErrOffset = 192; // uc_mcontext.gregs[REG_ERR]
|
||||
Operand err = context.Load(OperandType.I64, context.Add(ucontextPtr, Const(ErrOffset)));
|
||||
@ -322,7 +155,7 @@ namespace ARMeilleure.Signal
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
|
||||
private static UnixExceptionHandler GenerateUnixSignalHandler(IntPtr signalStructPtr)
|
||||
public static byte[] GenerateUnixSignalHandler(IntPtr signalStructPtr, int rangeStructSize, ulong pageSize)
|
||||
{
|
||||
EmitterContext context = new();
|
||||
|
||||
@ -335,7 +168,7 @@ namespace ARMeilleure.Signal
|
||||
|
||||
Operand isWrite = context.ICompareNotEqual(writeFlag, Const(0L)); // Normalize to 0/1.
|
||||
|
||||
Operand isInRegion = EmitGenericRegionCheck(context, signalStructPtr, faultAddress, isWrite);
|
||||
Operand isInRegion = EmitGenericRegionCheck(context, signalStructPtr, faultAddress, isWrite, rangeStructSize, pageSize);
|
||||
|
||||
Operand endLabel = Label();
|
||||
|
||||
@ -367,10 +200,10 @@ namespace ARMeilleure.Signal
|
||||
|
||||
OperandType[] argTypes = new OperandType[] { OperandType.I32, OperandType.I64, OperandType.I64 };
|
||||
|
||||
return Compiler.Compile(cfg, argTypes, OperandType.None, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<UnixExceptionHandler>();
|
||||
return Compiler.Compile(cfg, argTypes, OperandType.None, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Code;
|
||||
}
|
||||
|
||||
private static VectoredExceptionHandler GenerateWindowsSignalHandler(IntPtr signalStructPtr)
|
||||
public static byte[] GenerateWindowsSignalHandler(IntPtr signalStructPtr, int rangeStructSize, ulong pageSize)
|
||||
{
|
||||
EmitterContext context = new();
|
||||
|
||||
@ -399,7 +232,7 @@ namespace ARMeilleure.Signal
|
||||
|
||||
Operand isWrite = context.ICompareNotEqual(writeFlag, Const(0L)); // Normalize to 0/1.
|
||||
|
||||
Operand isInRegion = EmitGenericRegionCheck(context, signalStructPtr, faultAddress, isWrite);
|
||||
Operand isInRegion = EmitGenericRegionCheck(context, signalStructPtr, faultAddress, isWrite, rangeStructSize, pageSize);
|
||||
|
||||
Operand endLabel = Label();
|
||||
|
||||
@ -421,7 +254,7 @@ namespace ARMeilleure.Signal
|
||||
|
||||
OperandType[] argTypes = new OperandType[] { OperandType.I64 };
|
||||
|
||||
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<VectoredExceptionHandler>();
|
||||
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Code;
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@ using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
using Ryujinx.Common.Memory.PartialUnmaps;
|
||||
using System;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Signal
|
||||
@ -10,8 +10,28 @@ namespace ARMeilleure.Signal
|
||||
/// <summary>
|
||||
/// Methods to handle signals caused by partial unmaps. See the structs for C# implementations of the methods.
|
||||
/// </summary>
|
||||
internal static class WindowsPartialUnmapHandler
|
||||
internal static partial class WindowsPartialUnmapHandler
|
||||
{
|
||||
[LibraryImport("kernel32.dll", SetLastError = true, EntryPoint = "LoadLibraryA")]
|
||||
private static partial IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName);
|
||||
|
||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||
private static partial IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string procName);
|
||||
|
||||
private static IntPtr _getCurrentThreadIdPtr;
|
||||
|
||||
public static IntPtr GetCurrentThreadIdFunc()
|
||||
{
|
||||
if (_getCurrentThreadIdPtr == IntPtr.Zero)
|
||||
{
|
||||
IntPtr handle = LoadLibrary("kernel32.dll");
|
||||
|
||||
_getCurrentThreadIdPtr = GetProcAddress(handle, "GetCurrentThreadId");
|
||||
}
|
||||
|
||||
return _getCurrentThreadIdPtr;
|
||||
}
|
||||
|
||||
public static Operand EmitRetryFromAccessViolation(EmitterContext context)
|
||||
{
|
||||
IntPtr partialRemapStatePtr = PartialUnmapState.GlobalState;
|
||||
@ -20,7 +40,7 @@ namespace ARMeilleure.Signal
|
||||
// Get the lock first.
|
||||
EmitNativeReaderLockAcquire(context, IntPtr.Add(partialRemapStatePtr, PartialUnmapState.PartialUnmapLockOffset));
|
||||
|
||||
IntPtr getCurrentThreadId = WindowsSignalHandlerRegistration.GetCurrentThreadIdFunc();
|
||||
IntPtr getCurrentThreadId = GetCurrentThreadIdFunc();
|
||||
Operand threadId = context.Call(Const((ulong)getCurrentThreadId), OperandType.I32);
|
||||
Operand threadIndex = EmitThreadLocalMapIntGetOrReserve(context, localCountsPtr, threadId, Const(0));
|
||||
|
||||
@ -137,17 +157,6 @@ namespace ARMeilleure.Signal
|
||||
return context.Add(structsPtr, context.SignExtend32(OperandType.I64, offset));
|
||||
}
|
||||
|
||||
#pragma warning disable IDE0051 // Remove unused private member
|
||||
private static void EmitThreadLocalMapIntRelease(EmitterContext context, IntPtr threadLocalMapPtr, Operand threadId, Operand index)
|
||||
{
|
||||
Operand offset = context.Multiply(index, Const(sizeof(int)));
|
||||
Operand idsPtr = Const((ulong)IntPtr.Add(threadLocalMapPtr, ThreadLocalMap<int>.ThreadIdsOffset));
|
||||
Operand idPtr = context.Add(idsPtr, context.SignExtend32(OperandType.I64, offset));
|
||||
|
||||
context.CompareAndSwap(idPtr, threadId, Const(0));
|
||||
}
|
||||
#pragma warning restore IDE0051
|
||||
|
||||
private static void EmitAtomicAddI32(EmitterContext context, Operand ptr, Operand additive)
|
||||
{
|
||||
Operand loop = Label();
|
||||
|
@ -1,44 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ARMeilleure.Signal
|
||||
{
|
||||
unsafe partial class WindowsSignalHandlerRegistration
|
||||
{
|
||||
[LibraryImport("kernel32.dll")]
|
||||
private static partial IntPtr AddVectoredExceptionHandler(uint first, IntPtr handler);
|
||||
|
||||
[LibraryImport("kernel32.dll")]
|
||||
private static partial ulong RemoveVectoredExceptionHandler(IntPtr handle);
|
||||
|
||||
[LibraryImport("kernel32.dll", SetLastError = true, EntryPoint = "LoadLibraryA")]
|
||||
private static partial IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName);
|
||||
|
||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||
private static partial IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string procName);
|
||||
|
||||
private static IntPtr _getCurrentThreadIdPtr;
|
||||
|
||||
public static IntPtr RegisterExceptionHandler(IntPtr action)
|
||||
{
|
||||
return AddVectoredExceptionHandler(1, action);
|
||||
}
|
||||
|
||||
public static bool RemoveExceptionHandler(IntPtr handle)
|
||||
{
|
||||
return RemoveVectoredExceptionHandler(handle) != 0;
|
||||
}
|
||||
|
||||
public static IntPtr GetCurrentThreadIdFunc()
|
||||
{
|
||||
if (_getCurrentThreadIdPtr == IntPtr.Zero)
|
||||
{
|
||||
IntPtr handle = LoadLibrary("kernel32.dll");
|
||||
|
||||
_getCurrentThreadIdPtr = GetProcAddress(handle, "GetCurrentThreadId");
|
||||
}
|
||||
|
||||
return _getCurrentThreadIdPtr;
|
||||
}
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ namespace ARMeilleure.Translation
|
||||
/// </summary>
|
||||
/// <typeparam name="TK">Key</typeparam>
|
||||
/// <typeparam name="TV">Value</typeparam>
|
||||
class IntervalTree<TK, TV> where TK : IComparable<TK>
|
||||
public class IntervalTree<TK, TV> where TK : IComparable<TK>
|
||||
{
|
||||
private const int ArrayGrowthSize = 32;
|
||||
|
||||
|
@ -57,9 +57,6 @@ namespace ARMeilleure.Translation
|
||||
private Thread[] _backgroundTranslationThreads;
|
||||
private volatile int _threadCount;
|
||||
|
||||
// FIXME: Remove this once the init logic of the emulator will be redone.
|
||||
public static readonly ManualResetEvent IsReadyForTranslation = new(false);
|
||||
|
||||
public Translator(IJitMemoryAllocator allocator, IMemoryManager memory, bool for64Bits)
|
||||
{
|
||||
_allocator = allocator;
|
||||
@ -76,14 +73,9 @@ namespace ARMeilleure.Translation
|
||||
CountTable = new EntryTable<uint>();
|
||||
Functions = new TranslatorCache<TranslatedFunction>();
|
||||
FunctionTable = new AddressTable<ulong>(for64Bits ? _levels64Bit : _levels32Bit);
|
||||
Stubs = new TranslatorStubs(this);
|
||||
Stubs = new TranslatorStubs(FunctionTable);
|
||||
|
||||
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
||||
|
||||
if (memory.Type.IsHostMapped())
|
||||
{
|
||||
NativeSignalHandler.InitializeSignalHandler(allocator.GetPageSize());
|
||||
}
|
||||
}
|
||||
|
||||
public IPtcLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
|
||||
@ -105,8 +97,6 @@ namespace ARMeilleure.Translation
|
||||
{
|
||||
if (Interlocked.Increment(ref _threadCount) == 1)
|
||||
{
|
||||
IsReadyForTranslation.WaitOne();
|
||||
|
||||
if (_ptc.State == PtcState.Enabled)
|
||||
{
|
||||
Debug.Assert(Functions.Count == 0);
|
||||
|
@ -1,3 +1,4 @@
|
||||
using ARMeilleure.Common;
|
||||
using ARMeilleure.Instructions;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
@ -14,11 +15,11 @@ namespace ARMeilleure.Translation
|
||||
/// </summary>
|
||||
class TranslatorStubs : IDisposable
|
||||
{
|
||||
private static readonly Lazy<IntPtr> _slowDispatchStub = new(GenerateSlowDispatchStub, isThreadSafe: true);
|
||||
private readonly Lazy<IntPtr> _slowDispatchStub;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
private readonly Translator _translator;
|
||||
private readonly AddressTable<ulong> _functionTable;
|
||||
private readonly Lazy<IntPtr> _dispatchStub;
|
||||
private readonly Lazy<DispatcherFunction> _dispatchLoop;
|
||||
private readonly Lazy<WrapperFunction> _contextWrapper;
|
||||
@ -83,13 +84,14 @@ namespace ARMeilleure.Translation
|
||||
/// Initializes a new instance of the <see cref="TranslatorStubs"/> class with the specified
|
||||
/// <see cref="Translator"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="translator"><see cref="Translator"/> instance to use</param>
|
||||
/// <param name="functionTable">Function table used to store pointers to the functions that the guest code will call</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="translator"/> is null</exception>
|
||||
public TranslatorStubs(Translator translator)
|
||||
public TranslatorStubs(AddressTable<ulong> functionTable)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(translator);
|
||||
ArgumentNullException.ThrowIfNull(functionTable);
|
||||
|
||||
_translator = translator;
|
||||
_functionTable = functionTable;
|
||||
_slowDispatchStub = new(GenerateSlowDispatchStub, isThreadSafe: true);
|
||||
_dispatchStub = new(GenerateDispatchStub, isThreadSafe: true);
|
||||
_dispatchLoop = new(GenerateDispatchLoop, isThreadSafe: true);
|
||||
_contextWrapper = new(GenerateContextWrapper, isThreadSafe: true);
|
||||
@ -151,15 +153,15 @@ namespace ARMeilleure.Translation
|
||||
context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset())));
|
||||
|
||||
// Check if guest address is within range of the AddressTable.
|
||||
Operand masked = context.BitwiseAnd(guestAddress, Const(~_translator.FunctionTable.Mask));
|
||||
Operand masked = context.BitwiseAnd(guestAddress, Const(~_functionTable.Mask));
|
||||
context.BranchIfTrue(lblFallback, masked);
|
||||
|
||||
Operand index = default;
|
||||
Operand page = Const((long)_translator.FunctionTable.Base);
|
||||
Operand page = Const((long)_functionTable.Base);
|
||||
|
||||
for (int i = 0; i < _translator.FunctionTable.Levels.Length; i++)
|
||||
for (int i = 0; i < _functionTable.Levels.Length; i++)
|
||||
{
|
||||
ref var level = ref _translator.FunctionTable.Levels[i];
|
||||
ref var level = ref _functionTable.Levels[i];
|
||||
|
||||
// level.Mask is not used directly because it is more often bigger than 32-bits, so it will not
|
||||
// be encoded as an immediate on x86's bitwise and operation.
|
||||
@ -167,7 +169,7 @@ namespace ARMeilleure.Translation
|
||||
|
||||
index = context.BitwiseAnd(context.ShiftRightUI(guestAddress, Const(level.Index)), mask);
|
||||
|
||||
if (i < _translator.FunctionTable.Levels.Length - 1)
|
||||
if (i < _functionTable.Levels.Length - 1)
|
||||
{
|
||||
page = context.Load(OperandType.I64, context.Add(page, context.ShiftLeft(index, Const(3))));
|
||||
context.BranchIfFalse(lblFallback, page);
|
||||
@ -196,7 +198,7 @@ namespace ARMeilleure.Translation
|
||||
/// Generates a <see cref="SlowDispatchStub"/>.
|
||||
/// </summary>
|
||||
/// <returns>Generated <see cref="SlowDispatchStub"/></returns>
|
||||
private static IntPtr GenerateSlowDispatchStub()
|
||||
private IntPtr GenerateSlowDispatchStub()
|
||||
{
|
||||
var context = new EmitterContext();
|
||||
|
||||
@ -205,8 +207,7 @@ namespace ARMeilleure.Translation
|
||||
Operand guestAddress = context.Load(OperandType.I64,
|
||||
context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset())));
|
||||
|
||||
MethodInfo getFuncAddress = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress));
|
||||
Operand hostAddress = context.Call(getFuncAddress, guestAddress);
|
||||
Operand hostAddress = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)), guestAddress);
|
||||
context.Tailcall(hostAddress, nativeContext);
|
||||
|
||||
var cfg = context.GetControlFlowGraph();
|
||||
|
@ -31,7 +31,7 @@ namespace Ryujinx.Audio.Backends.CompatLayer
|
||||
private const int Minus6dBInQ15 = (int)(0.501f * RawQ15One);
|
||||
private const int Minus12dBInQ15 = (int)(0.251f * RawQ15One);
|
||||
|
||||
private static readonly int[] _defaultSurroundToStereoCoefficients = new int[4]
|
||||
private static readonly long[] _defaultSurroundToStereoCoefficients = new long[4]
|
||||
{
|
||||
RawQ15One,
|
||||
Minus3dBInQ15,
|
||||
@ -39,7 +39,7 @@ namespace Ryujinx.Audio.Backends.CompatLayer
|
||||
Minus3dBInQ15,
|
||||
};
|
||||
|
||||
private static readonly int[] _defaultStereoToMonoCoefficients = new int[2]
|
||||
private static readonly long[] _defaultStereoToMonoCoefficients = new long[2]
|
||||
{
|
||||
Minus6dBInQ15,
|
||||
Minus6dBInQ15,
|
||||
@ -62,19 +62,23 @@ namespace Ryujinx.Audio.Backends.CompatLayer
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static short DownMixStereoToMono(ReadOnlySpan<int> coefficients, short left, short right)
|
||||
private static short DownMixStereoToMono(ReadOnlySpan<long> coefficients, short left, short right)
|
||||
{
|
||||
return (short)((left * coefficients[0] + right * coefficients[1]) >> Q15Bits);
|
||||
return (short)Math.Clamp((left * coefficients[0] + right * coefficients[1]) >> Q15Bits, short.MinValue, short.MaxValue);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static short DownMixSurroundToStereo(ReadOnlySpan<int> coefficients, short back, short lfe, short center, short front)
|
||||
private static short DownMixSurroundToStereo(ReadOnlySpan<long> coefficients, short back, short lfe, short center, short front)
|
||||
{
|
||||
return (short)((coefficients[3] * back + coefficients[2] * lfe + coefficients[1] * center + coefficients[0] * front + RawQ15HalfOne) >> Q15Bits);
|
||||
return (short)Math.Clamp(
|
||||
(coefficients[3] * back +
|
||||
coefficients[2] * lfe +
|
||||
coefficients[1] * center +
|
||||
coefficients[0] * front + RawQ15HalfOne) >> Q15Bits, short.MinValue, short.MaxValue);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static short[] DownMixSurroundToStereo(ReadOnlySpan<int> coefficients, ReadOnlySpan<short> data)
|
||||
private static short[] DownMixSurroundToStereo(ReadOnlySpan<long> coefficients, ReadOnlySpan<short> data)
|
||||
{
|
||||
int samplePerChannelCount = data.Length / SurroundChannelCount;
|
||||
|
||||
@ -94,7 +98,7 @@ namespace Ryujinx.Audio.Backends.CompatLayer
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static short[] DownMixStereoToMono(ReadOnlySpan<int> coefficients, ReadOnlySpan<short> data)
|
||||
private static short[] DownMixStereoToMono(ReadOnlySpan<long> coefficients, ReadOnlySpan<short> data)
|
||||
{
|
||||
int samplePerChannelCount = data.Length / StereoChannelCount;
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
using ARMeilleure.Translation;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
@ -916,7 +915,6 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
Device.Gpu.SetGpuThread();
|
||||
Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token);
|
||||
Translator.IsReadyForTranslation.Set();
|
||||
|
||||
_renderer.Window.ChangeVSyncMode(Device.EnableDeviceVsync);
|
||||
|
||||
@ -1088,10 +1086,11 @@ namespace Ryujinx.Ava
|
||||
case KeyboardHotkeyState.ToggleMute:
|
||||
if (Device.IsAudioMuted())
|
||||
{
|
||||
Device.SetVolume(ConfigurationState.Instance.System.AudioVolume);
|
||||
Device.SetVolume(_viewModel.VolumeBeforeMute);
|
||||
}
|
||||
else
|
||||
{
|
||||
_viewModel.VolumeBeforeMute = Device.GetVolume();
|
||||
Device.SetVolume(0);
|
||||
}
|
||||
|
||||
|
155
src/Ryujinx.Ava/Assets/Icons/Controller_JoyConLeft.svg
Normal file
155
src/Ryujinx.Ava/Assets/Icons/Controller_JoyConLeft.svg
Normal file
@ -0,0 +1,155 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 27.9.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 1000 355.6" style="enable-background:new 0 0 1000 355.6;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#00BBDB;stroke:#000000;}
|
||||
.st1{fill:#333333;stroke:#000000;stroke-width:0.93;}
|
||||
.st2{fill:#333333;stroke:#000000;stroke-width:0.96;}
|
||||
.st3{fill:#333333;stroke:#000000;stroke-width:0.85;}
|
||||
.st4{fill:#1A1A1A;stroke:#000000;}
|
||||
.st5{fill:#333333;stroke:#000000;}
|
||||
.st6{fill:#1A1A1A;stroke:#1A1A1A;}
|
||||
.st7{fill:#1A1A1A;stroke:#4D4D4D;}
|
||||
.st8{stroke:#4D4D4D;}
|
||||
.st9{stroke:#000000;}
|
||||
</style>
|
||||
<g id="g344">
|
||||
<path id="path66-7" d="M906.6,6.5v7.9c0,3.6-2.9,6.4-6.5,6.5H71.2c-3.6,0-6.4-2.9-6.5-6.5V6.5c0-3.6,2.9-6.4,6.5-6.5L207,0
|
||||
c4.9,0,9.6,1.2,14,3.4l13,6.7h79.2l13-6.7c4.3-2.2,9.1-3.4,13.9-3.4l269.7,0c4.9,0,9.6,1.2,14,3.4l13,6.7H716l13-6.7
|
||||
c4.3-2.2,9.1-3.4,13.9-3.4l157.2,0C903.7,0,906.6,2.9,906.6,6.5L906.6,6.5L906.6,6.5z M65.7,14.4c0,3,2.4,5.5,5.5,5.5h828.9
|
||||
c3,0,5.5-2.4,5.5-5.5V6.5c0-3-2.4-5.5-5.5-5.5H742.9c-4.7,0-9.3,1.1-13.5,3.3l-13.1,6.8c-0.1,0-0.2,0.1-0.2,0.1h-79.5
|
||||
c-0.1,0-0.2,0-0.2-0.1l-13.1-6.8c-4.2-2.2-8.8-3.3-13.5-3.3H340.1c-4.7,0-9.3,1.1-13.5,3.3l-13.1,6.8c-0.1,0-0.2,0.1-0.2,0.1h-79.5
|
||||
c-0.1,0-0.2,0-0.2-0.1l-13.1-6.8C216.3,2.1,211.7,1,207,1L71.2,1c-3,0-5.5,2.4-5.5,5.5L65.7,14.4L65.7,14.4z"/>
|
||||
<path id="path68-5" d="M858.9,20.3v11.2c0,0.3-0.2,0.5-0.5,0.5H72c-0.3,0-0.5-0.2-0.5-0.5V20.3c0-0.3,0.2-0.5,0.5-0.5h786.4
|
||||
C858.7,19.8,858.9,20,858.9,20.3z M857.9,31V20.8H72.5V31H857.9z"/>
|
||||
<path id="path70-3" d="M1000,37.5v106.2c0,117-94.8,211.9-211.9,211.9l0,0H220.9c-116.8,0-211.8-95.1-211.8-211.9V37.5
|
||||
c0-3.6,2.9-6.5,6.5-6.5h978C997.1,31,1000,33.9,1000,37.5L1000,37.5L1000,37.5z M10.1,143.7c0,116.3,94.6,210.9,210.8,210.9h567.2
|
||||
C904.4,354.6,999,260,999,143.7V37.5c0-3-2.4-5.5-5.5-5.5h-978c-3,0-5.5,2.4-5.5,5.5v106.2L10.1,143.7L10.1,143.7z"/>
|
||||
<path id="path98" d="M717.1,6.5v4.2c0,0.6-0.4,1-1,1l0,0h-79.5c-0.6,0-1-0.4-1-1V6.5c0-3.8,3.1-6.9,7-7h67.6
|
||||
C714-0.5,717.1,2.6,717.1,6.5z M715.1,9.7V6.5c0-2.7-2.2-5-5-5h-67.6c-2.7,0-5,2.2-5,5v3.2H715.1z"/>
|
||||
<path id="path100" d="M314.3,6.5v4.2c0,0.6-0.4,1-1,1h-79.5c-0.6,0-1-0.4-1-1V6.5c0-3.8,3.1-6.9,7-7h67.6
|
||||
C311.2-0.5,314.3,2.6,314.3,6.5z M312.3,9.7V6.5c0-2.7-2.2-5-5-5h-67.6c-2.7,0-5,2.2-5,5v3.2H312.3z"/>
|
||||
<path id="path1144" class="st0" d="M997.9,162.4c-3,26.2-8.5,49.9-21,75.1c-10.9,21.9-25.7,41.1-42.5,57.5
|
||||
c-33.2,32.3-77.8,53.7-126.7,59c-3.4,0.4-30.5,0.1-72.2,0.3c-158.5,1.1-520.6,0.9-534.4-0.5c-17.7-1.8-36.5-6.1-52.4-12
|
||||
c-7.5-2.8-15.8-7.1-23.1-10.7C61.8,299.6,21.2,238.8,12,168.6C9.6,150.4,8.7,35.5,11,33.1c0.1-0.1,2.3-0.9,7-1
|
||||
c11.1-0.4,36-0.4,79.4-1.3c32.7-0.7,76.3,0,132.5-0.1c70.3-0.2,160.2,1.6,274.8,1.6c484.5,0,491.2-1.4,492.4,0.9
|
||||
c0.2,0.3,1.7,2.5,1.8,5.3c1.1,21.9-0.5,119.2-0.7,120.6L997.9,162.4L997.9,162.4z"/>
|
||||
<polygon id="polygon80" class="st1" points="470.2,195 470.2,168.3 448.1,181.7 "/>
|
||||
<polygon id="polygon82" class="st2" points="627.3,181.3 605.6,194.1 605.6,168.5 "/>
|
||||
<polygon id="polygon84" class="st1" points="538.1,271.6 551.5,249.7 524.7,249.7 "/>
|
||||
<polygon id="polygon86" class="st3" points="551.6,114.8 524.1,114.8 537.9,89.2 "/>
|
||||
<path id="path102" d="M139.3,338.3c0,0.3-0.1,0.5-0.3,0.7l-3.4,3.4c-2,2-5.1,2.6-7.8,1.5C50.1,309.1,0.1,231.9,0,146.7l0-51.2
|
||||
c0-3.8,3.1-6.9,7-7h2.6c0.6,0,1,0.4,1,1v54.2C10.5,228.2,61,304.5,138.7,337.4c0.3,0.1,0.5,0.4,0.6,0.7L139.3,338.3L139.3,338.3z
|
||||
M2,146.7c0.1,84.4,49.7,160.9,126.7,195.4c1.9,0.8,4.1,0.4,5.5-1.1l2.4-2.4C58.8,305,8.5,228.3,8.6,143.6V90.4H7c-2.7,0-5,2.2-5,5
|
||||
L2,146.7L2,146.7z"/>
|
||||
<path id="path104" d="M116.1,60v46.9c0,2.2-1.8,4-4,4h-11.7c-2.2,0-4-1.8-4-4V60c0-2.2,1.8-4,4-4h11.7
|
||||
C114.3,56,116.1,57.8,116.1,60z M98.5,106.9c0,1.1,0.9,2,2,2h11.7c1.1,0,2-0.9,2-2V60c0-1.1-0.9-2-2-2h-11.7c-1.1,0-2,0.9-2,2
|
||||
V106.9z"/>
|
||||
<path id="path106" d="M502.9,181.7c0,21.2-17.2,38.5-38.5,38.5s-38.5-17.2-38.5-38.5s17.2-38.5,38.5-38.5S502.9,160.4,502.9,181.7
|
||||
L502.9,181.7z M428,181.7c0,20.1,16.3,36.5,36.5,36.5s36.5-16.3,36.5-36.5s-16.3-36.5-36.5-36.5S428,161.5,428,181.7L428,181.7z"/>
|
||||
<path id="path108" d="M649.8,181.7c0,21.2-17.2,38.5-38.5,38.5s-38.5-17.2-38.5-38.5s17.2-38.5,38.5-38.5S649.8,160.4,649.8,181.7
|
||||
L649.8,181.7z M574.9,181.7c0,20.1,16.3,36.5,36.5,36.5s36.5-16.3,36.5-36.5s-16.3-36.5-36.5-36.5l0,0
|
||||
C591.2,145.2,574.9,161.5,574.9,181.7L574.9,181.7z"/>
|
||||
<path id="path110" d="M576.3,108.2c0,21.2-17.2,38.5-38.5,38.5s-38.5-17.2-38.5-38.5s17.2-38.5,38.5-38.5l0,0
|
||||
C559.1,69.8,576.3,87,576.3,108.2z M501.4,108.2c0,20.1,16.3,36.5,36.5,36.5s36.5-16.3,36.5-36.5s-16.3-36.5-36.5-36.5l0,0
|
||||
C517.7,71.8,501.4,88.1,501.4,108.2L501.4,108.2L501.4,108.2z"/>
|
||||
<path id="path112" d="M576.3,255.1c0,21.2-17.2,38.5-38.5,38.5s-38.5-17.2-38.5-38.5s17.2-38.5,38.5-38.5l0,0
|
||||
C559.1,216.7,576.3,233.9,576.3,255.1z M501.4,255.1c0,20.1,16.3,36.5,36.5,36.5s36.5-16.3,36.5-36.5s-16.3-36.5-36.5-36.5l0,0
|
||||
C517.7,218.7,501.4,235,501.4,255.1L501.4,255.1L501.4,255.1z"/>
|
||||
<path id="path114" d="M753.7,105.5v45c0,5.5-4.4,9.9-9.9,9.9h-45c-5.5,0-9.9-4.4-9.9-9.9v-45c0-5.5,4.4-9.9,9.9-9.9h45
|
||||
C749.3,95.6,753.7,100.1,753.7,105.5z M690.9,150.5c0,4.4,3.6,7.9,7.9,7.9h45c4.4,0,7.9-3.6,7.9-7.9v-45c0-4.4-3.6-7.9-7.9-7.9h-45
|
||||
c-4.4,0-7.9,3.6-7.9,7.9V150.5z"/>
|
||||
<path id="path116" d="M741.7,128c0,11.3-9.2,20.4-20.4,20.4s-20.4-9.2-20.4-20.4s9.2-20.4,20.4-20.4l0,0
|
||||
C732.6,107.6,741.7,116.7,741.7,128z M702.8,128c0,10.2,8.3,18.4,18.4,18.4s18.4-8.3,18.4-18.4s-8.3-18.4-18.4-18.4
|
||||
S702.9,117.8,702.8,128z"/>
|
||||
<path id="path118" d="M260.2,244.8v12.3c0,0.6-0.4,1-1,1l0,0c-39.6-1.7-71.3-33.4-73-73c0-0.3,0.1-0.5,0.3-0.7s0.4-0.3,0.7-0.3
|
||||
h12.3c3.5,0,6.4,2.6,6.9,6c3.7,24.7,23.1,44.2,47.9,47.9C257.6,238.4,260.2,241.3,260.2,244.8L260.2,244.8z M258.2,256v-11.2
|
||||
c0-2.5-1.8-4.6-4.3-4.9c-25.6-3.9-45.7-24-49.6-49.6c-0.3-2.5-2.4-4.3-4.9-4.3h-11.2C190.3,223.4,220.8,253.9,258.2,256L258.2,256z
|
||||
"/>
|
||||
<path id="path120" d="M338.9,185L338.9,185c-1.7,39.6-33.4,71.3-73,73c-0.6,0-1-0.4-1-1v-12.3c0-3.5,2.6-6.4,6-6.9
|
||||
c24.7-3.7,44.2-23.1,47.9-47.9c0.5-3.4,3.4-6,6.9-6H338C338.5,184,338.9,184.5,338.9,185L338.9,185z M266.9,256
|
||||
c37.4-2.2,67.8-32.6,70-70h-11.2c-2.5,0-4.6,1.8-4.9,4.3c-3.9,25.6-24,45.7-49.6,49.6c-2.5,0.3-4.3,2.4-4.3,4.9V256L266.9,256z"/>
|
||||
<path id="path122" d="M338.9,178.3c0,0.6-0.4,1-1,1h-12.3c-3.5,0-6.4-2.6-6.9-6c-3.7-24.7-23.1-44.2-47.9-47.9
|
||||
c-3.4-0.5-6-3.4-6-6.9v-12.3c0-0.6,0.4-1,1-1l0,0C305.5,107,337.2,138.7,338.9,178.3L338.9,178.3L338.9,178.3z M266.9,118.6
|
||||
c0,2.5,1.8,4.6,4.3,4.9c25.6,3.9,45.7,24,49.6,49.6c0.3,2.5,2.4,4.3,4.9,4.3h11.2c-2.2-37.4-32.6-67.8-70-70V118.6z"/>
|
||||
<path id="path124" d="M260.2,106.3v12.3c0,3.5-2.6,6.4-6,6.9c-24.7,3.7-44.2,23.1-47.9,47.9c-0.5,3.4-3.4,6-6.9,6h-12.3
|
||||
c-0.3,0-0.5-0.1-0.7-0.3s-0.3-0.5-0.3-0.7c1.7-39.6,33.4-71.3,73-73C259.7,105.3,260.2,105.7,260.2,106.3L260.2,106.3L260.2,106.3z
|
||||
M188.2,177.3h11.2c2.5,0,4.6-1.8,4.9-4.3c3.9-25.6,24-45.7,49.6-49.6c2.5-0.3,4.3-2.4,4.3-4.9v-11.2
|
||||
C220.8,109.5,190.3,140,188.2,177.3L188.2,177.3L188.2,177.3z"/>
|
||||
<path id="path126" d="M339,181.7c0,1.2,0,2.3-0.1,3.4c0,0.5-0.5,0.9-1,0.9h-12.3c-2.5,0-4.6,1.8-4.9,4.3
|
||||
c-3.9,25.6-24,45.7-49.6,49.6c-2.5,0.3-4.3,2.4-4.3,4.9v12.3c0,0.5-0.4,1-0.9,1c-1.1,0.1-2.2,0.1-3.4,0.1s-2.3,0-3.4-0.1
|
||||
c-0.5,0-0.9-0.5-0.9-1v-12.3c0-2.5-1.8-4.6-4.3-4.9c-25.6-3.9-45.7-24-49.6-49.6c-0.3-2.5-2.4-4.3-4.9-4.3h-12.3
|
||||
c-0.5,0-1-0.4-1-0.9c-0.1-2.3-0.1-4.5,0-6.8c0-0.5,0.5-0.9,1-0.9h12.3c2.5,0,4.6-1.8,4.9-4.3c3.9-25.6,24-45.7,49.6-49.6
|
||||
c2.5-0.3,4.3-2.4,4.3-4.9v-12.3c0-0.5,0.4-1,0.9-1c1.1-0.1,2.2-0.1,3.4-0.1s2.3,0,3.4,0.1c0.5,0,0.9,0.5,0.9,1v12.3
|
||||
c0,2.5,1.8,4.6,4.3,4.9c25.6,3.9,45.7,24,49.6,49.6c0.3,2.5,2.4,4.3,4.9,4.3h12.3c0.5,0,1,0.4,1,0.9C339,179.4,339,180.5,339,181.7
|
||||
L339,181.7z M337,184c0.1-1.5,0.1-3.1,0-4.7h-11.3c-3.5,0-6.4-2.6-6.9-6c-3.7-24.7-23.1-44.2-47.9-47.9c-3.4-0.5-6-3.4-6-6.9v-11.3
|
||||
h-4.6v11.4c0,3.5-2.6,6.4-6,6.9c-24.7,3.7-44.2,23.1-47.9,47.9c-0.5,3.4-3.4,6-6.9,6h-11.3c-0.1,1.5-0.1,3.1,0,4.7h11.3
|
||||
c3.5,0,6.4,2.6,6.9,6c3.7,24.7,23.1,44.2,47.9,47.9c3.4,0.5,6,3.4,6,6.9v11.3h4.6v-11.3c0-3.5,2.6-6.4,6-6.9
|
||||
c24.7-3.7,44.2-23.1,47.9-47.9c0.5-3.4,3.4-6,6.9-6L337,184L337,184z"/>
|
||||
<path id="path179" class="st4" d="M574.3,261.6c-3.2,14.7-12.7,24.9-27.1,29c-3.4,1-6.3,1.2-11.7,0.9c-6.5-0.3-7.8-0.7-13.5-3.5
|
||||
c-22.9-11.4-27.9-40.7-10-58.6c19.4-19.4,51.9-12,60.8,13.9C574.5,248.5,575.3,257.1,574.3,261.6L574.3,261.6L574.3,261.6z
|
||||
M538,249c-7.3,0-13.4,0.2-13.4,0.4c0,0.9,13.2,23.3,13.5,23c0.7-0.7,13.1-22.2,13.2-22.8C551.3,249.2,545.4,249,538,249L538,249
|
||||
L538,249z"/>
|
||||
<path id="path181" class="st4" d="M500.6,187.1c-0.9,6.9-5,15-10.4,20.5c-14.5,14.6-36.9,14.6-51.4-0.1c-22.9-23-7-62.3,25.4-62.3
|
||||
c10.4,0,18.8,3.5,26,10.7C498.4,164.3,502.1,175.2,500.6,187.1L500.6,187.1L500.6,187.1z M469.9,168.5
|
||||
c-2.4,0.9-22.4,12.8-22.4,13.3c0,0.4,9,5.9,19.9,12l3.6,2v-13.9C471,169.7,470.9,168.1,469.9,168.5L469.9,168.5L469.9,168.5z"/>
|
||||
<path id="path185" class="st4" d="M647,190.4c-3.5,12.9-13.7,23.1-26.6,26.5c-22.7,5.9-45.2-11.6-45.3-35.2
|
||||
c0-7.4,0.9-11.3,4.5-18.1c8.8-16.9,30.4-23.9,47.9-15.4C643,155.8,651.4,173.9,647,190.4L647,190.4L647,190.4z M616.9,174.2
|
||||
c-6.3-3.6-11.6-6.5-11.8-6.3s-0.3,6.4-0.1,13.8l0.2,13.5l11.6-6.7c6.4-3.7,11.6-6.9,11.6-7.2C628.4,181.1,623.2,177.8,616.9,174.2
|
||||
L616.9,174.2z"/>
|
||||
<path id="path187" class="st4" d="M574.2,111.7c0,0.3-0.3,1.6-0.5,2.9c-2.2,11.2-9.7,20.9-20.1,26.2c-5.6,2.8-12.2,4.2-17.9,3.8
|
||||
c-14.8-1.1-27.7-11.3-32.5-25.6c-2.9-8.8-2.2-18,2.2-26.7s12.1-15.5,21.3-18.6c15.3-5.3,32.2,0.7,41.6,14.7c4,6,6.3,13.4,6.2,20.2
|
||||
C574.3,109.9,574.3,111.4,574.2,111.7L574.2,111.7L574.2,111.7z M551.1,112.4c-0.4-0.7-1.6-3-2.8-5.1c-4.8-8.7-9.2-15.6-10-16.2
|
||||
c-0.4-0.3-0.5-0.3-0.9,0c-1.3,0.9-13.5,21.5-13.5,22.9c0,0.3,0.1,0.6,0.2,0.7c0.5,0.4,4.8,0.6,13.9,0.6c9.1,0,13-0.2,13.5-0.6
|
||||
C551.9,114.3,551.8,113.8,551.1,112.4L551.1,112.4L551.1,112.4z"/>
|
||||
<path id="path189" class="st5" d="M739.1,131.6c-0.2,1-1,3-1.6,4.4c-1,2.1-1.6,2.9-3.5,4.8c-1.9,1.9-2.7,2.5-4.8,3.5
|
||||
c-5.5,2.7-10.6,2.7-16.1,0c-2.1-1-2.8-1.6-4.8-3.5c-3.6-3.6-5.3-7.7-5.3-12.7c0-8.4,5.7-15.7,13.9-17.8c2-0.5,6.2-0.5,8.3,0
|
||||
c6.3,1.5,11.5,6.3,13.4,12.7C739.4,125.4,739.6,129.2,739.1,131.6L739.1,131.6L739.1,131.6z"/>
|
||||
<path id="path191" class="st4" d="M751.3,152.4c-0.4,1.6-1.3,3-2.6,4.1c-2.1,1.8-0.9,1.7-27.4,1.7s-25.3,0.1-27.5-1.9
|
||||
c-0.7-0.6-1.5-1.7-1.9-2.5l-0.7-1.5v-48.3l0.9-1.8c0.6-1.3,1.2-2,2.1-2.7c2.3-1.8,1.3-1.7,27.8-1.6c23.8,0.1,23.9,0.1,25.1,0.6
|
||||
c1.6,0.7,3.1,2.3,3.9,3.9l0.7,1.3l0,23.8C751.6,145.6,751.5,151.4,751.3,152.4L751.3,152.4L751.3,152.4z M741.6,125.1
|
||||
c-1.5-10.6-10.7-18.1-21.4-17.5c-3.4,0.2-5.3,0.7-8.1,2c-9.3,4.6-13.7,15.5-10.2,25.3c1,2.8,2.2,4.7,4.3,7c2.8,3,6.3,5.1,10.3,6
|
||||
c2.2,0.5,7.2,0.5,9.4,0c3.9-0.9,7.4-3,10.2-6c2.1-2.2,3.3-4.2,4.3-6.7C741.7,131.9,742.1,128.5,741.6,125.1L741.6,125.1
|
||||
L741.6,125.1z"/>
|
||||
<path id="path1082" class="st6" d="M330.4,183.8c-6,0-6.5,0.1-7.9,0.7c-2.1,1.1-3.4,2.9-3.9,5.6c-2.7,15.1-10.1,27.5-21.9,36.5
|
||||
c-6.5,5-15.1,8.8-23.2,10.4c-4.3,0.8-5.7,1.4-7,3c-1.5,1.8-1.8,3.3-1.8,10v5.9h-4.2v-6.3c0-6.1,0-6.3-0.9-8
|
||||
c-1.2-2.4-2.7-3.3-7.3-4.2c-23.2-4.6-41-22.4-45.4-45.6c-0.8-4.2-1.6-5.6-3.8-6.9c-1.5-0.9-1.6-0.9-8.1-1l-6.6-0.1v-4.2h6.3
|
||||
c6.2,0,6.3,0,8-0.9c2.6-1.3,3.4-2.6,4.4-7.6c3.8-19.4,17-35.1,35.4-42.3c2.7-1,5.9-1.9,12-3.3c2.6-0.6,4.2-1.7,5.2-3.6
|
||||
c0.6-1.1,0.7-2,0.8-7.9l0.1-6.6h4.2v6.3c0,6.1,0,6.3,0.9,8c1.1,2.2,2.9,3.4,5.9,3.9c23.8,4.1,42.3,22.2,46.8,46
|
||||
c0.8,4.2,1.7,5.7,4.1,7c1.5,0.8,1.9,0.8,8,0.9l6.4,0.1v4.2L330.4,183.8L330.4,183.8z"/>
|
||||
<path id="path1084" class="st7" d="M257.3,255.8c-0.5-0.1-1.9-0.3-3.2-0.4c-3.9-0.4-10.1-1.7-14.8-3.3c-24.1-8-42.7-28.1-48.9-52.7
|
||||
c-0.8-3.1-1.6-7.6-1.9-11.3l-0.2-2h5.9c8.5,0,9.1,0.4,10.4,6.5c3.7,18.4,15.2,33.7,31.9,41.9c5.1,2.6,10.1,4.1,17,5.5
|
||||
c2.4,0.5,4,1.8,4.4,3.7c0.2,0.7,0.3,3.8,0.3,6.8v5.5L257.3,255.8L257.3,255.8z"/>
|
||||
<path id="path1086" class="st7" d="M336.4,189.8c-3,27-21.4,50.8-46.9,60.9c-6,2.4-13.4,4.2-18.7,4.7c-1.3,0.1-2.7,0.3-3.1,0.4
|
||||
l-0.8,0.2l0.1-6.3c0.1-5.8,0.2-6.4,0.8-7.2c1.2-1.6,2.2-2,6.2-2.9c5.8-1.2,9.4-2.4,14.8-5.2c5.7-2.9,9.8-5.7,14.2-9.9
|
||||
c9.1-8.6,15.2-19.7,17.5-31.7c0.7-3.6,1.2-4.6,2.7-5.8c0.8-0.6,1.4-0.7,7.2-0.8c5.9-0.1,6.3-0.1,6.3,0.5
|
||||
C336.7,187,336.6,188.4,336.4,189.8L336.4,189.8L336.4,189.8z"/>
|
||||
<path id="path1088" class="st7" d="M257.7,119.9c-0.9,2.4-1.6,2.8-6.5,3.8c-18.2,3.7-33.5,15.4-41.6,32c-2.4,4.8-3.7,8.6-4.7,13.5
|
||||
c-1,4.7-1.6,6.2-2.9,7.1c-1.1,0.7-1.4,0.7-7.4,0.7c-3.5,0-6.3-0.1-6.3-0.3s0.2-1.9,0.5-4c1.5-12,5.6-22.9,12.4-32.8
|
||||
c3-4.4,5.6-7.4,9.9-11.6c9.6-9.3,20.7-15.5,33.5-18.8c3.6-0.9,10.9-2.1,12.9-2.1c0.6,0,0.6,0.3,0.6,5.6
|
||||
C258.1,116.9,258,119.1,257.7,119.9L257.7,119.9L257.7,119.9z"/>
|
||||
<path id="path1090" class="st7" d="M330.5,177.3c-5.8-0.1-6.4-0.2-7.2-0.8c-1.6-1.2-2-2.2-2.9-6.3c-4.7-23.5-23.3-41.9-47-46.4
|
||||
c-3.5-0.7-4.5-1.1-5.6-2.7c-0.6-0.8-0.7-1.4-0.8-7.2l-0.1-6.3l1.7,0.2c10.5,1.2,18,3.3,26.4,7.5c22.6,11.2,38.1,32.9,41.3,58
|
||||
c0.3,2.1,0.5,3.9,0.5,4S333.9,177.3,330.5,177.3L330.5,177.3L330.5,177.3z"/>
|
||||
<path id="path1092" class="st4" d="M113.5,108c-0.6,0.5-1.8,0.7-7.2,0.7c-4.5,0-6.7-0.2-7-0.5c-0.4-0.4-0.5-6.4-0.5-24.6
|
||||
c0-21.3,0.1-24.2,0.7-24.8c0.6-0.5,1.8-0.7,7-0.7c6.1,0,6.4,0,7,1c0.6,0.8,0.7,3.9,0.7,24.6S114,107.4,113.5,108z"/>
|
||||
<path id="path1094" class="st8" d="M134.2,340.6c-2.8,2.4-3.7,2.2-12.6-2.3c-21.7-10.8-39.6-23.6-56.5-40.5
|
||||
c-31.4-31.4-52.2-71.6-59.8-115.3c-2.6-15.1-2.7-16.5-2.9-54L2.3,93.7l1.2-1.4c0.9-1,1.7-1.4,3-1.6l1.8-0.2l0.2,33.8
|
||||
c0.2,36.2,0.3,38.8,2.7,53.3c6.2,38.4,22,72.9,47.3,103.3c5.7,6.8,18.3,19.5,25.2,25.2c10,8.4,21.7,16.5,32.4,22.5
|
||||
c5.4,3.1,18.7,9.6,19.5,9.6C136.4,338.3,136,339.1,134.2,340.6L134.2,340.6L134.2,340.6z"/>
|
||||
<path id="path1150" class="st9" d="M616,21.1c-216,0-634,0-526-0.1c108-0.1,614.3-0.3,722.4-0.1c29.2,0,43.2,0.1,45,0.1
|
||||
C862.4,21.1,773.6,21.1,616,21.1L616,21.1L616,21.1z"/>
|
||||
<path id="path1152" class="st4" d="M903.2,19c-2.1,1.5-44.2,0.8-417.6,0.8S70.3,20.6,68.2,19.1c-2-1.4-2.2-3.4-2.2-8.5
|
||||
c0-1.8-0.1-3.8,0.2-5.4c0.3-1.4,2-3.2,2.4-3.5c0.5-0.5,4.3-0.3,16.4-0.4c11.3-0.1,29,0.2,55.3,0.2c44.7,0,61.8-0.6,69.8-0.1
|
||||
c4.4,0.3,6.1,1.2,8.1,1.9c3.1,1.2,8.2,3.9,10.9,5.6l5,3.2l40.4-0.4c37.6-0.4,39,0.2,41.7-1.6c1.5-1.1,6.1-3,9.3-4.6l5.8-3l7.9-1.3
|
||||
l19.8,0.1l15.8,0.1l97.9,0.2c93.1,0.2,126.7-1.2,141.1,0.3c4.2,0.4,6.5,1.6,8.5,2.3c2.7,0.9,4.9,2.1,7.8,3.9l6.1,3.7l15.6-0.4
|
||||
l25.1-0.1c26.5-0.1,40.4-0.6,40.9-1.3c0.4-0.6,4-2.7,8.5-4.3l9-3.3l9-1.1l15.9,0.2l57.9,0.2c61.9,0.2,84.5-0.4,85.5,0.5
|
||||
c0.9,0.8,2.4,4.5,2.4,8.4C905.8,15.4,905.1,17.6,903.2,19L903.2,19L903.2,19z"/>
|
||||
<path id="path1154" class="st4" d="M464.9,30.8l-5.2,0l-387-0.4v-8.5h392.2l393-1.1l0.2,5.3l-0.2,4.9L464.9,30.8L464.9,30.8z"/>
|
||||
<path id="path1156" class="st0" d="M273.3,9.7h-38.6v-3c0-1.7,0.9-3.6,2.1-4.3c2.8-1.7,70.3-1.7,73.1,0c1.2,0.7,2.1,2.6,2.1,4.3v3
|
||||
H273.3L273.3,9.7z"/>
|
||||
<path id="path1158" class="st0" d="M676.2,9.7h-38.7v-3c0-1.8,0.9-3.6,2.1-4.3c2.8-1.7,70.3-1.7,73.2,0c1.2,0.7,2.1,2.6,2.1,4.3v3
|
||||
H676.2L676.2,9.7z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 15 KiB |
341
src/Ryujinx.Ava/Assets/Icons/Controller_JoyConPair.svg
Normal file
341
src/Ryujinx.Ava/Assets/Icons/Controller_JoyConPair.svg
Normal file
@ -0,0 +1,341 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 27.9.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 1000 1000.2" style="enable-background:new 0 0 1000 1000.2;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#00BBDB;stroke:#000000;}
|
||||
.st1{fill:#333333;stroke:#000000;stroke-width:0.93;}
|
||||
.st2{fill:#333333;stroke:#000000;stroke-width:0.96;}
|
||||
.st3{fill:#333333;stroke:#000000;stroke-width:0.85;}
|
||||
.st4{fill:#1A1A1A;stroke:#000000;}
|
||||
.st5{fill:#333333;stroke:#000000;}
|
||||
.st6{fill:#1A1A1A;stroke:#1A1A1A;}
|
||||
.st7{fill:#1A1A1A;stroke:#4D4D4D;}
|
||||
.st8{stroke:#4D4D4D;}
|
||||
.st9{stroke:#000000;}
|
||||
.st10{opacity:0.1;}
|
||||
.st11{fill:#FF5F55;}
|
||||
.st12{fill:#FF5F53;}
|
||||
.st13{fill:#FFFFFF;}
|
||||
.st14{fill:#999595;stroke:#000000;stroke-width:2.39;stroke-linecap:round;stroke-linejoin:round;}
|
||||
.st15{fill:#3A3D40;stroke:#000000;stroke-width:2.73;stroke-linecap:round;stroke-linejoin:round;}
|
||||
</style>
|
||||
<g id="layer1">
|
||||
<g id="g344">
|
||||
<path id="path66-7" d="M349.1,906.6h-7.9c-3.6,0-6.4-2.9-6.5-6.5V71.2c0-3.6,2.9-6.4,6.5-6.5h7.9c3.6,0,6.4,2.9,6.5,6.5V207
|
||||
c0,4.9-1.2,9.6-3.4,14l-6.7,13v79.2l6.7,13c2.2,4.3,3.4,9.1,3.4,13.9v269.7c0,4.9-1.2,9.6-3.4,14l-6.7,13V716l6.7,13
|
||||
c2.2,4.3,3.4,9.1,3.4,13.9v157.2C355.6,903.7,352.7,906.6,349.1,906.6L349.1,906.6L349.1,906.6z M341.2,65.7c-3,0-5.5,2.4-5.5,5.5
|
||||
v828.9c0,3,2.4,5.5,5.5,5.5h7.9c3,0,5.5-2.4,5.5-5.5V742.9c0-4.7-1.1-9.3-3.3-13.5l-6.8-13.1c0-0.1-0.1-0.2-0.1-0.2v-79.5
|
||||
c0-0.1,0-0.2,0.1-0.2l6.8-13.1c2.2-4.2,3.3-8.8,3.3-13.5V340.1c0-4.7-1.1-9.3-3.3-13.5l-6.8-13.1c0-0.1-0.1-0.2-0.1-0.2v-79.5
|
||||
c0-0.1,0-0.2,0.1-0.2l6.8-13.1c2.2-4.2,3.3-8.8,3.3-13.5V71.2c0-3-2.4-5.5-5.5-5.5L341.2,65.7L341.2,65.7z"/>
|
||||
<path id="path68-5" d="M335.3,858.9h-11.2c-0.3,0-0.5-0.2-0.5-0.5V72c0-0.3,0.2-0.5,0.5-0.5h11.2c0.3,0,0.5,0.2,0.5,0.5v786.4
|
||||
C335.8,858.7,335.6,858.9,335.3,858.9z M324.6,857.9h10.2V72.5h-10.2V857.9z"/>
|
||||
<path id="path70-3" d="M318.1,1000H211.9C94.9,1000,0,905.2,0,788.1l0,0V220.9C0,104.1,95.1,9.1,211.9,9.1h106.2
|
||||
c3.6,0,6.5,2.9,6.5,6.5v978C324.6,997.1,321.7,1000,318.1,1000L318.1,1000L318.1,1000z M211.9,10.1C95.6,10.1,1,104.7,1,220.9
|
||||
v567.2C1,904.4,95.6,999,211.9,999h106.2c3,0,5.5-2.4,5.5-5.5v-978c0-3-2.4-5.5-5.5-5.5H211.9L211.9,10.1L211.9,10.1z"/>
|
||||
<path id="path98" d="M349.1,717.1h-4.2c-0.6,0-1-0.4-1-1l0,0v-79.5c0-0.6,0.4-1,1-1h4.2c3.8,0,6.9,3.1,7,7v67.6
|
||||
C356.1,714,353,717.1,349.1,717.1z M345.9,715.1h3.2c2.7,0,5-2.2,5-5v-67.6c0-2.7-2.2-5-5-5h-3.2V715.1z"/>
|
||||
<path id="path100" d="M349.1,314.3h-4.2c-0.6,0-1-0.4-1-1v-79.5c0-0.6,0.4-1,1-1h4.2c3.8,0,6.9,3.1,7,7v67.6
|
||||
C356.1,311.2,353,314.3,349.1,314.3z M345.9,312.3h3.2c2.7,0,5-2.2,5-5v-67.6c0-2.7-2.2-5-5-5h-3.2V312.3z"/>
|
||||
<path id="path1144" class="st0" d="M193.2,997.9c-26.2-3-49.9-8.5-75.1-21C96.2,966,77,951.2,60.7,934.4
|
||||
C28.4,901.1,7,856.6,1.7,807.7c-0.4-3.4-0.1-30.5-0.3-72.2C0.3,576.9,0.4,214.9,1.8,201c1.8-17.7,6.1-36.5,12-52.4
|
||||
c2.8-7.5,7.1-15.8,10.7-23.1C55.9,61.8,116.8,21.2,187,12c18.2-2.4,133.1-3.3,135.5-0.9c0.1,0.1,0.9,2.3,1,7
|
||||
c0.4,11.1,0.4,36,1.3,79.4c0.7,32.7,0,76.3,0.1,132.5c0.2,70.3-1.6,160.2-1.6,274.8c0,484.5,1.4,491.2-0.9,492.4
|
||||
c-0.3,0.2-2.5,1.7-5.3,1.8c-21.9,1.1-119.2-0.5-120.6-0.7L193.2,997.9L193.2,997.9z"/>
|
||||
<polygon id="polygon80" class="st1" points="173.9,448.1 160.6,470.2 187.3,470.2 "/>
|
||||
<polygon id="polygon82" class="st2" points="187.1,605.6 174.3,627.3 161.5,605.6 "/>
|
||||
<polygon id="polygon84" class="st1" points="105.9,524.7 84,538.1 105.9,551.5 "/>
|
||||
<polygon id="polygon86" class="st3" points="266.4,537.9 240.8,551.6 240.8,524.1 "/>
|
||||
<path id="path102" d="M17.3,139.3c-0.3,0-0.5-0.1-0.7-0.3l-3.4-3.4c-2-2-2.6-5.1-1.5-7.8C46.5,50.1,123.7,0.1,208.9,0h51.2
|
||||
c3.8,0,6.9,3.1,7,7v2.6c0,0.6-0.4,1-1,1h-54.2C127.4,10.5,51.1,61,18.2,138.7c-0.1,0.3-0.4,0.5-0.7,0.6L17.3,139.3L17.3,139.3z
|
||||
M208.9,2C124.5,2.1,48,51.7,13.5,128.7c-0.8,1.9-0.4,4.1,1.1,5.5l2.4,2.4C50.6,58.8,127.3,8.5,212,8.6h53.2V7c0-2.7-2.2-5-5-5
|
||||
L208.9,2L208.9,2z"/>
|
||||
<path id="path104" d="M295.6,116.1h-46.9c-2.2,0-4-1.8-4-4v-11.7c0-2.2,1.8-4,4-4h46.9c2.2,0,4,1.8,4,4v11.7
|
||||
C299.6,114.3,297.8,116.1,295.6,116.1z M248.7,98.5c-1.1,0-2,0.9-2,2v11.7c0,1.1,0.9,2,2,2h46.9c1.1,0,2-0.9,2-2v-11.7
|
||||
c0-1.1-0.9-2-2-2H248.7z"/>
|
||||
<path id="path106" d="M173.9,502.9c-21.2,0-38.5-17.2-38.5-38.5s17.2-38.5,38.5-38.5s38.5,17.2,38.5,38.5S195.2,502.9,173.9,502.9
|
||||
L173.9,502.9z M173.9,428c-20.1,0-36.5,16.3-36.5,36.5s16.3,36.5,36.5,36.5s36.5-16.3,36.5-36.5S194.1,428,173.9,428L173.9,428z"
|
||||
/>
|
||||
<path id="path108" d="M173.9,649.8c-21.2,0-38.5-17.2-38.5-38.5s17.2-38.5,38.5-38.5s38.5,17.2,38.5,38.5S195.2,649.8,173.9,649.8
|
||||
L173.9,649.8z M173.9,574.9c-20.1,0-36.5,16.3-36.5,36.5s16.3,36.5,36.5,36.5s36.5-16.3,36.5-36.5l0,0
|
||||
C210.4,591.2,194.1,574.9,173.9,574.9L173.9,574.9z"/>
|
||||
<path id="path110" d="M247.4,576.3c-21.2,0-38.5-17.2-38.5-38.5s17.2-38.5,38.5-38.5s38.5,17.2,38.5,38.5l0,0
|
||||
C285.8,559.1,268.6,576.3,247.4,576.3z M247.4,501.4c-20.1,0-36.5,16.3-36.5,36.5s16.3,36.5,36.5,36.5s36.5-16.3,36.5-36.5l0,0
|
||||
C283.8,517.7,267.5,501.4,247.4,501.4L247.4,501.4L247.4,501.4z"/>
|
||||
<path id="path112" d="M100.5,576.3c-21.2,0-38.5-17.2-38.5-38.5s17.2-38.5,38.5-38.5s38.5,17.2,38.5,38.5l0,0
|
||||
C138.9,559.1,121.7,576.3,100.5,576.3z M100.5,501.4c-20.1,0-36.5,16.3-36.5,36.5s16.3,36.5,36.5,36.5s36.5-16.3,36.5-36.5l0,0
|
||||
C136.9,517.7,120.6,501.4,100.5,501.4L100.5,501.4L100.5,501.4z"/>
|
||||
<path id="path114" d="M250.1,753.7h-45c-5.5,0-9.9-4.4-9.9-9.9v-45c0-5.5,4.4-9.9,9.9-9.9h45c5.5,0,9.9,4.4,9.9,9.9v45
|
||||
C260,749.3,255.5,753.7,250.1,753.7z M205.1,690.9c-4.4,0-7.9,3.6-7.9,7.9v45c0,4.4,3.6,7.9,7.9,7.9h45c4.4,0,7.9-3.6,7.9-7.9v-45
|
||||
c0-4.4-3.6-7.9-7.9-7.9H205.1z"/>
|
||||
<path id="path116" d="M227.6,741.7c-11.3,0-20.4-9.2-20.4-20.4s9.2-20.4,20.4-20.4s20.4,9.2,20.4,20.4l0,0
|
||||
C248,732.6,238.9,741.7,227.6,741.7z M227.6,702.8c-10.2,0-18.4,8.3-18.4,18.4s8.3,18.4,18.4,18.4s18.4-8.3,18.4-18.4
|
||||
S237.8,702.9,227.6,702.8z"/>
|
||||
<path id="path118" d="M110.8,260.2H98.5c-0.6,0-1-0.4-1-1l0,0c1.7-39.6,33.4-71.3,73-73c0.3,0,0.5,0.1,0.7,0.3s0.3,0.4,0.3,0.7
|
||||
v12.3c0,3.5-2.6,6.4-6,6.9c-24.7,3.7-44.2,23.1-47.9,47.9C117.2,257.6,114.3,260.2,110.8,260.2L110.8,260.2z M99.6,258.2h11.2
|
||||
c2.5,0,4.6-1.8,4.9-4.3c3.9-25.6,24-45.7,49.6-49.6c2.5-0.3,4.3-2.4,4.3-4.9v-11.2C132.2,190.3,101.7,220.8,99.6,258.2L99.6,258.2
|
||||
z"/>
|
||||
<path id="path120" d="M170.6,338.9L170.6,338.9c-39.6-1.7-71.3-33.4-73-73c0-0.6,0.4-1,1-1h12.3c3.5,0,6.4,2.6,6.9,6
|
||||
c3.7,24.7,23.1,44.2,47.9,47.9c3.4,0.5,6,3.4,6,6.9V338C171.6,338.5,171.1,338.9,170.6,338.9L170.6,338.9z M99.6,266.9
|
||||
c2.2,37.4,32.6,67.8,70,70v-11.2c0-2.5-1.8-4.6-4.3-4.9c-25.6-3.9-45.7-24-49.6-49.6c-0.3-2.5-2.4-4.3-4.9-4.3H99.6L99.6,266.9z"
|
||||
/>
|
||||
<path id="path122" d="M177.3,338.9c-0.6,0-1-0.4-1-1v-12.3c0-3.5,2.6-6.4,6-6.9c24.7-3.7,44.2-23.1,47.9-47.9c0.5-3.4,3.4-6,6.9-6
|
||||
h12.3c0.6,0,1,0.4,1,1l0,0C248.6,305.5,216.9,337.2,177.3,338.9L177.3,338.9L177.3,338.9z M237,266.9c-2.5,0-4.6,1.8-4.9,4.3
|
||||
c-3.9,25.6-24,45.7-49.6,49.6c-2.5,0.3-4.3,2.4-4.3,4.9v11.2c37.4-2.2,67.8-32.6,70-70H237z"/>
|
||||
<path id="path124" d="M249.3,260.2H237c-3.5,0-6.4-2.6-6.9-6c-3.7-24.7-23.1-44.2-47.9-47.9c-3.4-0.5-6-3.4-6-6.9v-12.3
|
||||
c0-0.3,0.1-0.5,0.3-0.7s0.5-0.3,0.7-0.3c39.6,1.7,71.3,33.4,73,73C250.3,259.7,249.9,260.2,249.3,260.2L249.3,260.2L249.3,260.2z
|
||||
M178.3,188.2v11.2c0,2.5,1.8,4.6,4.3,4.9c25.6,3.9,45.7,24,49.6,49.6c0.3,2.5,2.4,4.3,4.9,4.3h11.2
|
||||
C246.1,220.8,215.6,190.3,178.3,188.2L178.3,188.2L178.3,188.2z"/>
|
||||
<path id="path126" d="M173.9,339c-1.2,0-2.3,0-3.4-0.1c-0.5,0-0.9-0.5-0.9-1v-12.3c0-2.5-1.8-4.6-4.3-4.9
|
||||
c-25.6-3.9-45.7-24-49.6-49.6c-0.3-2.5-2.4-4.3-4.9-4.3H98.5c-0.5,0-1-0.4-1-0.9c-0.1-1.1-0.1-2.2-0.1-3.4s0-2.3,0.1-3.4
|
||||
c0-0.5,0.5-0.9,1-0.9h12.3c2.5,0,4.6-1.8,4.9-4.3c3.9-25.6,24-45.7,49.6-49.6c2.5-0.3,4.3-2.4,4.3-4.9v-12.3c0-0.5,0.4-1,0.9-1
|
||||
c2.3-0.1,4.5-0.1,6.8,0c0.5,0,0.9,0.5,0.9,1v12.3c0,2.5,1.8,4.6,4.3,4.9c25.6,3.9,45.7,24,49.6,49.6c0.3,2.5,2.4,4.3,4.9,4.3h12.3
|
||||
c0.5,0,1,0.4,1,0.9c0.1,1.1,0.1,2.2,0.1,3.4s0,2.3-0.1,3.4c0,0.5-0.5,0.9-1,0.9H237c-2.5,0-4.6,1.8-4.9,4.3
|
||||
c-3.9,25.6-24,45.7-49.6,49.6c-2.5,0.3-4.3,2.4-4.3,4.9v12.3c0,0.5-0.4,1-0.9,1C176.2,339,175.1,339,173.9,339L173.9,339z
|
||||
M171.6,337c1.5,0.1,3.1,0.1,4.7,0v-11.3c0-3.5,2.6-6.4,6-6.9c24.7-3.7,44.2-23.1,47.9-47.9c0.5-3.4,3.4-6,6.9-6h11.3v-4.6H237
|
||||
c-3.5,0-6.4-2.6-6.9-6c-3.7-24.7-23.1-44.2-47.9-47.9c-3.4-0.5-6-3.4-6-6.9v-11.3c-1.5-0.1-3.1-0.1-4.7,0v11.3
|
||||
c0,3.5-2.6,6.4-6,6.9c-24.7,3.7-44.2,23.1-47.9,47.9c-0.5,3.4-3.4,6-6.9,6H99.4v4.6h11.3c3.5,0,6.4,2.6,6.9,6
|
||||
c3.7,24.7,23.1,44.2,47.9,47.9c3.4,0.5,6,3.4,6,6.9L171.6,337L171.6,337z"/>
|
||||
<path id="path179" class="st4" d="M94,574.3c-14.7-3.2-24.9-12.7-29-27.1c-1-3.4-1.2-6.3-0.9-11.7c0.3-6.5,0.7-7.8,3.5-13.5
|
||||
c11.4-22.9,40.7-27.9,58.6-10c19.4,19.4,12,51.9-13.9,60.8C107.1,574.5,98.5,575.3,94,574.3L94,574.3L94,574.3z M106.6,538
|
||||
c0-7.3-0.2-13.4-0.4-13.4c-0.9,0-23.3,13.2-23,13.5c0.7,0.7,22.2,13.1,22.8,13.2C106.4,551.3,106.6,545.4,106.6,538L106.6,538
|
||||
L106.6,538z"/>
|
||||
<path id="path181" class="st4" d="M168.5,500.6c-6.9-0.9-15-5-20.5-10.4c-14.6-14.5-14.6-36.9,0.1-51.4c23-22.9,62.3-7,62.3,25.4
|
||||
c0,10.4-3.5,18.8-10.7,26C191.3,498.4,180.4,502.1,168.5,500.6L168.5,500.6L168.5,500.6z M187.1,469.9
|
||||
c-0.9-2.4-12.8-22.4-13.3-22.4c-0.4,0-5.9,9-12,19.9l-2,3.6h13.9C185.9,471,187.5,470.9,187.1,469.9L187.1,469.9L187.1,469.9z"/>
|
||||
<path id="path185" class="st4" d="M165.2,647c-12.9-3.5-23.1-13.7-26.5-26.6c-5.9-22.7,11.6-45.2,35.2-45.3
|
||||
c7.4,0,11.3,0.9,18.1,4.5c16.9,8.8,23.9,30.4,15.4,47.9C199.8,643,181.7,651.4,165.2,647L165.2,647L165.2,647z M181.4,616.9
|
||||
c3.6-6.3,6.5-11.6,6.3-11.8s-6.4-0.3-13.8-0.1l-13.5,0.2l6.7,11.6c3.7,6.4,6.9,11.6,7.2,11.6C174.5,628.4,177.8,623.2,181.4,616.9
|
||||
L181.4,616.9z"/>
|
||||
<path id="path187" class="st4" d="M243.9,574.2c-0.3,0-1.6-0.3-2.9-0.5c-11.2-2.2-20.9-9.7-26.2-20.1c-2.8-5.6-4.2-12.2-3.8-17.9
|
||||
c1.1-14.8,11.3-27.7,25.6-32.5c8.8-2.9,18-2.2,26.7,2.2s15.5,12.1,18.6,21.3c5.3,15.3-0.7,32.2-14.7,41.6c-6,4-13.4,6.3-20.2,6.2
|
||||
C245.7,574.3,244.2,574.3,243.9,574.2L243.9,574.2L243.9,574.2z M243.2,551.1c0.7-0.4,3-1.6,5.1-2.8c8.7-4.8,15.6-9.2,16.2-10
|
||||
c0.3-0.4,0.3-0.5,0-0.9c-0.9-1.3-21.5-13.5-22.9-13.5c-0.3,0-0.6,0.1-0.7,0.2c-0.4,0.5-0.6,4.8-0.6,13.9c0,9.1,0.2,13,0.6,13.5
|
||||
C241.3,551.9,241.8,551.8,243.2,551.1L243.2,551.1L243.2,551.1z"/>
|
||||
<path id="path189" class="st5" d="M224,739.1c-1-0.2-3-1-4.4-1.6c-2.1-1-2.9-1.6-4.8-3.5c-1.9-1.9-2.5-2.7-3.5-4.8
|
||||
c-2.7-5.5-2.7-10.6,0-16.1c1-2.1,1.6-2.8,3.5-4.8c3.6-3.6,7.7-5.3,12.7-5.3c8.4,0,15.7,5.7,17.8,13.9c0.5,2,0.5,6.2,0,8.3
|
||||
c-1.5,6.3-6.3,11.5-12.7,13.4C230.2,739.4,226.4,739.6,224,739.1L224,739.1L224,739.1z"/>
|
||||
<path id="path191" class="st4" d="M203.2,751.3c-1.6-0.4-3-1.3-4.1-2.6c-1.8-2.1-1.7-0.9-1.7-27.4s-0.1-25.3,1.9-27.5
|
||||
c0.6-0.7,1.7-1.5,2.5-1.9l1.5-0.7h48.3l1.8,0.9c1.3,0.6,2,1.2,2.7,2.1c1.8,2.3,1.7,1.3,1.6,27.8c-0.1,23.8-0.1,23.9-0.6,25.1
|
||||
c-0.7,1.6-2.3,3.1-3.9,3.9l-1.3,0.7l-23.8,0C210,751.6,204.2,751.5,203.2,751.3L203.2,751.3L203.2,751.3z M230.5,741.6
|
||||
c10.6-1.5,18.1-10.7,17.5-21.4c-0.2-3.4-0.7-5.3-2-8.1c-4.6-9.3-15.5-13.7-25.3-10.2c-2.8,1-4.7,2.2-7,4.3c-3,2.8-5.1,6.3-6,10.3
|
||||
c-0.5,2.2-0.5,7.2,0,9.4c0.9,3.9,3,7.4,6,10.2c2.2,2.1,4.2,3.3,6.7,4.3C223.7,741.7,227.1,742.1,230.5,741.6L230.5,741.6
|
||||
L230.5,741.6z"/>
|
||||
<path id="path1082" class="st6" d="M171.8,330.4c0-6-0.1-6.5-0.7-7.9c-1.1-2.1-2.9-3.4-5.6-3.9c-15.1-2.7-27.5-10.1-36.5-21.9
|
||||
c-5-6.5-8.8-15.1-10.4-23.2c-0.8-4.3-1.4-5.7-3-7c-1.8-1.5-3.3-1.8-10-1.8h-5.9v-4.2h6.3c6.1,0,6.3,0,8-0.9
|
||||
c2.4-1.2,3.3-2.7,4.2-7.3c4.6-23.2,22.4-41,45.6-45.4c4.2-0.8,5.6-1.6,6.9-3.8c0.9-1.5,0.9-1.6,1-8.1l0.1-6.6h4.2v6.3
|
||||
c0,6.2,0,6.3,0.9,8c1.3,2.6,2.6,3.4,7.6,4.4c19.4,3.8,35.1,17,42.3,35.4c1,2.7,1.9,5.9,3.3,12c0.6,2.6,1.7,4.2,3.6,5.2
|
||||
c1.1,0.6,2,0.7,7.9,0.8l6.6,0.1v4.2h-6.3c-6.1,0-6.3,0-8,0.9c-2.2,1.1-3.4,2.9-3.9,5.9c-4.1,23.8-22.2,42.3-46,46.8
|
||||
c-4.2,0.8-5.7,1.7-7,4.1c-0.8,1.5-0.8,1.9-0.9,8l-0.1,6.4h-4.2L171.8,330.4L171.8,330.4z"/>
|
||||
<path id="path1084" class="st7" d="M99.8,257.3c0.1-0.5,0.3-1.9,0.4-3.2c0.4-3.9,1.7-10.1,3.3-14.8c8-24.1,28.1-42.7,52.7-48.9
|
||||
c3.1-0.8,7.6-1.6,11.3-1.9l2-0.2v5.9c0,8.5-0.4,9.1-6.5,10.4c-18.4,3.7-33.7,15.2-41.9,31.9c-2.6,5.1-4.1,10.1-5.5,17
|
||||
c-0.5,2.4-1.8,4-3.7,4.4c-0.7,0.2-3.8,0.3-6.8,0.3h-5.5L99.8,257.3L99.8,257.3z"/>
|
||||
<path id="path1086" class="st7" d="M165.8,336.4c-27-3-50.8-21.4-60.9-46.9c-2.4-6-4.2-13.4-4.7-18.7c-0.1-1.3-0.3-2.7-0.4-3.1
|
||||
l-0.2-0.8l6.3,0.1c5.8,0.1,6.4,0.2,7.2,0.8c1.6,1.2,2,2.2,2.9,6.2c1.2,5.8,2.4,9.4,5.2,14.8c2.9,5.7,5.7,9.8,9.9,14.2
|
||||
c8.6,9.1,19.7,15.2,31.7,17.5c3.6,0.7,4.6,1.2,5.8,2.7c0.6,0.8,0.7,1.4,0.8,7.2c0.1,5.9,0.1,6.3-0.5,6.3
|
||||
C168.6,336.7,167.2,336.6,165.8,336.4L165.8,336.4L165.8,336.4z"/>
|
||||
<path id="path1088" class="st7" d="M235.7,257.7c-2.4-0.9-2.8-1.6-3.8-6.5c-3.7-18.2-15.4-33.5-32-41.6c-4.8-2.4-8.6-3.7-13.5-4.7
|
||||
c-4.7-1-6.2-1.6-7.1-2.9c-0.7-1.1-0.7-1.4-0.7-7.4c0-3.5,0.1-6.3,0.3-6.3s1.9,0.2,4,0.5c12,1.5,22.9,5.6,32.8,12.4
|
||||
c4.4,3,7.4,5.6,11.6,9.9c9.3,9.6,15.5,20.7,18.8,33.5c0.9,3.6,2.1,10.9,2.1,12.9c0,0.6-0.3,0.6-5.6,0.6
|
||||
C238.7,258.1,236.5,258,235.7,257.7L235.7,257.7L235.7,257.7z"/>
|
||||
<path id="path1090" class="st7" d="M178.3,330.5c0.1-5.8,0.2-6.4,0.8-7.2c1.2-1.6,2.2-2,6.3-2.9c23.5-4.7,41.9-23.3,46.4-47
|
||||
c0.7-3.5,1.1-4.5,2.7-5.6c0.8-0.6,1.4-0.7,7.2-0.8l6.3-0.1l-0.2,1.7c-1.2,10.5-3.3,18-7.5,26.4c-11.2,22.6-32.9,38.1-58,41.3
|
||||
c-2.1,0.3-3.9,0.5-4,0.5S178.3,333.9,178.3,330.5L178.3,330.5L178.3,330.5z"/>
|
||||
<path id="path1092" class="st4" d="M247.6,113.5c-0.5-0.6-0.7-1.8-0.7-7.2c0-4.5,0.2-6.7,0.5-7c0.4-0.4,6.4-0.5,24.6-0.5
|
||||
c21.3,0,24.2,0.1,24.8,0.7c0.5,0.6,0.7,1.8,0.7,7c0,6.1,0,6.4-1,7c-0.8,0.6-3.9,0.7-24.6,0.7S248.2,114,247.6,113.5z"/>
|
||||
<path id="path1094" class="st8" d="M15,134.2c-2.3-2.8-2.2-3.7,2.3-12.6C28,99.9,40.9,82,57.8,65.1C89.1,33.7,129.4,12.8,173,5.3
|
||||
c15.1-2.6,16.5-2.7,54-2.9l34.8-0.2l1.4,1.2c1,0.9,1.4,1.7,1.6,3l0.2,1.8l-33.8,0.2C195,8.6,192.5,8.8,178,11.1
|
||||
c-38.4,6.2-72.9,22-103.3,47.3c-6.8,5.7-19.5,18.3-25.2,25.2c-8.4,10-16.5,21.7-22.5,32.4c-3.1,5.4-9.6,18.7-9.6,19.5
|
||||
C17.3,136.4,16.5,136,15,134.2L15,134.2L15,134.2z"/>
|
||||
<path id="path1150" class="st9" d="M334.4,616c0-216,0-634,0.1-526c0.1,108,0.3,614.3,0.1,722.4c0,29.2-0.1,43.2-0.1,45
|
||||
C334.5,862.4,334.5,773.6,334.4,616L334.4,616L334.4,616z"/>
|
||||
<path id="path1152" class="st4" d="M336.6,903.2c-1.5-2.1-0.8-44.2-0.8-417.6S335,70.3,336.5,68.2c1.4-2,3.4-2.2,8.5-2.2
|
||||
c1.8,0,3.8-0.1,5.4,0.2c1.4,0.3,3.2,2,3.5,2.4c0.5,0.5,0.3,4.3,0.4,16.4c0.1,11.3-0.2,29-0.2,55.3c0,44.7,0.6,61.8,0.1,69.8
|
||||
c-0.3,4.4-1.2,6.1-1.9,8.1c-1.2,3.1-3.9,8.2-5.6,10.9l-3.2,5l0.4,40.4c0.4,37.6-0.2,39,1.6,41.7c1.1,1.5,3,6.1,4.6,9.3l3,5.8
|
||||
l1.3,7.9l-0.1,19.8l-0.1,15.8l-0.2,97.9c-0.2,93.1,1.2,126.7-0.3,141.1c-0.4,4.2-1.6,6.5-2.3,8.5c-0.9,2.7-2.1,4.9-3.9,7.8
|
||||
l-3.7,6.1l0.4,15.6l0.1,25.1c0.1,26.5,0.6,40.4,1.3,40.9c0.6,0.4,2.7,4,4.3,8.5l3.3,9l1.1,9l-0.2,15.9l-0.2,57.9
|
||||
c-0.2,61.9,0.4,84.5-0.5,85.5c-0.8,0.9-4.5,2.4-8.4,2.4C340.2,905.8,338,905.1,336.6,903.2L336.6,903.2L336.6,903.2z"/>
|
||||
<path id="path1154" class="st4" d="M325.2,464.9V72.7h8.5v392.2l1.1,393l-5.3,0.2l-4.9-0.2L325.2,464.9L325.2,464.9z"/>
|
||||
<path id="path1156" class="st0" d="M345.9,273.3v-38.6h3c1.7,0,3.6,0.9,4.3,2.1c1.7,2.8,1.7,70.3,0,73.1c-0.7,1.2-2.6,2.1-4.3,2.1
|
||||
h-3V273.3L345.9,273.3z"/>
|
||||
<path id="path1158" class="st0" d="M345.9,676.2v-38.7h3c1.8,0,3.6,0.9,4.3,2.1c1.7,2.8,1.7,70.3,0,73.2c-0.7,1.2-2.6,2.1-4.3,2.1
|
||||
h-3V676.2L345.9,676.2z"/>
|
||||
</g>
|
||||
<g id="g315">
|
||||
<g id="g64" class="st10">
|
||||
<path id="path36" class="st11" d="M654.6,233.9v79.5h-4.2c-3.3,0-6-2.7-6-6v-67.6c0-3.3,2.7-6,6-6h4.2V233.9z"/>
|
||||
<path id="path38" class="st11" d="M654.6,636.6v79.5h-4.2c-3.3,0-6-2.7-6-6v-67.6c0-3.3,2.7-6,6-6L654.6,636.6L654.6,636.6z"/>
|
||||
<path id="path40" class="st11" d="M985.7,134.9l-3.4,3.4C949.1,60.3,872.5,9.6,787.6,9.6h-54.2V7c0-3.3,2.7-6,6-6h51.2
|
||||
C875.4,1,952.3,50.8,987,128.2C988,130.5,987.5,133.1,985.7,134.9L985.7,134.9L985.7,134.9z"/>
|
||||
<path id="path42" class="st11" d="M736.2,94.5V82.8c0-1.6-1.3-3-3-3h-11.7c-1.6,0-3,1.3-3,3l0,0v11.7c0,1.6-1.3,3-3,3h-11.7
|
||||
c-1.6,0-3,1.3-3,3l0,0v11.7c0,1.6,1.3,3,3,3h11.7c1.6,0,3,1.3,3,3l0,0v11.7c0,1.6,1.3,3,3,3h11.7c1.6,0,3-1.3,3-3l0,0v-11.7
|
||||
c0-1.6,1.3-3,3-3h11.7c1.6,0,3-1.3,3-3l0,0v-11.7c0-1.6-1.3-3-3-3h-11.7C737.5,97.5,736.2,96.1,736.2,94.5L736.2,94.5z"/>
|
||||
<circle id="circle44" class="st11" cx="825.6" cy="333.9" r="37.5"/>
|
||||
<circle id="circle46" class="st11" cx="825.6" cy="187.1" r="37.5"/>
|
||||
<circle id="circle48" class="st11" cx="899" cy="260.5" r="37.5"/>
|
||||
<circle id="circle50" class="st11" cx="752.2" cy="260.5" r="37.5"/>
|
||||
<circle id="circle52" class="st11" cx="771.7" cy="721.3" r="27.9"/>
|
||||
<path id="path54" class="st11" d="M822.3,460.3v12.3c0,3-2.2,5.5-5.2,5.9c-25.2,3.7-45,23.5-48.7,48.7c-0.4,2.9-2.9,5.1-5.9,5.2
|
||||
h-12.3C751.9,493.3,783.2,462,822.3,460.3L822.3,460.3L822.3,460.3z"/>
|
||||
<path id="path56" class="st11" d="M822.3,598.8v12.3c-39.1-1.7-70.3-33-72.1-72h12.3c3,0,5.5,2.2,5.9,5.2
|
||||
c3.7,25.2,23.5,45,48.7,48.7C820.1,593.3,822.3,595.8,822.3,598.8L822.3,598.8L822.3,598.8z"/>
|
||||
<path id="path58" class="st11" d="M901,539c-1.7,39.1-33,70.3-72.1,72v-12.3c0-3,2.2-5.5,5.2-5.9c25.2-3.7,45-23.5,48.7-48.7
|
||||
c0.4-2.9,2.9-5.1,5.9-5.2L901,539L901,539z"/>
|
||||
<path id="path60" class="st11" d="M901,532.4h-12.3c-3,0-5.5-2.2-5.9-5.2c-3.7-25.2-23.5-45-48.7-48.7c-2.9-0.4-5.1-2.9-5.2-5.9
|
||||
v-12.3C868,462,899.3,493.3,901,532.4L901,532.4L901,532.4z"/>
|
||||
<path id="path62" class="st11" d="M901.1,535.7c0,1.1,0,2.2-0.1,3.3h-12.3c-3,0-5.5,2.2-5.9,5.2c-3.7,25.2-23.5,45-48.7,48.7
|
||||
c-2.9,0.4-5.1,2.9-5.2,5.9v12.3c-1.1,0.1-2.2,0.1-3.3,0.1s-2.2,0-3.3-0.1v-12.3c0-3-2.2-5.5-5.2-5.9c-25.2-3.7-45-23.5-48.7-48.7
|
||||
c-0.4-2.9-2.9-5.1-5.9-5.2h-12.3c-0.1-1.1-0.1-2.2-0.1-3.3s0-2.2,0.1-3.3h12.3c3,0,5.5-2.2,5.9-5.2c3.7-25.2,23.5-45,48.7-48.7
|
||||
c2.9-0.4,5.1-2.9,5.2-5.9v-12.3c1.1-0.1,2.2-0.1,3.3-0.1s2.2,0,3.3,0.1v12.3c0,3,2.2,5.5,5.2,5.9c25.2,3.7,45,23.5,48.7,48.7
|
||||
c0.4,2.9,2.9,5.1,5.9,5.2H901C901,533.5,901.1,534.6,901.1,535.7L901.1,535.7z"/>
|
||||
</g>
|
||||
<path id="path72" d="M658.3,906.6h-7.9c-3.6,0-6.4-2.9-6.5-6.5V742.9c0-4.9,1.2-9.6,3.4-13.9l6.7-13v-79.2l-6.7-13
|
||||
c-2.2-4.3-3.4-9.1-3.4-14V340.1c0-4.9,1.2-9.6,3.4-13.9l6.7-13V234l-6.7-13c-2.2-4.3-3.4-9.1-3.4-14V71.2c0-3.6,2.9-6.4,6.5-6.5
|
||||
h7.9c3.6,0,6.4,2.9,6.5,6.5c1.1,276.3,1.2,552.6,0,828.9C664.7,903.7,661.8,906.6,658.3,906.6L658.3,906.6L658.3,906.6z
|
||||
M650.4,65.7c-3,0-5.5,2.4-5.5,5.5V207c0,4.7,1.1,9.3,3.3,13.5l6.8,13.1c0,0.1,0.1,0.1,0.1,0.2v79.5c0,0.1,0,0.2-0.1,0.2
|
||||
l-6.8,13.1c-2.2,4.2-3.3,8.8-3.3,13.5v269.7c0,4.7,1.1,9.3,3.3,13.5l6.8,13.1c0,0.1,0.1,0.1,0.1,0.2v79.5c0,0.1,0,0.2-0.1,0.2
|
||||
l-6.8,13.1c-2.2,4.2-3.3,8.8-3.3,13.5v157.2c0,3,2.4,5.5,5.5,5.5h7.9c3,0,5.5-2.4,5.5-5.5V71.2c0-3-2.4-5.5-5.5-5.5L650.4,65.7
|
||||
L650.4,65.7z"/>
|
||||
<path id="path74" d="M675.5,858.9h-11.3c-0.3,0-0.5-0.2-0.5-0.5l0,0L664.5,72c0-0.3,0.2-0.5,0.5-0.5h11.3c0.3,0-0.3,0.2-0.3,0.5
|
||||
v786.4C676,858.7,675.8,858.9,675.5,858.9z M665.6,857.9h9.4l0.8-785.4h-10.3l1,8.8c-0.6,256.1,4.5,511.8-1.1,768.3
|
||||
C665.3,852.3,665.6,855.1,665.6,857.9L665.6,857.9z"/>
|
||||
<path id="path76" d="M787.4,1000.2H681.2c-3.6,0-6.5-2.9-6.5-6.5v-978c0-3.6,2.9-6.5,6.5-6.5h106.2
|
||||
c116.8,0,211.9,95.1,211.9,211.9v567.2C999.3,905.3,904.5,1000.2,787.4,1000.2L787.4,1000.2z M681.2,10.4c-3,0-5.5,2.4-5.5,5.5
|
||||
v978c0,3,2.4,5.5,5.5,5.5h106.2c116.3,0,210.9-94.6,210.9-210.9V221.1c0-116.3-94.6-210.9-210.9-210.9L681.2,10.4z"/>
|
||||
<path id="path128" d="M654.6,314.3h-4.2c-3.8,0-6.9-3.1-7-7v-67.6c0-3.8,3.1-6.9,7-7h4.2c0.6,0,1,0.4,1,1l0,0v79.5
|
||||
C655.6,313.9,655.1,314.3,654.6,314.3L654.6,314.3z M650.4,234.8c-2.7,0-5,2.2-5,5v67.6c0,2.7,2.2,5,5,5h3.2v-77.5h-3.2V234.8z"/>
|
||||
<path id="path130" d="M654.6,717.1h-4.2c-3.8,0-6.9-3.1-7-7v-67.6c0-3.8,3.1-6.9,7-7h4.2c0.6,0,1,0.4,1,1l0,0V716
|
||||
C655.6,716.6,655.1,717.1,654.6,717.1L654.6,717.1z M650.4,637.6c-2.7,0-5,2.2-5,5v67.6c0,2.7,2.2,5,5,5h3.2v-77.5L650.4,637.6
|
||||
L650.4,637.6z"/>
|
||||
<path id="path1240" class="st12" d="M805.5,998.7c26.6-2.4,50.5-9.2,75.9-21.8c5.1-2.5,10-5.2,14.9-8
|
||||
c19.1-11.3,36.3-27.1,49.6-41.7c25.5-28,45.5-70.1,51.4-116.5c0.1-0.7,0.4-4.3,0.4-6.8c-0.1-6.9,0.7-20,0.8-38.3
|
||||
c0.7-77.5,1.1-244,1.1-376.4c0-101.2-0.3-182.5-1-188.9c-2.7-26.1-9.3-49.1-20.8-72.1c-31.8-63.9-93-107.4-164-116.7
|
||||
c-11.4-1.5-60.7-1.6-96.6-1.3c-7.7,0.1-14.1-0.1-20.1,0.1c-7.5,0.2-12.9-0.1-16.2,0.2c-1.7,0.2-3,0.8-3.2,1
|
||||
c-0.1,0.1-1.4,1.4-1.5,3.3c-0.3,5.2-0.3,17.1-0.4,38c0,4.2-0.1,8.8-0.1,13.8c0,4.9,0.1,10.1,0.1,15.8c-0.1,8.8,0.1,18.6,0.1,29.2
|
||||
c0,9.8-0.2,20.5,0,32c0.2,9,0.2,18.4,0.2,28.4c-0.1,15.1,0.6,31.1,0.3,49c-0.2,15,0.1,30,0,46.9c-0.1,11.1,0.1,22.6,0,34.6
|
||||
c-0.1,7.2,0,14.5-0.1,22.1c-0.5,52.1,0,112.2,0,180.3c0,254.3-0.3,376.1,0,435.2c0,7.7-0.2,14.5-0.1,20.2c0,2.2,0,4.3,0,6.2
|
||||
c0,3.3,0,6.6,0,9.3c0,2.9-0.1,5.8,0,8c0.1,5,0.1,8.1,0.3,10c0.3,2.8,1.3,3.6,1.6,3.8c0.2,0.2,1.3,0.9,3.1,1.1
|
||||
c4.5,0.4,14.6,0.3,27.1,0.2c9.9-0.1,21.3-0.2,32.8-0.2C772.7,998.8,805.8,999,805.5,998.7L805.5,998.7L805.5,998.7z"/>
|
||||
<path id="path78" d="M771.7,763.2c-23.1,0-41.9-18.7-41.9-41.9s18.7-41.9,41.9-41.9c23.2,0,41.9,18.7,41.9,41.9l0,0
|
||||
C813.5,744.4,794.8,763.1,771.7,763.2z M771.7,680.4c-22.6,0-40.9,18.3-40.9,40.9s18.3,40.9,40.9,40.9c22.6,0,40.9-18.3,40.9-40.9
|
||||
l0,0C812.5,698.7,794.3,680.4,771.7,680.4z"/>
|
||||
<path id="path88" class="st13" d="M838.9,203.2h-5.5l-7.6-10.9l-8,10.9h-5.4l10.6-16.3l-9.8-15.6h5.2l7.4,10.6l7.3-10.6h5
|
||||
l-9.8,15.4L838.9,203.2L838.9,203.2z"/>
|
||||
<path id="path90" class="st13" d="M765.9,244.5l-11.6,20.6v11.4h-4.4V265l-11.6-20.5h5.3l6.4,11.7l2.1,3l2.4-2.6l6.4-12.1H765.9
|
||||
L765.9,244.5z"/>
|
||||
<path id="path92" class="st13" d="M912.6,276.5h-4.7l-2.2-7l-12.4,0.3l-3.2,6.7h-4.5l9.9-35.2l6.7,3.2L912.6,276.5z M903.9,265.2
|
||||
l-4.9-14.6l-4.6,14.8L903.9,265.2L903.9,265.2z"/>
|
||||
<path id="path132" d="M982.3,139.3h-0.2c-0.3-0.1-0.6-0.3-0.7-0.6C948.4,61,872.1,10.5,787.6,10.6h-54.2c-0.6,0-1-0.4-1-1l0,0V7
|
||||
c0-3.8,3.1-6.9,7-7h51.2C875.8,0.1,953,50.1,987.9,127.8c1.2,2.6,0.6,5.7-1.5,7.8L983,139C982.8,139.2,982.5,139.3,982.3,139.3
|
||||
L982.3,139.3z M734.4,8.6h53.2c84.7-0.1,161.3,50.2,195,128l2.4-2.4l0,0c1.5-1.4,1.9-3.6,1.1-5.5C951.5,51.7,875,2.1,790.6,2
|
||||
h-51.2c-2.7,0-5,2.2-5,5V8.6z"/>
|
||||
<path id="path134" d="M733.2,133.7h-11.7c-2.2,0-4-1.8-4-4V118c0-1.1-0.9-2-2-2h-11.7c-2.2,0-4-1.8-4-4v-11.7c0-2.2,1.8-4,4-4
|
||||
h11.7c1.1,0,2-0.9,2-2V82.8c0-2.2,1.8-4,4-4h11.7c2.2,0,4,1.8,4,4v11.7c0,1.1,0.9,2,2,2h11.7c2.2,0,4,1.8,4,4v11.7
|
||||
c0,2.2-1.8,4-4,4h-11.7c-1.1,0-2,0.9-2,2v11.7C737.2,131.9,735.4,133.7,733.2,133.7z M703.9,98.5c-1.1,0-2,0.9-2,2v11.7
|
||||
c0,1.1,0.9,2,2,2h11.7c2.2,0,4,1.8,4,4v11.7c0,1.1,0.9,2,2,2h11.7c1.1,0,2-0.9,2-2v-11.7c0-2.2,1.8-4,4-4H751c1.1,0,2-0.9,2-2
|
||||
v-11.7c0-1.1-0.9-2-2-2h-11.7c-2.2,0-4-1.8-4-4V82.8c0-1.1-0.9-2-2-2h-11.7c-1.1,0-2,0.9-2,2v11.7c0,2.2-1.8,4-4,4H703.9z"/>
|
||||
<path id="path136" d="M825.6,372.4c-21.2,0-38.5-17.2-38.5-38.5s17.2-38.5,38.5-38.5c21.3,0,38.5,17.2,38.5,38.5
|
||||
C864,355.2,846.8,372.4,825.6,372.4z M825.6,297.5c-20.1,0-36.5,16.3-36.5,36.5s16.3,36.5,36.5,36.5c20.2,0,36.5-16.3,36.5-36.5
|
||||
C862,313.8,845.7,297.5,825.6,297.5z"/>
|
||||
<path id="path138" d="M825.6,225.5c-21.2,0-38.5-17.2-38.5-38.5s17.2-38.5,38.5-38.5c21.3,0,38.5,17.2,38.5,38.5
|
||||
C864,208.3,846.8,225.5,825.6,225.5z M825.6,150.6c-20.1,0-36.5,16.3-36.5,36.5s16.3,36.5,36.5,36.5c20.2,0,36.5-16.3,36.5-36.5
|
||||
C862,166.9,845.7,150.6,825.6,150.6z"/>
|
||||
<path id="path140" d="M899,299c-21.2,0-38.5-17.2-38.5-38.5S877.7,222,899,222c21.3,0,38.5,17.2,38.5,38.5S920.3,298.9,899,299z
|
||||
M899,224c-20.1,0-36.5,16.3-36.5,36.5S878.8,297,899,297c20.2,0,36.5-16.3,36.5-36.5S919.2,224,899,224z"/>
|
||||
<path id="path142" d="M752.2,299c-21.2,0-38.5-17.2-38.5-38.5s17.2-38.5,38.5-38.5c21.3,0,38.5,17.2,38.5,38.5
|
||||
C790.6,281.7,773.4,298.9,752.2,299z M752.2,224c-20.1,0-36.5,16.3-36.5,36.5s16.2,36.2,36.4,36.2s36.6-16,36.6-36.2
|
||||
C788.6,240.4,772.3,224,752.2,224z"/>
|
||||
<path id="path144" d="M771.7,750.2c-16,0-28.9-13-28.9-28.9s13-28.9,28.9-28.9s28.9,13,28.9,28.9l0,0
|
||||
C800.6,737.3,787.7,750.2,771.7,750.2z M771.7,694.3c-14.9,0-26.9,12.1-26.9,26.9s12.1,26.9,26.9,26.9s26.9-12.1,26.9-26.9
|
||||
S786.6,694.4,771.7,694.3z"/>
|
||||
<path id="path146" d="M762.5,533.4h-12.3c-0.6,0-1-0.4-1-1l0,0c1.7-39.6,33.4-71.3,73-73c0.3,0,0.5,0.1,0.7,0.3
|
||||
c0.2,0.2,0.3,0.4,0.3,0.7v12.3c0,3.5-2.6,6.4-6,6.9c-24.7,3.7-44.2,23.1-47.9,47.9C768.9,530.8,766,533.4,762.5,533.4z
|
||||
M751.3,531.4h11.2c2.5,0,4.6-1.8,4.9-4.3c3.9-25.6,24-45.7,49.6-49.6c2.5-0.3,4.3-2.4,4.3-4.9v-11.2
|
||||
C783.9,463.5,753.4,494,751.3,531.4z"/>
|
||||
<path id="path148" d="M822.3,612.1C822.3,612.1,822.2,612.1,822.3,612.1c-39.6-1.7-71.3-33.4-73-73c0-0.6,0.4-1,1-1h12.3
|
||||
c3.5,0,6.4,2.6,6.9,6c3.7,24.7,23.1,44.2,47.9,47.9c3.4,0.5,6,3.4,6,6.9v12.3C823.3,611.6,822.8,612.1,822.3,612.1L822.3,612.1
|
||||
L822.3,612.1z M751.3,540c2.2,37.4,32.6,67.8,70,70v-11.2c0-2.5-1.8-4.6-4.3-4.9c-25.6-3.9-45.7-24-49.6-49.6
|
||||
c-0.3-2.5-2.4-4.3-4.9-4.3H751.3z"/>
|
||||
<path id="path150" d="M828.9,612.1c-0.6,0-1-0.4-1-1l0,0v-12.3c0-3.5,2.6-6.4,6-6.9c24.7-3.7,44.2-23.1,47.9-47.9
|
||||
c0.5-3.4,3.4-6,6.9-6H901c0.6,0,1,0.4,1,1l0,0C900.2,578.7,868.6,610.3,828.9,612.1C829,612.1,829,612.1,828.9,612.1L828.9,612.1
|
||||
L828.9,612.1z M888.7,540c-2.5,0-4.6,1.8-4.9,4.3c-3.9,25.6-24,45.7-49.6,49.6c-2.5,0.3-4.3,2.4-4.3,4.9V610
|
||||
c37.4-2.2,67.8-32.6,70-70H888.7z"/>
|
||||
<path id="path152" d="M901,533.4h-12.3c-3.5,0-6.4-2.6-6.9-6c-3.7-24.7-23.1-44.2-47.9-47.9c-3.4-0.5-6-3.4-6-6.9v-12.3
|
||||
c0-0.3,0.1-0.5,0.3-0.7c0.2-0.2,0.5-0.3,0.7-0.3c39.6,1.7,71.3,33.4,73,73C902,532.9,901.6,533.3,901,533.4L901,533.4L901,533.4z
|
||||
M829.9,461.4v11.2c0,2.5,1.8,4.6,4.3,4.9c25.6,3.9,45.7,24,49.6,49.6c0.3,2.5,2.4,4.3,4.9,4.3h11.2
|
||||
C897.8,494,867.3,463.5,829.9,461.4z"/>
|
||||
<path id="path154" d="M825.6,612.2c-1.2,0-2.3,0-3.4-0.1c-0.5,0-0.9-0.5-0.9-1v-12.3c0-2.5-1.8-4.6-4.3-4.9
|
||||
c-25.6-3.9-45.7-24-49.6-49.6c-0.3-2.5-2.4-4.3-4.9-4.3h-12.3c-0.5,0-1-0.4-1-0.9c-0.1-1.1-0.1-2.2-0.1-3.4s0-2.3,0.1-3.4
|
||||
c0-0.5,0.5-0.9,1-0.9h12.3c2.5,0,4.6-1.8,4.9-4.3c3.9-25.6,24-45.7,49.6-49.6c2.5-0.3,4.3-2.4,4.3-4.9v-12.3c0-0.5,0.4-1,0.9-1
|
||||
c2.3-0.1,4.5-0.1,6.8,0c0.5,0,0.9,0.5,0.9,1v12.3c0,2.5,1.8,4.6,4.3,4.9c25.6,3.9,45.7,24,49.6,49.6c0.3,2.5,2.4,4.3,4.9,4.3H901
|
||||
c0.5,0,1,0.4,1,0.9c0.1,1.1,0.1,2.2,0.1,3.4s0,2.3-0.1,3.4c0,0.5-0.5,0.9-1,0.9h-12.3c-2.5,0-4.6,1.8-4.9,4.3
|
||||
c-3.9,25.6-24,45.7-49.6,49.6c-2.5,0.3-4.3,2.4-4.3,4.9v12.3c0,0.5-0.4,1-0.9,1C827.9,612.1,826.8,612.2,825.6,612.2L825.6,612.2
|
||||
L825.6,612.2z M823.3,610.1c1.5,0.1,3.1,0.1,4.7,0v-11.3c0-3.5,2.6-6.4,6-6.9c24.7-3.7,44.2-23.1,47.9-47.9c0.5-3.4,3.4-6,6.9-6
|
||||
h11.3v-4.6h-11.4c-3.5,0-6.4-2.6-6.9-6c-3.7-24.7-23.1-44.2-47.9-47.9c-3.4-0.5-6-3.4-6-6.9v-11.3c-1.5-0.1-3.1-0.1-4.7,0v11.3
|
||||
c0,3.5-2.6,6.4-6,6.9c-24.7,3.7-44.2,23.1-47.9,47.9c-0.5,3.4-3.4,6-6.9,6h-11.3v4.6h11.3c3.5,0,6.4,2.6,6.9,6
|
||||
c3.7,24.7,23.1,44.2,47.9,47.9c3.4,0.5,6,3.4,6,6.9L823.3,610.1L823.3,610.1L823.3,610.1z"/>
|
||||
<path id="path1462" class="st4" d="M743,295.5c-6.3-1.7-11.3-3.9-16.5-9.1c-7.7-7.7-11.3-17-10.8-27.7c0.6-13.5,8-25,20-31
|
||||
c10.5-5.2,22.6-5.3,32.7-0.3c7.9,3.9,12.8,10,16.6,18c5.2,10.8,4.2,24.4-2.4,34.6c-5,7.6-13.5,13.7-22,15.7
|
||||
C755.7,296.8,747.8,296.7,743,295.5L743,295.5L743,295.5z"/>
|
||||
<path id="path1464" class="st4" d="M819.4,222.9c0-0.3-3.7-0.9-5.7-1.6c-3-1.1-5.9-2.6-8-4c-6.9-4.6-11.7-10.9-14.5-18.9
|
||||
c-1.3-3.8-2.1-5.2-2.1-11.4s0.7-7.2,2.1-11c3.9-11.1,11.8-19.1,22.8-23c4.2-1.5,5.2-2.3,11.6-2.3c6.4,0,7.3,0.8,11.6,2.3
|
||||
c14.5,5.2,24,17.6,24.7,32.9c0.5,10.6-2.7,19.5-10.2,26.9c-7.6,7.5-14.4,10.7-24.6,11C823.6,223.7,820.2,223,819.4,222.9
|
||||
L819.4,222.9L819.4,222.9z"/>
|
||||
<path id="path1466" class="st4" d="M891.7,296.5c-6.7-1-15.6-5.8-20.8-12.9c-3-4.1-6.2-10.7-7.3-15.7
|
||||
c-2.8-13.2,2.6-27.9,13.3-35.9c20-15.1,48.1-6.7,56.4,17c1.9,5.3,3,13.3,1.7,19.2c-1.5,6.8-4.9,12.9-10.2,18.2
|
||||
c-5.3,5.2-9.9,8-16.4,9.6C903.5,297.2,896.3,297.2,891.7,296.5L891.7,296.5L891.7,296.5z"/>
|
||||
<path id="path1468" class="st4" d="M820.4,369.9c-8.1-1.3-14.2-4-20.6-10.3c-3.8-3.7-5.4-6.2-7.2-10c-7.6-15.8-3-33.1,10.8-44.1
|
||||
c6.4-5.1,13.6-7.6,22.1-7.6c29.9,0,46.7,33.8,28.7,58c-2.8,3.8-6.3,7.5-10.5,9.8C836.7,369.8,828.1,371.1,820.4,369.9L820.4,369.9
|
||||
L820.4,369.9z"/>
|
||||
<path id="path1500" class="st6" d="M823.6,602.7c-0.3-8.9-0.8-9.7-10.1-11.9c-21.1-4.8-37.2-20.1-42.6-41.3
|
||||
c-0.6-2.2-1.1-5.2-1.1-5.9s-1-2.2-2.2-3.5l-2.3-2l-6.9-0.3h-7v-4h5.9c6.6,0,9.9-1,11.2-3.4c0.4-0.8,1.5-3.9,2-7
|
||||
c2.7-16.3,14.9-30.7,29.5-38c4.7-2.4,11.4-4.6,15.5-5.4c6.6-1.1,8.2-3.6,8.2-12.8v-5.9h4v6.9c0,6.6,0.1,7,1.8,8.8
|
||||
c1.5,1.6,3.9,1.7,9.5,3.1c20.4,5,35.3,20.5,40.7,40.1c0.8,2.9,1.8,6.4,2.2,7.8c1.2,4.3,3.8,5.6,11.7,5.6h6.5v4h-6.6
|
||||
c-7,0-9.9,0.8-10.8,3.1c-0.3,0.7-1.3,4.6-2.1,8c-5.4,21.4-21.3,36.7-42.1,41.5c-9.9,2.3-10.7,3.2-10.7,12.8v6.3h-3.8L823.6,602.7
|
||||
L823.6,602.7z"/>
|
||||
<path id="path1504" class="st7" d="M752.2,526.5c1.8-15.3,8.9-31.2,20.4-43c11.9-12.2,27.5-19.2,45.7-21.6l2.8-0.4v6.2
|
||||
c0,7.9-0.1,8.9-6.8,10.2c-22.3,4.6-41.8,22.1-46.1,44.2c-1.8,9.1-2.4,9.1-11.1,9.1h-5.4L752.2,526.5L752.2,526.5z"/>
|
||||
<path id="path1506" class="st7" d="M811.2,608.2c-32.2-6.9-55.7-32.9-59.4-65.6l-0.2-2.1h6.4c6.9,0,6.8,0,7.8,1
|
||||
c1.3,1.4,1.5,3.8,2.4,7.9c4.8,21.2,24.2,39.1,46.6,44c6.9,1.5,6.4,2.7,6.4,10.3v5.9l-2.1,0C817.8,609.5,814.3,608.9,811.2,608.2
|
||||
L811.2,608.2L811.2,608.2z"/>
|
||||
<path id="path1508" class="st7" d="M830,603c-0.3-7.3,1-8.2,5.3-9c4.8-0.8,11.1-2.9,16.3-5.5c15.8-7.7,27.1-22,31.6-39.9
|
||||
c1-4,1.6-7.2,2.2-7.7c0.7-0.5,3.9-0.5,7.8-0.5h6.2l-0.4,3.8c-3.8,33.2-31.8,61.3-64.8,65l-4,0.5L830,603L830,603z"/>
|
||||
<path id="path1510" class="st7" d="M885.4,529.8c-1.2-1.1-1.3-3-2.1-6.7c-4.4-20.5-19-36.6-39.3-43.5c-2.4-0.8-5.8-1.7-7.5-2
|
||||
c-1.9-0.3-3.9-0.6-4.8-1.5c-1.4-1.4-1.3-2.6-1.3-8.2v-6.2l2.5,0.3c19,2.5,32.7,9.2,45.4,22.1c10.4,10.5,17.4,23.8,20.2,38.1
|
||||
c0.6,2.9,1,6.1,1,7.1v1.9h-5.9C888.3,531.3,886.8,531.1,885.4,529.8L885.4,529.8L885.4,529.8z"/>
|
||||
<path id="path1512" class="st4" d="M720.3,131c-0.5-0.6-0.6-1.7-0.6-7.6v-6.9l-1.1-1.2l-1.1-1.2l-7.1-0.1
|
||||
c-4.1-0.1-6.5,0.5-7.6-0.2c-1.3-0.8-0.9-3.1-0.9-7.6s0.1-6.3,0.5-6.8c0.5-0.7,1.1-0.7,7.3-0.8c3.7-0.1,7.1-0.2,7.6-0.3
|
||||
c0.5-0.1,1.2-0.7,1.6-1.4c0.7-1.1,0.7-1.9,0.7-7.8c0-3.8,0.2-6.8,0.4-7.3c0.4-0.7,0.9-0.7,7.3-0.7s7,0.1,7.3,0.7
|
||||
c0.2,0.4,0.4,3.5,0.4,7.3c0,5.8,0.1,6.7,0.7,7.8c0.4,0.7,1.1,1.3,1.6,1.4s3.9,0.2,7.6,0.3c6.2,0.1,6.8,0.2,7.3,0.8
|
||||
c0.4,0.6,0.5,2.2,0.5,6.8s0.7,7.2-0.9,7.9c-1.2,0.5-3.8-0.1-7.6-0.1l-7.1,0.1l-1.1,1.2l-1.1,1.2v6.9c0,6.2-0.1,7-0.7,7.6
|
||||
c-0.6,0.5-1.7,0.6-7.1,0.6C721.7,131.7,720.9,131.6,720.3,131L720.3,131L720.3,131z"/>
|
||||
<path id="path1514" class="st7" d="M766.6,726.4v-5.3h10.5v10.5h-10.5V726.4L766.6,726.4z"/>
|
||||
<path id="path1518" class="st7" d="M766,761.6c-14.8-2.3-27.2-12.3-32.4-26.2c-7.5-20,2.3-43,22-51.4c6.1-2.6,8-3.5,15.7-3.5
|
||||
c6.5,0,7.9,0.6,11.3,1.6c14.4,4.4,24.6,14.9,28.6,29.4c0.8,2.9,1.1,5.2,1.1,10.7c0,5.5-0.3,6.2-1.1,9c-4.8,17.7-20,30-38.1,30.5
|
||||
C770.5,761.9,767.4,761.8,766,761.6L766,761.6L766,761.6z M778.1,749.7c11.9-2.6,21.3-13,22.5-25.1c2.2-21.5-18.1-37.7-38.5-30.7
|
||||
c-4.2,1.4-7,3.3-10.8,7.1c-2.8,2.8-3.8,4.1-5.3,7.2c-2.6,5.1-3.3,8.7-3,14.6c0.3,6.8,2.2,11.7,6.3,16.9
|
||||
C755.9,748,767.4,752,778.1,749.7L778.1,749.7L778.1,749.7z"/>
|
||||
<path id="path1520" class="st9" d="M648.2,714.3c-1.3-0.8-2.3-0.9-2.5-5.3c-0.2-4.3,0.1-13.5,0.1-32.6c0-32.8-0.5-35.2,0.6-36.3
|
||||
c0.1-0.1,0.1-0.2,0.2-0.2c1.1-1.5,2.7-2.1,4.9-2.1h1.9V715h-2C650.1,715,648.9,714.8,648.2,714.3L648.2,714.3L648.2,714.3z"/>
|
||||
<path id="path1522" class="st9" d="M649.6,312.1c-0.5-0.1-1.3-0.4-1.8-0.8c-0.8-0.5-1.5-0.8-1.8-2.7c-0.5-3.2-0.2-11.4-0.2-35.1
|
||||
c0-19.3-0.5-28.4-0.4-32.5c0.1-2.7,0.8-3.2,1-3.6c0.9-1.6,2.4-2.3,4.8-2.3h2.1v77.3l-1.4,0C651.2,312.3,650.1,312.2,649.6,312.1
|
||||
L649.6,312.1L649.6,312.1z"/>
|
||||
<path id="path1524" class="st8" d="M978,126.4c-10.1-20.6-21.9-37.2-38.2-53.9c-34.8-35.7-78.7-57-129.6-63.1
|
||||
c-5.1-0.6-13.1-0.8-40.9-1l-34.6-0.2V6.9c0-1,0-3,1.4-3.7l2.1-1.1l30.4,0.2c16.9,0.1,33.6,0.2,37,0.5
|
||||
c50.8,3.9,97.1,24.6,133.3,59.5c17.6,16.9,31.5,35.7,42.8,57.9c4.3,8.4,4.9,10.1,4.2,11.8c-0.4,1.1-2.7,4.2-3.2,4.1
|
||||
C982.6,136.1,980.5,131.5,978,126.4L978,126.4L978,126.4z"/>
|
||||
<path id="path1530" class="st4" d="M646.8,903.8c-0.6-0.5-1.7-1.2-1.6-5c0.3-6.7-0.2-27.2-0.1-79.1l0.3-82.6l2.6-6.5l7.9-15
|
||||
l0.1-39.4l-0.4-39.6l-7.1-13.2l-2.9-7.7l-0.4-7.3l0.1-48l-0.1-22.1l0.1-34.1l0.1-10.5V483l-0.1-8.6l0-25.6v-19.4l-0.2-27.5
|
||||
l0.1-21.6l0-11l0.1-19.6l0-10.2l0.5-5.7l1.9-5.8l3-5.5l5.3-9.8v-39.5l-0.4-39.6l-5.3-9.8l-2.7-5.3l-1.8-5.1l-0.7-7.6l0.2-13.3
|
||||
l-0.1-12.2v-11.1l-0.1-10.9l0-9.9V142l0-2.9l0.1-17.4l0-21.5V89.4l-0.1-8.4l0-5.1l-0.2-5.9l2.2-3.1l3.2-1.1l3.2,0.1
|
||||
c1.4,0,2.7,0.1,3.9,0.2c0.9,0.1,1.8,0.1,2.6,0.4c1,0.4,1.7,1,2,1.3c0.2,0.2,1.2,1.7,1.3,4.8c0.1,2.1,0,4.7,0,8.3
|
||||
c0,4.1,0.1,9.7,0,16.4c-0.1,10,0,23.1-0.1,40.4c0,13.2,0.2,28.7,0.1,47c-0.2,62.8-0.2,158-0.2,301l0,416.2l-1.3,1.8
|
||||
C660.4,906.2,649.5,906.5,646.8,903.8L646.8,903.8L646.8,903.8z"/>
|
||||
<path id="path1532" class="st4" d="M665.8,465.1V72.7h8.6v784.7h-8.6V465.1L665.8,465.1z"/>
|
||||
<ellipse id="path3879" class="st14" cx="771.6" cy="721.1" rx="40.3" ry="40.3"/>
|
||||
<ellipse id="path3881" class="st15" cx="771.7" cy="721.4" rx="34.1" ry="34.5"/>
|
||||
<path id="path96" d="M771.7,694.3l-26.8,26.4h7.7v22.5h38.5v-22.5h7.2L771.7,694.3L771.7,694.3z M779.5,735.2h-15.2v-14.5h15.2
|
||||
V735.2z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 32 KiB |
185
src/Ryujinx.Ava/Assets/Icons/Controller_JoyConRight.svg
Normal file
185
src/Ryujinx.Ava/Assets/Icons/Controller_JoyConRight.svg
Normal file
@ -0,0 +1,185 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 27.9.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 1000.4 356.1" style="enable-background:new 0 0 1000.4 356.1;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{opacity:0.1;}
|
||||
.st1{fill:#FF5F55;}
|
||||
.st2{fill:#FF5F53;}
|
||||
.st3{fill:#FFFFFF;}
|
||||
.st4{fill:#1A1A1A;stroke:#000000;}
|
||||
.st5{fill:#1A1A1A;stroke:#1A1A1A;}
|
||||
.st6{fill:#1A1A1A;stroke:#4D4D4D;}
|
||||
.st7{stroke:#000000;}
|
||||
.st8{stroke:#4D4D4D;}
|
||||
.st9{fill:#999595;stroke:#000000;stroke-width:2.39;stroke-linecap:round;stroke-linejoin:round;}
|
||||
.st10{fill:#3A3D40;stroke:#000000;stroke-width:2.73;stroke-linecap:round;stroke-linejoin:round;}
|
||||
</style>
|
||||
<g id="g315">
|
||||
<g id="g64" class="st0">
|
||||
<path id="path36" class="st1" d="M766.5,11.2H687V7c0-3.3,2.7-6,6-6h67.6c3.3,0,6,2.7,6,6L766.5,11.2L766.5,11.2z"/>
|
||||
<path id="path38" class="st1" d="M363.8,11.2h-79.5V7c0-3.3,2.7-6,6-6h67.6c3.3,0,6,2.7,6,6L363.8,11.2L363.8,11.2z"/>
|
||||
<path id="path40" class="st1" d="M865.5,342.3l-3.4-3.4c78-33.2,128.7-109.8,128.7-194.7V90h2.6c3.3,0,6,2.7,6,6v51.2
|
||||
c0,84.8-49.8,161.7-127.2,196.4C869.9,344.6,867.3,344.1,865.5,342.3L865.5,342.3L865.5,342.3z"/>
|
||||
<path id="path42" class="st1" d="M905.9,92.8h11.7c1.6,0,3-1.3,3-3V78.1c0-1.6-1.3-3-3-3l0,0h-11.7c-1.6,0-3-1.3-3-3V60.4
|
||||
c0-1.6-1.3-3-3-3l0,0h-11.7c-1.6,0-3,1.3-3,3v11.7c0,1.6-1.3,3-3,3l0,0h-11.7c-1.6,0-3,1.3-3,3v11.7c0,1.6,1.3,3,3,3l0,0h11.7
|
||||
c1.6,0,3,1.3,3,3v11.7c0,1.6,1.3,3,3,3l0,0h11.7c1.6,0,3-1.3,3-3V95.8C902.9,94.1,904.3,92.8,905.9,92.8L905.9,92.8z"/>
|
||||
<circle id="circle44" class="st1" cx="666.5" cy="182.2" r="37.5"/>
|
||||
<circle id="circle46" class="st1" cx="813.3" cy="182.2" r="37.5"/>
|
||||
<circle id="circle48" class="st1" cx="739.9" cy="255.6" r="37.5"/>
|
||||
<circle id="circle50" class="st1" cx="739.9" cy="108.8" r="37.5"/>
|
||||
<circle id="circle52" class="st1" cx="279.1" cy="128.3" r="27.9"/>
|
||||
<path id="path54" class="st1" d="M540.1,178.9h-12.3c-3,0-5.5-2.2-5.9-5.2c-3.7-25.2-23.5-45-48.7-48.7c-2.9-0.4-5.1-2.9-5.2-5.9
|
||||
v-12.3C507.1,108.5,538.4,139.8,540.1,178.9L540.1,178.9L540.1,178.9z"/>
|
||||
<path id="path56" class="st1" d="M401.6,178.9h-12.3c1.7-39.1,33-70.3,72-72.1v12.3c0,3-2.2,5.5-5.2,5.9
|
||||
c-25.2,3.7-45,23.5-48.7,48.7C407.1,176.7,404.6,178.9,401.6,178.9L401.6,178.9L401.6,178.9z"/>
|
||||
<path id="path58" class="st1" d="M461.4,257.6c-39.1-1.7-70.3-33-72-72.1h12.3c3,0,5.5,2.2,5.9,5.2c3.7,25.2,23.5,45,48.7,48.7
|
||||
c2.9,0.4,5.1,2.9,5.2,5.9L461.4,257.6L461.4,257.6z"/>
|
||||
<path id="path60" class="st1" d="M468,257.6v-12.3c0-3,2.2-5.5,5.2-5.9c25.2-3.7,45-23.5,48.7-48.7c0.4-2.9,2.9-5.1,5.9-5.2h12.3
|
||||
C538.4,224.6,507.1,255.9,468,257.6L468,257.6L468,257.6z"/>
|
||||
<path id="path62" class="st1" d="M464.7,257.7c-1.1,0-2.2,0-3.3-0.1v-12.3c0-3-2.2-5.5-5.2-5.9c-25.2-3.7-45-23.5-48.7-48.7
|
||||
c-0.4-2.9-2.9-5.1-5.9-5.2h-12.3c-0.1-1.1-0.1-2.2-0.1-3.3s0-2.2,0.1-3.3h12.3c3,0,5.5-2.2,5.9-5.2c3.7-25.2,23.5-45,48.7-48.7
|
||||
c2.9-0.4,5.1-2.9,5.2-5.9v-12.3c1.1-0.1,2.2-0.1,3.3-0.1s2.2,0,3.3,0.1v12.3c0,3,2.2,5.5,5.2,5.9c25.2,3.7,45,23.5,48.7,48.7
|
||||
c0.4,2.9,2.9,5.1,5.9,5.2h12.3c0.1,1.1,0.1,2.2,0.1,3.3s0,2.2-0.1,3.3h-12.3c-3,0-5.5,2.2-5.9,5.2c-3.7,25.2-23.5,45-48.7,48.7
|
||||
c-2.9,0.4-5.1,2.9-5.2,5.9v12.3C466.9,257.6,465.8,257.7,464.7,257.7L464.7,257.7z"/>
|
||||
</g>
|
||||
<path id="path72" d="M93.8,14.9V7c0-3.6,2.9-6.4,6.5-6.5h157.2c4.9,0,9.6,1.2,13.9,3.4l13,6.7h79.2l13-6.7c4.3-2.2,9.1-3.4,14-3.4
|
||||
h269.7c4.9,0,9.6,1.2,13.9,3.4l13,6.7h79.2l13-6.7c4.3-2.2,9.1-3.4,14-3.4h135.8c3.6,0,6.4,2.9,6.5,6.5v7.9c0,3.6-2.9,6.4-6.5,6.5
|
||||
c-276.3,1.1-552.6,1.2-828.9,0C96.7,21.3,93.8,18.4,93.8,14.9L93.8,14.9L93.8,14.9z M934.7,7c0-3-2.4-5.5-5.5-5.5H793.4
|
||||
c-4.7,0-9.3,1.1-13.5,3.3l-13.1,6.8c-0.1,0-0.1,0.1-0.2,0.1h-79.5c-0.1,0-0.2,0-0.2-0.1l-13.1-6.8c-4.2-2.2-8.8-3.3-13.5-3.3H390.6
|
||||
c-4.7,0-9.3,1.1-13.5,3.3L364,11.6c-0.1,0-0.1,0.1-0.2,0.1h-79.5c-0.1,0-0.2,0-0.2-0.1L271,4.8c-4.2-2.2-8.8-3.3-13.5-3.3H100.3
|
||||
c-3,0-5.5,2.4-5.5,5.5v7.9c0,3,2.4,5.5,5.5,5.5h828.9c3,0,5.5-2.4,5.5-5.5L934.7,7L934.7,7z"/>
|
||||
<path id="path74" d="M141.5,32.1V20.8c0-0.3,0.2-0.5,0.5-0.5l0,0l786.4,0.8c0.3,0,0.5,0.2,0.5,0.5v11.3c0,0.3-0.2-0.3-0.5-0.3H142
|
||||
C141.7,32.6,141.5,32.4,141.5,32.1z M142.5,22.2v9.4L928,32.4V22.1l-8.8,1c-256.1-0.6-511.8,4.5-768.3-1.1
|
||||
C148.1,21.9,145.3,22.2,142.5,22.2L142.5,22.2z"/>
|
||||
<path id="path76" d="M0.2,144V37.8c0-3.6,2.9-6.5,6.5-6.5h978c3.6,0,6.5,2.9,6.5,6.5V144c0,116.8-95.1,211.9-211.9,211.9H212.1
|
||||
C95.1,355.9,0.2,261.1,0.2,144L0.2,144L0.2,144z M990.1,37.8c0-3-2.4-5.5-5.5-5.5H6.6c-3,0-5.5,2.4-5.5,5.5V144
|
||||
c0,116.3,94.6,210.9,210.9,210.9h567.3c116.3,0,210.9-94.6,210.9-210.9L990.1,37.8L990.1,37.8z"/>
|
||||
<path id="path128" d="M686.1,11.2V7c0-3.8,3.1-6.9,7-7h67.6c3.8,0,6.9,3.1,7,7v4.2c0,0.6-0.4,1-1,1l0,0h-79.5
|
||||
C686.5,12.2,686.1,11.7,686.1,11.2L686.1,11.2z M765.6,7c0-2.7-2.2-5-5-5H693c-2.7,0-5,2.2-5,5v3.2h77.5L765.6,7L765.6,7z"/>
|
||||
<path id="path130" d="M283.3,11.2V7c0-3.8,3.1-6.9,7-7h67.6c3.8,0,6.9,3.1,7,7v4.2c0,0.6-0.4,1-1,1l0,0h-79.5
|
||||
C283.8,12.2,283.3,11.7,283.3,11.2L283.3,11.2z M362.8,7c0-2.7-2.2-5-5-5h-67.6c-2.7,0-5,2.2-5,5v3.2h77.5L362.8,7L362.8,7z"/>
|
||||
<path id="path1240" class="st2" d="M1.8,162.1c2.4,26.6,9.2,50.5,21.8,76c2.5,5.1,5.2,10,8,14.9c11.3,19.1,27.1,36.3,41.7,49.6
|
||||
c28,25.5,70.1,45.5,116.5,51.4c0.7,0.1,4.3,0.4,6.8,0.4c6.9-0.1,20,0.7,38.3,0.8c77.5,0.7,244,1.1,376.4,1.1
|
||||
c101.2,0,182.5-0.3,188.9-1c26.1-2.7,49.1-9.3,72.1-20.8c63.9-31.8,107.4-93,116.7-164c1.5-11.4,1.6-60.7,1.3-96.6
|
||||
c-0.1-7.7,0.1-14.1-0.1-20.1c-0.2-7.5,0.1-12.9-0.2-16.2c-0.2-1.7-0.8-3-1-3.2c-0.1-0.1-1.4-1.4-3.3-1.5c-5.2-0.3-17.1-0.3-38-0.4
|
||||
c-4.2,0-8.8-0.1-13.8-0.1c-4.9,0-10.1,0.1-15.8,0.1c-8.8,0-18.6,0.1-29.2,0.1c-9.8,0-20.5-0.2-32,0c-9,0.2-18.4,0.2-28.4,0.2
|
||||
c-15.1-0.1-31.1,0.6-49,0.3c-15-0.2-30,0.1-46.9,0c-11.1-0.1-22.6,0.1-34.6,0c-7.2-0.1-14.5,0-22.1-0.1c-52.1-0.5-112.2,0-180.3,0
|
||||
c-254.3,0-376.1-0.3-435.2,0c-7.7,0-14.5-0.2-20.2-0.1c-2.2,0-4.3,0-6.2,0c-3.3,0-6.6,0-9.3,0c-2.9,0-5.8-0.1-8,0
|
||||
c-5,0.1-8.1,0.1-10,0.3c-2.8,0.3-3.6,1.3-3.8,1.6c-0.2,0.2-0.9,1.3-1.1,3.1c-0.4,4.5-0.3,14.6-0.2,27.1c0.1,9.9,0.2,21.3,0.2,32.8
|
||||
C1.7,129.3,1.5,162.4,1.8,162.1L1.8,162.1L1.8,162.1z"/>
|
||||
<path id="path78" d="M237.2,128.3c0-23.1,18.7-41.9,41.9-41.9s41.9,18.7,41.9,41.9s-18.7,41.9-41.9,41.9l0,0
|
||||
C256,170.1,237.3,151.4,237.2,128.3z M320,128.3c0-22.6-18.3-40.9-40.9-40.9s-40.9,18.3-40.9,40.9s18.3,40.9,40.9,40.9l0,0
|
||||
C301.7,169.1,320,150.9,320,128.3z"/>
|
||||
<path id="path92" class="st3" d="M723.9,269.2v-4.7l7-2.2l-0.3-12.4l-6.7-3.2v-4.5l35.2,9.9l-3.2,6.7L723.9,269.2z M735.2,260.5
|
||||
l14.6-4.9L735,251L735.2,260.5L735.2,260.5z"/>
|
||||
<path id="path132" d="M861.1,338.9v-0.2c0.1-0.3,0.3-0.6,0.6-0.7c77.7-33,128.2-109.3,128.1-193.8V90c0-0.6,0.4-1,1-1l0,0h2.6
|
||||
c3.8,0,6.9,3.1,7,7v51.2c-0.1,85.2-50.1,162.4-127.8,197.3c-2.6,1.2-5.7,0.6-7.8-1.5l-3.4-3.4C861.2,339.4,861.1,339.1,861.1,338.9
|
||||
L861.1,338.9z M991.8,91v53.2c0.1,84.7-50.2,161.3-128,195l2.4,2.4l0,0c1.4,1.5,3.6,1.9,5.5,1.1c77-34.6,126.6-111.1,126.7-195.5
|
||||
V96c0-2.7-2.2-5-5-5H991.8z"/>
|
||||
<path id="path134" d="M866.7,89.8V78.1c0-2.2,1.8-4,4-4h11.7c1.1,0,2-0.9,2-2V60.4c0-2.2,1.8-4,4-4h11.7c2.2,0,4,1.8,4,4v11.7
|
||||
c0,1.1,0.9,2,2,2h11.5c2.2,0,4,1.8,4,4v11.7c0,2.2-1.8,4-4,4h-11.7c-1.1,0-2,0.9-2,2v11.7c0,2.2-1.8,4-4,4h-11.7c-2.2,0-4-1.8-4-4
|
||||
V95.8c0-1.1-0.9-2-2-2h-11.7C868.5,93.8,866.7,92,866.7,89.8z M901.9,60.5c0-1.1-0.9-2-2-2h-11.7c-1.1,0-2,0.9-2,2v11.7
|
||||
c0,2.2-1.8,4-4,4h-11.7c-1.1,0-2,0.9-2,2v11.7c0,1.1,0.9,2,2,2h11.7c2.2,0,4,1.8,4,4v11.7c0,1.1,0.9,2,2,2h11.7c1.1,0,2-0.9,2-2
|
||||
V95.9c0-2.2,1.8-4,4-4h11.7c1.1,0,2-0.9,2-2V78.2c0-1.1-0.9-2-2-2h-11.7c-2.2,0-4-1.8-4-4V60.5z"/>
|
||||
<path id="path136" d="M628,182.2c0-21.2,17.2-38.5,38.5-38.5s38.5,17.2,38.5,38.5s-17.2,38.5-38.5,38.5
|
||||
C645.2,220.6,628,203.4,628,182.2z M702.9,182.2c0-20.1-16.3-36.5-36.5-36.5s-36.5,16.3-36.5,36.5s16.3,36.5,36.5,36.5
|
||||
C686.6,218.6,702.9,202.3,702.9,182.2z"/>
|
||||
<path id="path138" d="M774.9,182.2c0-21.2,17.2-38.5,38.5-38.5s38.5,17.2,38.5,38.5s-17.2,38.5-38.5,38.5
|
||||
C792.1,220.6,774.9,203.4,774.9,182.2z M849.8,182.2c0-20.1-16.3-36.5-36.5-36.5c-20.2,0-36.5,16.3-36.5,36.5s16.3,36.5,36.5,36.5
|
||||
C833.5,218.6,849.8,202.3,849.8,182.2z"/>
|
||||
<path id="path140" d="M701.4,255.6c0-21.2,17.2-38.5,38.5-38.5s38.5,17.2,38.5,38.5s-17.2,38.5-38.5,38.5S701.5,276.9,701.4,255.6z
|
||||
M776.4,255.6c0-20.1-16.3-36.5-36.5-36.5s-36.5,16.3-36.5,36.5s16.3,36.5,36.5,36.5S776.4,275.8,776.4,255.6z"/>
|
||||
<path id="path142" d="M701.4,108.8c0-21.2,17.2-38.5,38.5-38.5s38.5,17.2,38.5,38.5s-17.2,38.5-38.5,38.5
|
||||
C718.7,147.2,701.5,130,701.4,108.8z M776.4,108.8c0-20.1-16.3-36.5-36.5-36.5s-36.2,16.2-36.2,36.4s16,36.6,36.2,36.6
|
||||
C760,145.2,776.4,128.9,776.4,108.8z"/>
|
||||
<path id="path144" d="M250.2,128.3c0-16,13-28.9,28.9-28.9s28.9,13,28.9,28.9s-13,28.9-28.9,28.9l0,0
|
||||
C263.1,157.2,250.2,144.3,250.2,128.3z M306.1,128.3c0-14.9-12.1-26.9-26.9-26.9s-26.9,12.1-26.9,26.9s12.1,26.9,26.9,26.9
|
||||
S306,143.2,306.1,128.3z"/>
|
||||
<path id="path146" d="M467,119.1v-12.3c0-0.6,0.4-1,1-1l0,0c39.6,1.7,71.3,33.4,73,73c0,0.3-0.1,0.5-0.3,0.7s-0.4,0.3-0.7,0.3
|
||||
h-12.3c-3.5,0-6.4-2.6-6.9-6c-3.7-24.7-23.1-44.2-47.9-47.9C469.6,125.5,467,122.6,467,119.1z M469,107.9v11.2
|
||||
c0,2.5,1.8,4.6,4.3,4.9c25.6,3.9,45.7,24,49.6,49.6c0.3,2.5,2.4,4.3,4.9,4.3H539C536.9,140.5,506.4,110,469,107.9z"/>
|
||||
<path id="path148" d="M388.3,178.9C388.3,178.9,388.3,178.8,388.3,178.9c1.7-39.6,33.4-71.3,73-73c0.6,0,1,0.4,1,1v12.3
|
||||
c0,3.5-2.6,6.4-6,6.9c-24.7,3.7-44.2,23.1-47.9,47.9c-0.5,3.4-3.4,6-6.9,6h-12.3C388.8,179.9,388.3,179.4,388.3,178.9L388.3,178.9
|
||||
L388.3,178.9z M460.4,107.9c-37.4,2.2-67.8,32.6-70,70h11.2c2.5,0,4.6-1.8,4.9-4.3c3.9-25.6,24-45.7,49.6-49.6
|
||||
c2.5-0.3,4.3-2.4,4.3-4.9V107.9z"/>
|
||||
<path id="path150" d="M388.3,185.5c0-0.6,0.4-1,1-1l0,0h12.3c3.5,0,6.4,2.6,6.9,6c3.7,24.7,23.1,44.2,47.9,47.9
|
||||
c3.4,0.5,6,3.4,6,6.9v12.3c0,0.6-0.4,1-1,1l0,0C421.7,256.8,390.1,225.2,388.3,185.5C388.3,185.6,388.3,185.6,388.3,185.5
|
||||
L388.3,185.5L388.3,185.5z M460.4,245.3c0-2.5-1.8-4.6-4.3-4.9c-25.6-3.9-45.7-24-49.6-49.6c-0.3-2.5-2.4-4.3-4.9-4.3h-11.2
|
||||
c2.2,37.4,32.6,67.8,70,70V245.3z"/>
|
||||
<path id="path152" d="M467,257.6v-12.3c0-3.5,2.6-6.4,6-6.9c24.7-3.7,44.2-23.1,47.9-47.9c0.5-3.4,3.4-6,6.9-6h12.3
|
||||
c0.3,0,0.5,0.1,0.7,0.3s0.3,0.5,0.3,0.7c-1.7,39.6-33.4,71.3-73,73C467.5,258.6,467.1,258.2,467,257.6L467,257.6L467,257.6z
|
||||
M539,186.5h-11.2c-2.5,0-4.6,1.8-4.9,4.3c-3.9,25.6-24,45.7-49.6,49.6c-2.5,0.3-4.3,2.4-4.3,4.9v11.2
|
||||
C506.4,254.4,536.9,223.9,539,186.5z"/>
|
||||
<path id="path154" d="M388.2,182.2c0-1.2,0-2.3,0.1-3.4c0-0.5,0.5-0.9,1-0.9h12.3c2.5,0,4.6-1.8,4.9-4.3
|
||||
c3.9-25.6,24-45.7,49.6-49.6c2.5-0.3,4.3-2.4,4.3-4.9v-12.3c0-0.5,0.4-1,0.9-1c1.1-0.1,2.2-0.1,3.4-0.1s2.3,0,3.4,0.1
|
||||
c0.5,0,0.9,0.5,0.9,1v12.3c0,2.5,1.8,4.6,4.3,4.9c25.6,3.9,45.7,24,49.6,49.6c0.3,2.5,2.4,4.3,4.9,4.3h12.3c0.5,0,1,0.4,1,0.9
|
||||
c0.1,2.3,0.1,4.5,0,6.8c0,0.5-0.5,0.9-1,0.9h-12.3c-2.5,0-4.6,1.8-4.9,4.3c-3.9,25.6-24,45.7-49.6,49.6c-2.5,0.3-4.3,2.4-4.3,4.9
|
||||
v12.3c0,0.5-0.4,1-0.9,1c-1.1,0.1-2.2,0.1-3.4,0.1s-2.3,0-3.4-0.1c-0.5,0-0.9-0.5-0.9-1v-12.3c0-2.5-1.8-4.6-4.3-4.9
|
||||
c-25.6-3.9-45.7-24-49.6-49.6c-0.3-2.5-2.4-4.3-4.9-4.3h-12.3c-0.5,0-1-0.4-1-0.9C388.3,184.5,388.2,183.4,388.2,182.2L388.2,182.2
|
||||
L388.2,182.2z M390.3,179.9c-0.1,1.5-0.1,3.1,0,4.7h11.3c3.5,0,6.4,2.6,6.9,6c3.7,24.7,23.1,44.2,47.9,47.9c3.4,0.5,6,3.4,6,6.9
|
||||
v11.3h4.6v-11.4c0-3.5,2.6-6.4,6-6.9c24.7-3.7,44.2-23.1,47.9-47.9c0.5-3.4,3.4-6,6.9-6h11.3c0.1-1.5,0.1-3.1,0-4.7h-11.3
|
||||
c-3.5,0-6.4-2.6-6.9-6c-3.7-24.7-23.1-44.2-47.9-47.9c-3.4-0.5-6-3.4-6-6.9v-11.3h-4.6V119c0,3.5-2.6,6.4-6,6.9
|
||||
c-24.7,3.7-44.2,23.1-47.9,47.9c-0.5,3.4-3.4,6-6.9,6h-11.3V179.9L390.3,179.9z"/>
|
||||
<path id="path1462" class="st4" d="M705,99.6c1.7-6.3,3.9-11.3,9.1-16.5c7.7-7.7,17-11.3,27.7-10.8c13.5,0.6,25,8,31,20
|
||||
c5.2,10.5,5.3,22.5,0.3,32.7c-4,7.9-10,12.8-18,16.6c-10.8,5.2-24.4,4.2-34.6-2.4c-7.7-5-13.7-13.5-15.7-22
|
||||
C703.6,112.3,703.7,104.4,705,99.6L705,99.6L705,99.6z"/>
|
||||
<path id="path1464" class="st4" d="M777.6,176c0.3,0,0.9-3.7,1.6-5.7c1.1-3,2.6-5.9,4-8c4.6-6.9,10.9-11.7,18.9-14.5
|
||||
c3.8-1.3,5.2-2.1,11.5-2.1s7.2,0.7,11,2.1c11.1,3.9,19.1,11.8,23,22.8c1.5,4.2,2.3,5.2,2.3,11.6s-0.8,7.3-2.3,11.6
|
||||
c-5.2,14.5-17.6,24-32.9,24.7c-10.6,0.5-19.5-2.7-26.9-10.2c-7.5-7.6-10.7-14.4-11-24.6C776.7,180.2,777.4,176.7,777.6,176
|
||||
L777.6,176L777.6,176z"/>
|
||||
<path id="path1466" class="st4" d="M704,248.2c1-6.7,5.8-15.6,12.9-20.8c4.1-3,10.7-6.2,15.7-7.3c13.2-2.8,27.9,2.6,35.9,13.3
|
||||
c15.2,20,6.7,48.1-17,56.4c-5.3,1.9-13.3,3-19.2,1.7c-6.8-1.5-12.9-4.9-18.2-10.2c-5.2-5.3-8-9.9-9.6-16.4
|
||||
C703.3,260.1,703.3,252.9,704,248.2L704,248.2L704,248.2z"/>
|
||||
<path id="path1468" class="st4" d="M630.6,177c1.3-8.1,4-14.2,10.3-20.6c3.7-3.8,6.2-5.4,10-7.2c15.8-7.6,33.1-3,44.1,10.8
|
||||
c5.1,6.4,7.6,13.6,7.6,22.1c0,29.9-33.8,46.7-58,28.7c-3.8-2.8-7.5-6.3-9.8-10.5C630.7,193.3,629.4,184.7,630.6,177L630.6,177
|
||||
L630.6,177z"/>
|
||||
<path id="path1500" class="st5" d="M397.8,180.2c8.9-0.3,9.7-0.8,11.9-10.1c4.8-21.1,20.1-37.2,41.3-42.6c2.2-0.5,5.2-1.1,5.9-1.1
|
||||
s2.2-1,3.5-2.2l2-2.3l0.3-6.9v-7h4v5.9c0,6.6,1,9.9,3.4,11.2c0.8,0.4,3.9,1.5,7,2c16.3,2.7,30.7,14.9,38,29.5
|
||||
c2.4,4.7,4.6,11.4,5.4,15.5c1.1,6.6,3.6,8.2,12.8,8.2h5.9v4H532c-6.6,0-7,0.1-8.8,1.8c-1.6,1.5-1.7,3.9-3.1,9.5
|
||||
c-5,20.4-20.5,35.3-40.2,40.7c-2.9,0.8-6.4,1.8-7.8,2.2c-4.3,1.2-5.6,3.8-5.6,11.7v6.5h-4V250c0-7-0.8-9.9-3.1-10.8
|
||||
c-0.7-0.3-4.6-1.3-8-2.1c-21.4-5.4-36.7-21.3-41.5-42.1c-2.3-10-3.2-10.7-12.8-10.7h-6.3v-3.8L397.8,180.2L397.8,180.2z"/>
|
||||
<path id="path1504" class="st6" d="M473.9,108.8c15.3,1.8,31.2,8.9,43,20.4c12.2,12,19.2,27.5,21.6,45.7l0.4,2.8h-6.2
|
||||
c-7.9,0-8.9-0.1-10.2-6.8c-4.6-22.3-22.1-41.8-44.2-46.1c-9.1-1.8-9.1-2.4-9.1-11.1v-5.4L473.9,108.8L473.9,108.8z"/>
|
||||
<path id="path1506" class="st6" d="M392.2,167.8c6.9-32.2,32.9-55.7,65.6-59.4l2.1-0.2v6.4c0,6.9,0,6.8-1,7.8
|
||||
c-1.4,1.3-3.8,1.5-7.9,2.4c-21.2,4.8-39.1,24.2-44,46.6c-1.5,6.9-2.7,6.4-10.3,6.4h-5.9l0-2.1C391,174.4,391.5,170.9,392.2,167.8
|
||||
L392.2,167.8L392.2,167.8z"/>
|
||||
<path id="path1508" class="st6" d="M397.4,186.6c7.3-0.3,8.2,1,9,5.3c0.8,4.8,2.9,11.1,5.5,16.3c7.7,15.8,22,27.1,39.9,31.6
|
||||
c4,1,7.2,1.6,7.7,2.2c0.5,0.7,0.5,3.9,0.5,7.8v6.2l-3.8-0.4c-33.2-3.8-61.3-31.8-65-64.8l-0.5-4L397.4,186.6L397.4,186.6z"/>
|
||||
<path id="path1510" class="st6" d="M470.6,242c1.1-1.2,3-1.3,6.7-2.1c20.5-4.4,36.6-19,43.5-39.3c0.8-2.4,1.7-5.8,2-7.5
|
||||
c0.3-1.9,0.6-3.9,1.5-4.8c1.4-1.4,2.6-1.3,8.2-1.3h6.2l-0.3,2.5c-2.5,19-9.2,32.7-22.1,45.4c-10.6,10.4-23.8,17.4-38.2,20.2
|
||||
c-2.9,0.5-6.1,1-7.1,1h-1.9v-5.9C469.2,244.9,469.3,243.4,470.6,242L470.6,242L470.6,242z"/>
|
||||
<path id="path1512" class="st4" d="M869.4,76.9c0.6-0.5,1.7-0.6,7.5-0.6h6.9l1.2-1.1l1.2-1.1l0.1-7.1c0.1-4.1-0.5-6.5,0.2-7.6
|
||||
c0.8-1.3,3.1-0.9,7.6-0.9s6.3,0.1,6.8,0.5c0.7,0.5,0.7,1.1,0.8,7.3c0,3.7,0.2,7.1,0.3,7.6c0.1,0.5,0.7,1.2,1.4,1.6
|
||||
c1.1,0.7,1.9,0.7,7.8,0.7c3.8,0,6.9,0.2,7.3,0.4c0.7,0.4,0.7,0.9,0.7,7.3s0,7-0.7,7.3c-0.4,0.2-3.5,0.4-7.3,0.4
|
||||
c-5.8,0-6.7,0.1-7.8,0.7c-0.7,0.4-1.3,1.1-1.4,1.6s-0.2,3.9-0.3,7.6c-0.1,6.2-0.2,6.8-0.8,7.3c-0.6,0.4-2.2,0.5-6.8,0.5
|
||||
c-4.7,0-7.2,0.7-7.9-0.9c-0.5-1.2,0.1-3.8,0.1-7.5l-0.1-7.1l-1.2-1.1l-1.2-1.1h-6.9c-6.2,0-7-0.1-7.5-0.7c-0.5-0.6-0.6-1.7-0.6-7.1
|
||||
C868.7,78.2,868.8,77.5,869.4,76.9L869.4,76.9L869.4,76.9z"/>
|
||||
<path id="path1514" class="st6" d="M274,123.2h5.3v10.5h-10.5v-10.5H274L274,123.2z"/>
|
||||
<path id="path1518" class="st6" d="M238.8,122.6c2.3-14.8,12.3-27.2,26.2-32.4c20-7.5,43,2.3,51.4,22c2.6,6.1,3.5,8,3.5,15.7
|
||||
c0,6.5-0.6,7.9-1.6,11.3c-4.4,14.4-14.9,24.6-29.4,28.6c-2.9,0.8-5.2,1.1-10.7,1.1c-5.5,0-6.2-0.3-9-1.1
|
||||
c-17.7-4.8-29.9-20-30.5-38.1C238.5,127.1,238.6,124,238.8,122.6L238.8,122.6L238.8,122.6z M250.8,134.7
|
||||
c2.6,11.9,13,21.3,25.1,22.5c21.5,2.2,37.7-18.1,30.7-38.5c-1.4-4.2-3.3-7-7.1-10.8c-2.8-2.8-4.1-3.8-7.2-5.3
|
||||
c-5.1-2.6-8.7-3.3-14.6-3c-6.8,0.3-11.7,2.2-16.9,6.3C252.4,112.5,248.4,124,250.8,134.7L250.8,134.7L250.8,134.7z"/>
|
||||
<path id="path1520" class="st7" d="M286.1,4.8c0.8-1.3,0.9-2.3,5.3-2.5c4.3-0.2,13.5,0.1,32.6,0.1c32.8,0,35.2-0.5,36.3,0.6
|
||||
c0.1,0.1,0.2,0.1,0.2,0.2c1.5,1.1,2.1,2.7,2.1,4.9v1.9h-77.3v-2C285.4,6.6,285.7,5.5,286.1,4.8L286.1,4.8L286.1,4.8z"/>
|
||||
<path id="path1522" class="st7" d="M688.3,6.2c0.1-0.5,0.4-1.3,0.8-1.8c0.5-0.8,0.8-1.5,2.7-1.8c3.2-0.5,11.4-0.2,35.1-0.2
|
||||
c19.3,0,28.3-0.5,32.5-0.4c2.7,0.1,3.2,0.8,3.6,1c1.6,0.9,2.3,2.4,2.3,4.8v2.1h-77.3l0-1.4C688.1,7.7,688.2,6.7,688.3,6.2
|
||||
L688.3,6.2L688.3,6.2z"/>
|
||||
<path id="path1524" class="st8" d="M874,334.6c20.6-10.1,37.2-21.9,53.9-38.2c35.7-34.8,57-78.7,63.1-129.7
|
||||
c0.6-5.1,0.8-13.1,1-40.9l0.2-34.6h1.4c1,0,3,0,3.7,1.4l1.1,2.1l-0.2,30.4c-0.1,16.9-0.2,33.5-0.5,37
|
||||
c-3.9,50.8-24.6,97.1-59.5,133.3c-16.9,17.6-35.7,31.5-57.9,42.8c-8.4,4.3-10.1,4.9-11.8,4.2c-1.1-0.4-4.2-2.7-4.1-3.2
|
||||
C864.4,339.2,869,337.1,874,334.6L874,334.6L874,334.6z"/>
|
||||
<path id="path1530" class="st4" d="M96.7,3.4c0.5-0.6,1.2-1.7,5-1.6c6.7,0.3,27.2-0.2,79.1-0.1L263.4,2l6.5,2.6l15,7.9l39.4,0.1
|
||||
l39.6-0.4l13.2-7.1l7.7-2.9l7.3-0.4l48,0.1l22.1-0.1l34.1,0.1l10.5,0.1h10.5l8.6-0.1l25.6,0h19.4l27.5-0.2L620,1.8l11,0l19.6,0.1
|
||||
l10.2,0l5.7,0.5l5.8,1.9l5.5,3l9.8,5.3h39.5l39.6-0.4l9.8-5.3l5.3-2.7l5.1-1.8l7.6-0.7l13.3,0.2l12.2-0.1h11.1l10.9,0l9.9,0h6.5
|
||||
l2.9,0l17.4,0.1l21.5,0h10.8l8.4-0.1l5.1,0l5.9-0.2l3.1,2.2l1.1,3.2l-0.1,3.2c0,1.4-0.1,2.7-0.2,3.9c-0.1,0.9-0.1,1.8-0.4,2.6
|
||||
c-0.4,1-1,1.7-1.3,2c-0.2,0.2-1.7,1.2-4.8,1.3c-2.1,0.1-4.7,0-8.3,0c-4.1,0-9.7,0.1-16.4,0c-10-0.1-23.1,0-40.4-0.1
|
||||
c-13.2,0-28.7,0.2-47,0.1c-62.8-0.2-158-0.2-301-0.2L98.7,20l-1.8-1.3C94.3,16.9,94,6.1,96.7,3.4L96.7,3.4L96.7,3.4z"/>
|
||||
<path id="path1532" class="st4" d="M535.4,22.4h392.4V31H143v-8.6H535.4L535.4,22.4z"/>
|
||||
<ellipse id="path3879" class="st9" cx="279.4" cy="128.2" rx="40.3" ry="40.3"/>
|
||||
<ellipse id="path3881" class="st10" cx="279.1" cy="128.2" rx="34.5" ry="34.1"/>
|
||||
<path id="path96" d="M306.1,128.3l-26.4-26.8v7.7h-22.5v38.5h22.5v7.2L306.1,128.3L306.1,128.3z M265.2,136.1v-15.2h14.5v15.2
|
||||
H265.2z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 17 KiB |
84
src/Ryujinx.Ava/Assets/Icons/Controller_ProCon.svg
Normal file
84
src/Ryujinx.Ava/Assets/Icons/Controller_ProCon.svg
Normal file
@ -0,0 +1,84 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 996.25 690.92">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
stroke: #fff;
|
||||
stroke-miterlimit: 10;
|
||||
stroke-width: 2px;
|
||||
}
|
||||
|
||||
.cls-1, .cls-2 {
|
||||
fill: #444542;
|
||||
}
|
||||
|
||||
.cls-3 {
|
||||
fill: #3b3b3b;
|
||||
}
|
||||
|
||||
.cls-3, .cls-4, .cls-2, .cls-5, .cls-6, .cls-7 {
|
||||
stroke-width: 0px;
|
||||
}
|
||||
|
||||
.cls-4 {
|
||||
fill: #3b3c3a;
|
||||
}
|
||||
|
||||
.cls-5 {
|
||||
fill: #454644;
|
||||
}
|
||||
|
||||
.cls-6 {
|
||||
fill: #20221f;
|
||||
}
|
||||
|
||||
.cls-7 {
|
||||
fill: #121212;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="Front">
|
||||
<path id="Right_Grip" data-name="Right Grip" class="cls-6" d="m739.17,492.09c34,28.2,27.6,35.9,68.5,108.5,36.7,74.7,64.4,104.4,125.1,84.1h0c95.3-57.9,59.3-145.3,43.6-275.2-10-60.6-35.6-190.3-35.6-190.3l-201.6,272.9Z"/>
|
||||
<path id="Left_Grip" data-name="Left Grip" class="cls-6" d="m55.47,219.19s-25.6,129.7-35.6,190.3c-15.7,129.9-51.7,217.2,43.6,275.1h0c60.8,20.3,88.4-9.4,125.1-84.1,40.9-72.7,34.5-80.3,68.5-108.5L55.47,219.19Z"/>
|
||||
<path id="Right_Bumper" data-name="Right Bumper" class="cls-3" d="m649.47,19.99c10.1-4.3,39.7-22.5,58.7-19.7,59.5.9,166.7,17.7,172.6,81.2"/>
|
||||
<path id="Left_Bumper" data-name="Left Bumper" class="cls-3" d="m115.57,81.49C121.47,18.09,228.57,1.29,288.17.29c19-2.8,48.6,15.4,58.7,19.7"/>
|
||||
<path id="Background" class="cls-7" d="m739.17,492.09c35.5-30.8,68.5-74.7,96-113.5,26.9-36.3,94.7-136.7,105.6-159.3,0-2.4-6.3-30.1-12.8-56.2C892.27,3.49,675.37,19.69,498.17,19.69S104.07,3.49,68.27,162.99c-6.5,26-12.8,53.8-12.8,56.2,10.9,22.6,78.8,123,105.6,159.3,27.5,38.8,60.5,82.8,96,113.5"/>
|
||||
<g id="Directional_Pad" data-name="Directional Pad">
|
||||
<path id="Background-2" data-name="Background" class="cls-2" d="m439.37,325.09h-40c-2.8,0-5-2.2-5-5v-40c0-2.8-2.2-5-5-5h-30c-2.8,0-5,2.2-5,5v40c0,2.8-2.2,5-5,5h-40c-2.8,0-5,2.2-5,5v30c0,2.8,2.2,5,5,5h40c2.8,0,5,2.2,5,5v40c0,2.8,2.2,5,5,5h30c2.8,0,5-2.2,5-5v-40c0-2.8,2.2-5,5-5h40c2.8,0,5-2.2,5-5v-30c0-2.7-2.2-5-5-5Z"/>
|
||||
</g>
|
||||
<g id="R_Thumbstick" data-name="R Thumbstick">
|
||||
<circle id="Background-3" data-name="Background" class="cls-6" cx="623.77" cy="345.09" r="55"/>
|
||||
<circle id="Stick" class="cls-1" cx="623.77" cy="345.09" r="45"/>
|
||||
</g>
|
||||
<g id="L_Thumbstick" data-name="L Thumbstick">
|
||||
<circle id="Background-4" data-name="Background" class="cls-6" cx="213.37" cy="206.39" r="55"/>
|
||||
<circle id="Stick-2" data-name="Stick" class="cls-1" cx="213.37" cy="206.39" r="45" transform="translate(-24.53 383.95) rotate(-80.78)"/>
|
||||
</g>
|
||||
<g id="Minus_Button" data-name="Minus Button">
|
||||
<circle id="_Background" data-name=" Background" class="cls-5" cx="374.17" cy="130.89" r="22.5"/>
|
||||
</g>
|
||||
<g id="Plus_Button" data-name="Plus Button">
|
||||
<circle id="_Background-2" data-name=" Background" class="cls-5" cx="623.57" cy="131.19" r="22.5"/>
|
||||
</g>
|
||||
<g id="Home_Button" data-name="Home Button">
|
||||
<circle id="_Background-3" data-name=" Background" class="cls-5" cx="578.57" cy="206.39" r="22.5"/>
|
||||
</g>
|
||||
<g id="Capture_Button" data-name="Capture Button">
|
||||
<path class="cls-5" d="m441.77,228.09h-30c-2.8,0-5-2.2-5-5v-29.5c0-2.8,2.2-5,5-5h30c2.8,0,5,2.2,5,5v29.5c0,2.7-2.2,5-5,5Z"/>
|
||||
</g>
|
||||
<g id="Buttons">
|
||||
<g id="A_Button" data-name="A Button">
|
||||
<circle id="Background-5" data-name="Background" class="cls-4" cx="837.07" cy="206.39" r="35"/>
|
||||
</g>
|
||||
<g id="X_Button" data-name="X Button">
|
||||
<circle id="Background-6" data-name="Background" class="cls-4" cx="767.07" cy="136.39" r="35"/>
|
||||
</g>
|
||||
<g id="Y_Button" data-name="Y Button">
|
||||
<circle id="Background-7" data-name="Background" class="cls-4" cx="697.07" cy="206.39" r="35"/>
|
||||
</g>
|
||||
<g id="B_Button" data-name="B Button">
|
||||
<circle id="Background-8" data-name="Background" class="cls-4" cx="767.07" cy="276.39" r="35"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.9 KiB |
@ -549,9 +549,10 @@
|
||||
"SoftwareKeyboardModeNumeric": "Must be 0-9 or '.' only",
|
||||
"SoftwareKeyboardModeAlphabet": "Must be non CJK-characters only",
|
||||
"SoftwareKeyboardModeASCII": "Must be ASCII text only",
|
||||
"DialogControllerAppletMessagePlayerRange": "Application requests {0} player(s) with:\n\nTYPES: {1}\n\nPLAYERS: {2}\n\n{3}Please open Settings and reconfigure Input now or press Close.",
|
||||
"DialogControllerAppletMessage": "Application requests exactly {0} player(s) with:\n\nTYPES: {1}\n\nPLAYERS: {2}\n\n{3}Please open Settings and reconfigure Input now or press Close.",
|
||||
"DialogControllerAppletDockModeSet": "Docked mode set. Handheld is also invalid.\n\n",
|
||||
"ControllerAppletControllers": "Supported Controllers:",
|
||||
"ControllerAppletPlayers": "Players:",
|
||||
"ControllerAppletDescription": "Your current configuration is invalid. Open settings and reconfigure your inputs.",
|
||||
"ControllerAppletDocked": "Docked mode set. Handheld control should be disabled.",
|
||||
"UpdaterRenaming": "Renaming Old Files...",
|
||||
"UpdaterRenameFailed": "Updater was unable to rename file: {0}",
|
||||
"UpdaterAddingFiles": "Adding New Files...",
|
||||
|
@ -16,8 +16,10 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
private readonly Dictionary<LocaleKeys, string> _localeStrings;
|
||||
private Dictionary<LocaleKeys, string> _localeDefaultStrings;
|
||||
private readonly ConcurrentDictionary<LocaleKeys, object[]> _dynamicValues;
|
||||
private string _localeLanguageCode;
|
||||
|
||||
public static LocaleManager Instance { get; } = new();
|
||||
public event Action LocaleChanged;
|
||||
|
||||
public LocaleManager()
|
||||
{
|
||||
@ -104,6 +106,15 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsRTL()
|
||||
{
|
||||
return _localeLanguageCode switch
|
||||
{
|
||||
"he_IL" => true,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
public string UpdateAndGetDynamicValue(LocaleKeys key, params object[] values)
|
||||
{
|
||||
_dynamicValues[key] = values;
|
||||
@ -124,6 +135,9 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
{
|
||||
this[item.Key] = item.Value;
|
||||
}
|
||||
|
||||
_localeLanguageCode = languageCode;
|
||||
LocaleChanged?.Invoke();
|
||||
}
|
||||
|
||||
private static Dictionary<LocaleKeys, string> LoadJsonLanguage(string languageCode = DefaultLanguageCode)
|
||||
|
@ -119,6 +119,7 @@
|
||||
<None Remove="Assets\Locales\en_US.json" />
|
||||
<None Remove="Assets\Locales\es_ES.json" />
|
||||
<None Remove="Assets\Locales\fr_FR.json" />
|
||||
<None Remove="Assets\Locales\he_IL.json" />
|
||||
<None Remove="Assets\Locales\de_DE.json" />
|
||||
<None Remove="Assets\Locales\it_IT.json" />
|
||||
<None Remove="Assets\Locales\ja_JP.json" />
|
||||
@ -132,6 +133,10 @@
|
||||
<None Remove="Assets\Locales\zh_TW.json" />
|
||||
<None Remove="Assets\Styles\Styles.xaml" />
|
||||
<None Remove="Assets\Styles\Themes.xaml" />
|
||||
<None Remove="Assets\Icons\Controller_JoyConLeft.svg" />
|
||||
<None Remove="Assets\Icons\Controller_JoyConPair.svg" />
|
||||
<None Remove="Assets\Icons\Controller_JoyConRight.svg" />
|
||||
<None Remove="Assets\Icons\Controller_ProCon.svg" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -139,6 +144,7 @@
|
||||
<EmbeddedResource Include="Assets\Locales\en_US.json" />
|
||||
<EmbeddedResource Include="Assets\Locales\es_ES.json" />
|
||||
<EmbeddedResource Include="Assets\Locales\fr_FR.json" />
|
||||
<EmbeddedResource Include="Assets\Locales\he_IL.json" />
|
||||
<EmbeddedResource Include="Assets\Locales\de_DE.json" />
|
||||
<EmbeddedResource Include="Assets\Locales\it_IT.json" />
|
||||
<EmbeddedResource Include="Assets\Locales\ja_JP.json" />
|
||||
@ -151,6 +157,10 @@
|
||||
<EmbeddedResource Include="Assets\Locales\zh_CN.json" />
|
||||
<EmbeddedResource Include="Assets\Locales\zh_TW.json" />
|
||||
<EmbeddedResource Include="Assets\Styles\Styles.xaml" />
|
||||
<EmbeddedResource Include="Assets\Icons\Controller_JoyConLeft.svg" />
|
||||
<EmbeddedResource Include="Assets\Icons\Controller_JoyConPair.svg" />
|
||||
<EmbeddedResource Include="Assets\Icons\Controller_JoyConRight.svg" />
|
||||
<EmbeddedResource Include="Assets\Icons\Controller_ProCon.svg" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="Assets\Locales\en_US.json" />
|
||||
|
@ -29,14 +29,24 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
|
||||
public bool DisplayMessageDialog(ControllerAppletUiArgs args)
|
||||
{
|
||||
string message = LocaleManager.Instance.UpdateAndGetDynamicValue(
|
||||
args.PlayerCountMin == args.PlayerCountMax ? LocaleKeys.DialogControllerAppletMessage : LocaleKeys.DialogControllerAppletMessagePlayerRange,
|
||||
args.PlayerCountMin == args.PlayerCountMax ? args.PlayerCountMin.ToString() : $"{args.PlayerCountMin}-{args.PlayerCountMax}",
|
||||
args.SupportedStyles,
|
||||
string.Join(", ", args.SupportedPlayers),
|
||||
args.IsDocked ? LocaleManager.Instance[LocaleKeys.DialogControllerAppletDockModeSet] : "");
|
||||
ManualResetEvent dialogCloseEvent = new(false);
|
||||
|
||||
return DisplayMessageDialog(LocaleManager.Instance[LocaleKeys.DialogControllerAppletTitle], message);
|
||||
bool okPressed = false;
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
var response = await ControllerAppletDialog.ShowControllerAppletDialog(_parent, args);
|
||||
if (response == UserResult.Ok)
|
||||
{
|
||||
okPressed = true;
|
||||
}
|
||||
|
||||
dialogCloseEvent.Set();
|
||||
});
|
||||
|
||||
dialogCloseEvent.WaitOne();
|
||||
|
||||
return okPressed;
|
||||
}
|
||||
|
||||
public bool DisplayMessageDialog(string title, string message)
|
||||
@ -75,6 +85,8 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
|
||||
await _parent.SettingsWindow.ShowDialog(window);
|
||||
|
||||
_parent.SettingsWindow = null;
|
||||
|
||||
opened = false;
|
||||
});
|
||||
|
||||
|
145
src/Ryujinx.Ava/UI/Applet/ControllerAppletDialog.axaml
Normal file
145
src/Ryujinx.Ava/UI/Applet/ControllerAppletDialog.axaml
Normal file
@ -0,0 +1,145 @@
|
||||
<UserControl
|
||||
x:Class="Ryujinx.Ava.UI.Applet.ControllerAppletDialog"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
xmlns:applet="using:Ryujinx.Ava.UI.Applet"
|
||||
mc:Ignorable="d"
|
||||
Width="400"
|
||||
Focusable="True"
|
||||
x:DataType="applet:ControllerAppletDialog">
|
||||
<Grid
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border
|
||||
Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="0 0 0 10"
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="5">
|
||||
<StackPanel
|
||||
Spacing="10"
|
||||
Margin="10">
|
||||
<TextBlock
|
||||
Text="{locale:Locale ControllerAppletDescription}" />
|
||||
<TextBlock
|
||||
IsVisible="{Binding IsDocked}"
|
||||
FontWeight="Bold"
|
||||
Text="{locale:Locale ControllerAppletDocked}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<Border
|
||||
Grid.Column="0"
|
||||
Grid.Row="1"
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="5"
|
||||
Margin="0 0 10 0">
|
||||
<StackPanel
|
||||
Margin="10"
|
||||
Spacing="10"
|
||||
Orientation="Vertical">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
TextAlignment="Center"
|
||||
FontWeight="Bold"
|
||||
Text="{locale:Locale ControllerAppletControllers}" />
|
||||
<StackPanel
|
||||
Spacing="10"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<Image
|
||||
Height="50"
|
||||
Width="50"
|
||||
Stretch="Uniform"
|
||||
Source="{Binding ProControllerImage}"
|
||||
IsVisible="{Binding SupportsProController}" />
|
||||
<Image
|
||||
Height="50"
|
||||
Width="50"
|
||||
Stretch="Uniform"
|
||||
Source="{Binding JoyconPairImage}"
|
||||
IsVisible="{Binding SupportsJoyconPair}" />
|
||||
<Image
|
||||
Height="50"
|
||||
Width="50"
|
||||
Stretch="Uniform"
|
||||
Source="{Binding JoyconLeftImage}"
|
||||
IsVisible="{Binding SupportsLeftJoycon}" />
|
||||
<Image
|
||||
Height="50"
|
||||
Width="50"
|
||||
Stretch="Uniform"
|
||||
Source="{Binding JoyconRightImage}"
|
||||
IsVisible="{Binding SupportsRightJoycon}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<Border
|
||||
Grid.Column="1"
|
||||
Grid.Row="1"
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="5">
|
||||
<StackPanel
|
||||
Margin="10"
|
||||
Spacing="10"
|
||||
Orientation="Vertical">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
TextAlignment="Center"
|
||||
FontWeight="Bold"
|
||||
Text="{locale:Locale ControllerAppletPlayers}" />
|
||||
<Border Height="50">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
TextAlignment="Center"
|
||||
FontSize="40"
|
||||
FontWeight="Thin"
|
||||
Text="{Binding PlayerCount}" />
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<Panel
|
||||
Margin="0 24 0 0"
|
||||
Grid.Column="0"
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="2">
|
||||
<StackPanel
|
||||
Orientation="Horizontal"
|
||||
Spacing="10"
|
||||
HorizontalAlignment="Right">
|
||||
<Button
|
||||
Name="SaveButton"
|
||||
MinWidth="90"
|
||||
Command="{Binding OpenSettingsWindow}">
|
||||
<TextBlock Text="{locale:Locale DialogOpenSettingsWindowLabel}" />
|
||||
</Button>
|
||||
<Button
|
||||
Name="CancelButton"
|
||||
MinWidth="90"
|
||||
Command="{Binding Close}">
|
||||
<TextBlock Text="{locale:Locale SettingsButtonClose}" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Panel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
139
src/Ryujinx.Ava/UI/Applet/ControllerAppletDialog.axaml.cs
Normal file
139
src/Ryujinx.Ava/UI/Applet/ControllerAppletDialog.axaml.cs
Normal file
@ -0,0 +1,139 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Styling;
|
||||
using Avalonia.Svg.Skia;
|
||||
using Avalonia.Threading;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.HLE.HOS.Applets;
|
||||
using Ryujinx.HLE.HOS.Services.Hid;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Applet
|
||||
{
|
||||
internal partial class ControllerAppletDialog : UserControl
|
||||
{
|
||||
private const string ProControllerResource = "Ryujinx.Ava/Assets/Icons/Controller_ProCon.svg";
|
||||
private const string JoyConPairResource = "Ryujinx.Ava/Assets/Icons/Controller_JoyConPair.svg";
|
||||
private const string JoyConLeftResource = "Ryujinx.Ava/Assets/Icons/Controller_JoyConLeft.svg";
|
||||
private const string JoyConRightResource = "Ryujinx.Ava/Assets/Icons/Controller_JoyConRight.svg";
|
||||
|
||||
public static SvgImage ProControllerImage => GetResource(ProControllerResource);
|
||||
public static SvgImage JoyconPairImage => GetResource(JoyConPairResource);
|
||||
public static SvgImage JoyconLeftImage => GetResource(JoyConLeftResource);
|
||||
public static SvgImage JoyconRightImage => GetResource(JoyConRightResource);
|
||||
|
||||
public string PlayerCount { get; set; } = "";
|
||||
public bool SupportsProController { get; set; }
|
||||
public bool SupportsLeftJoycon { get; set; }
|
||||
public bool SupportsRightJoycon { get; set; }
|
||||
public bool SupportsJoyconPair { get; set; }
|
||||
public bool IsDocked { get; set; }
|
||||
|
||||
private readonly MainWindow _mainWindow;
|
||||
|
||||
public ControllerAppletDialog(MainWindow mainWindow, ControllerAppletUiArgs args)
|
||||
{
|
||||
if (args.PlayerCountMin == args.PlayerCountMax)
|
||||
{
|
||||
PlayerCount = args.PlayerCountMin.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayerCount = $"{args.PlayerCountMin} - {args.PlayerCountMax}";
|
||||
}
|
||||
|
||||
SupportsProController = (args.SupportedStyles & ControllerType.ProController) != 0;
|
||||
SupportsLeftJoycon = (args.SupportedStyles & ControllerType.JoyconLeft) != 0;
|
||||
SupportsRightJoycon = (args.SupportedStyles & ControllerType.JoyconRight) != 0;
|
||||
SupportsJoyconPair = (args.SupportedStyles & ControllerType.JoyconPair) != 0;
|
||||
|
||||
IsDocked = args.IsDocked;
|
||||
|
||||
_mainWindow = mainWindow;
|
||||
|
||||
DataContext = this;
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public ControllerAppletDialog(MainWindow mainWindow)
|
||||
{
|
||||
_mainWindow = mainWindow;
|
||||
DataContext = this;
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public static async Task<UserResult> ShowControllerAppletDialog(MainWindow window, ControllerAppletUiArgs args)
|
||||
{
|
||||
ContentDialog contentDialog = new();
|
||||
UserResult result = UserResult.Cancel;
|
||||
ControllerAppletDialog content = new(window, args);
|
||||
|
||||
contentDialog.Title = LocaleManager.Instance[LocaleKeys.DialogControllerAppletTitle];
|
||||
contentDialog.Content = content;
|
||||
|
||||
void Handler(ContentDialog sender, ContentDialogClosedEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.Result == ContentDialogResult.Primary)
|
||||
{
|
||||
result = UserResult.Ok;
|
||||
}
|
||||
}
|
||||
|
||||
contentDialog.Closed += Handler;
|
||||
|
||||
Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>());
|
||||
bottomBorder.Setters.Add(new Setter(IsVisibleProperty, false));
|
||||
|
||||
contentDialog.Styles.Add(bottomBorder);
|
||||
|
||||
await ContentDialogHelper.ShowAsync(contentDialog);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static SvgImage GetResource(string path)
|
||||
{
|
||||
SvgImage image = new();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
SvgSource source = new();
|
||||
|
||||
source.Load(EmbeddedResources.GetStream(path));
|
||||
|
||||
image.Source = source;
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
public void OpenSettingsWindow()
|
||||
{
|
||||
if (_mainWindow.SettingsWindow == null)
|
||||
{
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
_mainWindow.SettingsWindow = new SettingsWindow(_mainWindow.VirtualFileSystem, _mainWindow.ContentManager);
|
||||
_mainWindow.SettingsWindow.NavPanel.Content = _mainWindow.SettingsWindow.InputPage;
|
||||
_mainWindow.SettingsWindow.NavPanel.SelectedItem = _mainWindow.SettingsWindow.NavPanel.MenuItems.ElementAt(1);
|
||||
|
||||
await ContentDialogHelper.ShowWindowAsync(_mainWindow.SettingsWindow, _mainWindow);
|
||||
_mainWindow.SettingsWindow = null;
|
||||
this.Close();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
((ContentDialog)Parent)?.Hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -86,17 +86,17 @@
|
||||
HorizontalAlignment="Stretch"
|
||||
FontWeight="Bold"
|
||||
Text="{Binding TitleName}"
|
||||
TextAlignment="Left"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Stretch"
|
||||
Text="{Binding Developer}"
|
||||
TextAlignment="Left"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Stretch"
|
||||
Text="{Binding Version}"
|
||||
TextAlignment="Left"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
@ -110,12 +110,12 @@
|
||||
<TextBlock
|
||||
HorizontalAlignment="Stretch"
|
||||
Text="{Binding TitleId}"
|
||||
TextAlignment="Left"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Stretch"
|
||||
Text="{Binding FileExtension}"
|
||||
TextAlignment="Left"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
@ -127,17 +127,17 @@
|
||||
<TextBlock
|
||||
HorizontalAlignment="Stretch"
|
||||
Text="{Binding TimePlayedString}"
|
||||
TextAlignment="Right"
|
||||
TextAlignment="End"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Stretch"
|
||||
Text="{Binding LastPlayedString, Converter={helpers:LocalizedNeverConverter}}"
|
||||
TextAlignment="Right"
|
||||
TextAlignment="End"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Stretch"
|
||||
Text="{Binding FileSizeString}"
|
||||
TextAlignment="Right"
|
||||
TextAlignment="End"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
<ui:SymbolIcon
|
||||
|
@ -18,6 +18,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
public static class ContentDialogHelper
|
||||
{
|
||||
private static bool _isChoiceDialogOpen;
|
||||
private static ContentDialogOverlayWindow _contentDialogOverlayWindow;
|
||||
|
||||
private async static Task<UserResult> ShowContentDialog(
|
||||
string title,
|
||||
@ -310,16 +311,20 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
public static async Task<ContentDialogResult> ShowAsync(ContentDialog contentDialog)
|
||||
{
|
||||
ContentDialogResult result;
|
||||
|
||||
ContentDialogOverlayWindow contentDialogOverlayWindow = null;
|
||||
bool isTopDialog = true;
|
||||
|
||||
Window parent = GetMainWindow();
|
||||
|
||||
if (_contentDialogOverlayWindow != null)
|
||||
{
|
||||
isTopDialog = false;
|
||||
}
|
||||
|
||||
if (parent is MainWindow window)
|
||||
{
|
||||
parent.Activate();
|
||||
|
||||
contentDialogOverlayWindow = new()
|
||||
_contentDialogOverlayWindow = new ContentDialogOverlayWindow
|
||||
{
|
||||
Height = parent.Bounds.Height,
|
||||
Width = parent.Bounds.Width,
|
||||
@ -331,14 +336,14 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
|
||||
void OverlayOnPositionChanged(object sender, PixelPointEventArgs e)
|
||||
{
|
||||
contentDialogOverlayWindow.Position = parent.PointToScreen(new Point());
|
||||
_contentDialogOverlayWindow.Position = parent.PointToScreen(new Point());
|
||||
}
|
||||
|
||||
contentDialogOverlayWindow.ContentDialog = contentDialog;
|
||||
_contentDialogOverlayWindow.ContentDialog = contentDialog;
|
||||
|
||||
bool opened = false;
|
||||
|
||||
contentDialogOverlayWindow.Opened += OverlayOnActivated;
|
||||
_contentDialogOverlayWindow.Opened += OverlayOnActivated;
|
||||
|
||||
async void OverlayOnActivated(object sender, EventArgs e)
|
||||
{
|
||||
@ -349,12 +354,12 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
|
||||
opened = true;
|
||||
|
||||
contentDialogOverlayWindow.Position = parent.PointToScreen(new Point());
|
||||
_contentDialogOverlayWindow.Position = parent.PointToScreen(new Point());
|
||||
|
||||
result = await ShowDialog();
|
||||
}
|
||||
|
||||
result = await contentDialogOverlayWindow.ShowDialog<ContentDialogResult>(parent);
|
||||
result = await _contentDialogOverlayWindow.ShowDialog<ContentDialogResult>(parent);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -363,11 +368,11 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
|
||||
async Task<ContentDialogResult> ShowDialog()
|
||||
{
|
||||
if (contentDialogOverlayWindow is not null)
|
||||
if (_contentDialogOverlayWindow is not null)
|
||||
{
|
||||
result = await contentDialog.ShowAsync(contentDialogOverlayWindow);
|
||||
result = await contentDialog.ShowAsync(_contentDialogOverlayWindow);
|
||||
|
||||
contentDialogOverlayWindow!.Close();
|
||||
_contentDialogOverlayWindow!.Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -379,15 +384,22 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
return result;
|
||||
}
|
||||
|
||||
if (contentDialogOverlayWindow is not null)
|
||||
if (isTopDialog && _contentDialogOverlayWindow is not null)
|
||||
{
|
||||
contentDialogOverlayWindow.Content = null;
|
||||
contentDialogOverlayWindow.Close();
|
||||
_contentDialogOverlayWindow.Content = null;
|
||||
_contentDialogOverlayWindow.Close();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Task ShowWindowAsync(Window dialogWindow, Window mainWindow = null)
|
||||
{
|
||||
mainWindow ??= GetMainWindow();
|
||||
|
||||
return dialogWindow.ShowDialog(_contentDialogOverlayWindow ?? mainWindow);
|
||||
}
|
||||
|
||||
private static Window GetMainWindow()
|
||||
{
|
||||
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime al)
|
||||
|
@ -1,40 +0,0 @@
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Models
|
||||
{
|
||||
public class CheatModel : BaseModel
|
||||
{
|
||||
private bool _isEnabled;
|
||||
|
||||
public event EventHandler<bool> EnableToggled;
|
||||
|
||||
public CheatModel(string name, string buildId, bool isEnabled)
|
||||
{
|
||||
Name = name;
|
||||
BuildId = buildId;
|
||||
IsEnabled = isEnabled;
|
||||
}
|
||||
|
||||
public bool IsEnabled
|
||||
{
|
||||
get => _isEnabled;
|
||||
set
|
||||
{
|
||||
_isEnabled = value;
|
||||
|
||||
EnableToggled?.Invoke(this, _isEnabled);
|
||||
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public string BuildId { get; }
|
||||
|
||||
public string BuildIdKey => $"{BuildId}-{Name}";
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public string CleanName => Name[1..^7];
|
||||
}
|
||||
}
|
57
src/Ryujinx.Ava/UI/Models/CheatNode.cs
Normal file
57
src/Ryujinx.Ava/UI/Models/CheatNode.cs
Normal file
@ -0,0 +1,57 @@
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Models
|
||||
{
|
||||
public class CheatNode : BaseModel
|
||||
{
|
||||
private bool _isEnabled = false;
|
||||
public ObservableCollection<CheatNode> SubNodes { get; } = new();
|
||||
public string CleanName => Name[1..^7];
|
||||
public string BuildIdKey => $"{BuildId}-{Name}";
|
||||
public bool IsRootNode { get; }
|
||||
public string Name { get; }
|
||||
public string BuildId { get; }
|
||||
public string Path { get; }
|
||||
public bool IsEnabled
|
||||
{
|
||||
get
|
||||
{
|
||||
if (SubNodes.Count > 0)
|
||||
{
|
||||
return SubNodes.ToList().TrueForAll(x => x.IsEnabled);
|
||||
}
|
||||
|
||||
return _isEnabled;
|
||||
}
|
||||
set
|
||||
{
|
||||
foreach (var cheat in SubNodes)
|
||||
{
|
||||
cheat.IsEnabled = value;
|
||||
cheat.OnPropertyChanged();
|
||||
}
|
||||
|
||||
_isEnabled = value;
|
||||
}
|
||||
}
|
||||
|
||||
public CheatNode(string name, string buildId, string path, bool isRootNode, bool isEnabled = false)
|
||||
{
|
||||
Name = name;
|
||||
BuildId = buildId;
|
||||
Path = path;
|
||||
IsEnabled = isEnabled;
|
||||
IsRootNode = isRootNode;
|
||||
|
||||
SubNodes.CollectionChanged += CheatsList_CollectionChanged;
|
||||
}
|
||||
|
||||
private void CheatsList_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
OnPropertyChanged(nameof(IsEnabled));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Models
|
||||
{
|
||||
public class CheatsList : ObservableCollection<CheatModel>
|
||||
{
|
||||
public CheatsList(string buildId, string path)
|
||||
{
|
||||
BuildId = buildId;
|
||||
Path = path;
|
||||
|
||||
CollectionChanged += CheatsList_CollectionChanged;
|
||||
}
|
||||
|
||||
public string BuildId { get; }
|
||||
public string Path { get; }
|
||||
|
||||
public bool IsEnabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.ToList().TrueForAll(x => x.IsEnabled);
|
||||
}
|
||||
set
|
||||
{
|
||||
foreach (var cheat in this)
|
||||
{
|
||||
cheat.IsEnabled = value;
|
||||
}
|
||||
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsEnabled)));
|
||||
}
|
||||
}
|
||||
|
||||
private void CheatsList_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if (e.Action == NotifyCollectionChangedAction.Add)
|
||||
{
|
||||
(e.NewItems[0] as CheatModel).EnableToggled += Item_EnableToggled;
|
||||
}
|
||||
}
|
||||
|
||||
private void Item_EnableToggled(object sender, bool e)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsEnabled)));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +1,12 @@
|
||||
<UserControl
|
||||
<UserControl
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="800"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="800"
|
||||
d:DesignHeight="450"
|
||||
x:Class="Ryujinx.Ava.UI.Renderer.RendererHost"
|
||||
FlowDirection="LeftToRight"
|
||||
Focusable="True">
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
|
@ -17,6 +17,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava.UI.ViewModels
|
||||
@ -188,35 +189,83 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
_httpClient.Dispose();
|
||||
}
|
||||
|
||||
private async Task LoadContentAsync()
|
||||
private static bool TryGetAmiiboJson(string json, out AmiiboJson amiiboJson)
|
||||
{
|
||||
string amiiboJsonString = DefaultJson;
|
||||
|
||||
if (File.Exists(_amiiboJsonPath))
|
||||
if (string.IsNullOrEmpty(json))
|
||||
{
|
||||
amiiboJsonString = await File.ReadAllTextAsync(_amiiboJsonPath);
|
||||
amiiboJson = JsonHelper.Deserialize(DefaultJson, _serializerContext.AmiiboJson);
|
||||
|
||||
if (await NeedsUpdate(JsonHelper.Deserialize(amiiboJsonString, _serializerContext.AmiiboJson).LastUpdated))
|
||||
{
|
||||
amiiboJsonString = await DownloadAmiiboJson();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else
|
||||
|
||||
try
|
||||
{
|
||||
amiiboJson = JsonHelper.Deserialize(json, _serializerContext.AmiiboJson);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (JsonException exception)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"Unable to deserialize amiibo data: {exception}");
|
||||
amiiboJson = JsonHelper.Deserialize(DefaultJson, _serializerContext.AmiiboJson);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<AmiiboJson> GetMostRecentAmiiboListOrDefaultJson()
|
||||
{
|
||||
bool localIsValid = false;
|
||||
bool remoteIsValid = false;
|
||||
AmiiboJson amiiboJson = new();
|
||||
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
amiiboJsonString = await DownloadAmiiboJson();
|
||||
if (File.Exists(_amiiboJsonPath))
|
||||
{
|
||||
localIsValid = TryGetAmiiboJson(await File.ReadAllTextAsync(_amiiboJsonPath), out amiiboJson);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (Exception exception)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"Failed to download amiibo data: {ex}");
|
||||
Logger.Warning?.Print(LogClass.Application, $"Unable to read data from '{_amiiboJsonPath}': {exception}");
|
||||
}
|
||||
|
||||
if (!localIsValid || await NeedsUpdate(amiiboJson.LastUpdated))
|
||||
{
|
||||
remoteIsValid = TryGetAmiiboJson(await DownloadAmiiboJson(), out amiiboJson);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (!(localIsValid || remoteIsValid))
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"Couldn't get valid amiibo data: {exception}");
|
||||
|
||||
// Neither local or remote files are valid JSON, close window.
|
||||
ShowInfoDialog();
|
||||
Close();
|
||||
}
|
||||
else if (!remoteIsValid)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"Couldn't update amiibo data: {exception}");
|
||||
|
||||
// Only the local file is valid, the local one should be used
|
||||
// but the user should be warned.
|
||||
ShowInfoDialog();
|
||||
}
|
||||
}
|
||||
|
||||
_amiiboList = JsonHelper.Deserialize(amiiboJsonString, _serializerContext.AmiiboJson).Amiibo;
|
||||
_amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList();
|
||||
return amiiboJson;
|
||||
}
|
||||
|
||||
private async Task LoadContentAsync()
|
||||
{
|
||||
AmiiboJson amiiboJson = await GetMostRecentAmiiboListOrDefaultJson();
|
||||
|
||||
_amiiboList = amiiboJson.Amiibo.OrderBy(amiibo => amiibo.AmiiboSeries).ToList();
|
||||
|
||||
ParseAmiiboData();
|
||||
}
|
||||
@ -364,43 +413,50 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
try
|
||||
{
|
||||
HttpResponseMessage response =
|
||||
await _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, "https://amiibo.ryujinx.org/"));
|
||||
HttpResponseMessage response = await _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, "https://amiibo.ryujinx.org/"));
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return response.Content.Headers.LastModified != new DateTimeOffset(oldLastModified.Ticks - (oldLastModified.Ticks % TimeSpan.TicksPerSecond), TimeSpan.Zero);
|
||||
return response.Content.Headers.LastModified != oldLastModified;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (HttpRequestException exception)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"Failed to check for amiibo updates: {ex}");
|
||||
|
||||
ShowInfoDialog();
|
||||
|
||||
return false;
|
||||
Logger.Error?.Print(LogClass.Application, $"Unable to check for amiibo data updates: {exception}");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task<string> DownloadAmiiboJson()
|
||||
{
|
||||
HttpResponseMessage response = await _httpClient.GetAsync("https://amiibo.ryujinx.org/");
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
try
|
||||
{
|
||||
string amiiboJsonString = await response.Content.ReadAsStringAsync();
|
||||
HttpResponseMessage response = await _httpClient.GetAsync("https://amiibo.ryujinx.org/");
|
||||
|
||||
using (FileStream amiiboJsonStream = File.Create(_amiiboJsonPath, 4096, FileOptions.WriteThrough))
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
amiiboJsonStream.Write(Encoding.UTF8.GetBytes(amiiboJsonString));
|
||||
string amiiboJsonString = await response.Content.ReadAsStringAsync();
|
||||
|
||||
try
|
||||
{
|
||||
using FileStream dlcJsonStream = File.Create(_amiiboJsonPath, 4096, FileOptions.WriteThrough);
|
||||
dlcJsonStream.Write(Encoding.UTF8.GetBytes(amiiboJsonString));
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"Couldn't write amiibo data to file '{_amiiboJsonPath}: {exception}'");
|
||||
}
|
||||
|
||||
return amiiboJsonString;
|
||||
}
|
||||
|
||||
return amiiboJsonString;
|
||||
Logger.Error?.Print(LogClass.Application, $"Failed to download amiibo data. Response status code: {response.StatusCode}");
|
||||
}
|
||||
catch (HttpRequestException exception)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"Failed to request amiibo data: {exception}");
|
||||
}
|
||||
|
||||
Logger.Error?.Print(LogClass.Application, $"Failed to download amiibo data. Response status code: {response.StatusCode}");
|
||||
|
||||
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogAmiiboApiTitle],
|
||||
LocaleManager.Instance[LocaleKeys.DialogAmiiboApiFailFetchMessage],
|
||||
@ -408,9 +464,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
"",
|
||||
LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
|
||||
|
||||
Close();
|
||||
|
||||
return DefaultJson;
|
||||
return null;
|
||||
}
|
||||
|
||||
private void Close()
|
||||
|
@ -90,6 +90,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
private string _pauseKey = "F5";
|
||||
private string _screenshotKey = "F8";
|
||||
private float _volume;
|
||||
private float _volumeBeforeMute;
|
||||
private string _backendText;
|
||||
|
||||
private bool _canUpdate = true;
|
||||
@ -554,6 +555,17 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public float VolumeBeforeMute
|
||||
{
|
||||
get => _volumeBeforeMute;
|
||||
set
|
||||
{
|
||||
_volumeBeforeMute = value;
|
||||
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShowStatusSeparator
|
||||
{
|
||||
get => _showStatusSeparator;
|
||||
|
@ -101,7 +101,7 @@
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
SelectedIndex="0"
|
||||
ItemsSource="{Binding ProfilesList}"
|
||||
ItemsSource="{Binding ProfilesList}"
|
||||
Text="{Binding ProfileName, Mode=TwoWay}" />
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
@ -218,6 +218,7 @@
|
||||
<Grid
|
||||
Name="SettingButtons"
|
||||
MinHeight="450"
|
||||
FlowDirection="LeftToRight"
|
||||
IsVisible="{Binding ShowSettings}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
|
@ -122,6 +122,8 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
|
||||
await Window.SettingsWindow.ShowDialog(Window);
|
||||
|
||||
Window.SettingsWindow = null;
|
||||
|
||||
ViewModel.LoadConfigurableHotKeys();
|
||||
}
|
||||
|
||||
|
@ -88,7 +88,7 @@
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
PointerReleased="VsyncStatus_PointerReleased"
|
||||
Text="VSync"
|
||||
TextAlignment="Left" />
|
||||
TextAlignment="Start" />
|
||||
<Border
|
||||
Width="2"
|
||||
Height="12"
|
||||
@ -105,7 +105,7 @@
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
PointerReleased="DockedStatus_PointerReleased"
|
||||
Text="{Binding DockedStatusText}"
|
||||
TextAlignment="Left" />
|
||||
TextAlignment="Start" />
|
||||
<Border
|
||||
Width="2"
|
||||
Height="12"
|
||||
@ -225,7 +225,7 @@
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
Text="{Binding GameStatusText}"
|
||||
TextAlignment="Left" />
|
||||
TextAlignment="Start" />
|
||||
<Border
|
||||
Width="2"
|
||||
Height="12"
|
||||
@ -240,7 +240,7 @@
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
Text="{Binding FifoStatusText}"
|
||||
TextAlignment="Left" />
|
||||
TextAlignment="Start" />
|
||||
<Border
|
||||
Width="2"
|
||||
Height="12"
|
||||
@ -255,7 +255,7 @@
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
Text="{Binding BackendText}"
|
||||
TextAlignment="Left" />
|
||||
TextAlignment="Start" />
|
||||
<Border
|
||||
Width="2"
|
||||
Height="12"
|
||||
@ -270,7 +270,7 @@
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
Text="{Binding GpuNameText}"
|
||||
TextAlignment="Left" />
|
||||
TextAlignment="Start" />
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Column="3"
|
||||
|
@ -27,7 +27,7 @@
|
||||
Grid.Row="0"
|
||||
TextWrapping="Wrap"
|
||||
HorizontalAlignment="Left"
|
||||
TextAlignment="Left"
|
||||
TextAlignment="Start"
|
||||
Text="{locale:Locale ProfileImageSelectionNote}" />
|
||||
<StackPanel
|
||||
Grid.Row="2"
|
||||
@ -59,4 +59,4 @@
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
|
@ -49,7 +49,7 @@
|
||||
<TextBlock
|
||||
HorizontalAlignment="Stretch"
|
||||
Text="{Binding UserId}"
|
||||
TextAlignment="Left"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="Wrap" />
|
||||
<Button Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
@ -79,4 +79,4 @@
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
|
@ -238,7 +238,7 @@
|
||||
<TextBlock
|
||||
FontSize="10"
|
||||
Text="{locale:Locale AboutRyujinxContributorsButtonHeader}"
|
||||
TextAlignment="Right"
|
||||
TextAlignment="End"
|
||||
ToolTip.Tip="{locale:Locale AboutRyujinxMaintainersContentTooltipMessage}" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
@ -72,4 +72,4 @@
|
||||
Click="CancelButton_Click" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</window:StyleableWindow>
|
||||
</window:StyleableWindow>
|
||||
|
@ -86,28 +86,16 @@
|
||||
</Style>
|
||||
</Styles>
|
||||
</TreeView.Styles>
|
||||
<TreeView.DataTemplates>
|
||||
<TreeDataTemplate DataType="model:CheatsList" ItemsSource="{Binding}">
|
||||
<TreeView.ItemTemplate>
|
||||
<TreeDataTemplate ItemsSource="{Binding SubNodes}">
|
||||
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
|
||||
<CheckBox MinWidth="20" IsChecked="{Binding IsEnabled}" />
|
||||
<TextBlock Width="150" Text="{Binding BuildId}" />
|
||||
<TextBlock Text="{Binding Path}" />
|
||||
<TextBlock Width="150" Text="{Binding CleanName}" IsVisible="{Binding !IsRootNode}" />
|
||||
<TextBlock Width="150" Text="{Binding BuildId}" IsVisible="{Binding IsRootNode}" />
|
||||
<TextBlock Text="{Binding Path}" IsVisible="{Binding IsRootNode}" />
|
||||
</StackPanel>
|
||||
</TreeDataTemplate>
|
||||
<DataTemplate x:DataType="model:CheatModel">
|
||||
<StackPanel
|
||||
Margin="0"
|
||||
HorizontalAlignment="Left"
|
||||
Orientation="Horizontal">
|
||||
<CheckBox
|
||||
MinWidth="20"
|
||||
Margin="5,0"
|
||||
Padding="0"
|
||||
IsChecked="{Binding IsEnabled}" />
|
||||
<TextBlock VerticalAlignment="Center" Text="{Binding CleanName}" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</TreeView.DataTemplates>
|
||||
</TreeView.ItemTemplate>
|
||||
</TreeView>
|
||||
</Border>
|
||||
<DockPanel
|
||||
|
@ -17,7 +17,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
private readonly string _enabledCheatsPath;
|
||||
public bool NoCheatsFound { get; }
|
||||
|
||||
public AvaloniaList<CheatsList> LoadedCheats { get; }
|
||||
public AvaloniaList<CheatNode> LoadedCheats { get; }
|
||||
|
||||
public string Heading { get; }
|
||||
public string BuildId { get; }
|
||||
@ -33,7 +33,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
public CheatWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName, string titlePath)
|
||||
{
|
||||
LoadedCheats = new AvaloniaList<CheatsList>();
|
||||
LoadedCheats = new AvaloniaList<CheatNode>();
|
||||
|
||||
Heading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.CheatWindowHeading, titleName, titleId.ToUpper());
|
||||
BuildId = ApplicationData.GetApplicationBuildId(virtualFileSystem, titlePath);
|
||||
@ -62,7 +62,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
string currentCheatFile = string.Empty;
|
||||
string buildId = string.Empty;
|
||||
|
||||
CheatsList currentGroup = null;
|
||||
CheatNode currentGroup = null;
|
||||
|
||||
foreach (var cheat in mods.Cheats)
|
||||
{
|
||||
@ -72,13 +72,13 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
string parentPath = currentCheatFile.Replace(titleModsPath, "");
|
||||
|
||||
buildId = Path.GetFileNameWithoutExtension(currentCheatFile).ToUpper();
|
||||
currentGroup = new CheatsList(buildId, parentPath);
|
||||
currentGroup = new CheatNode("", buildId, parentPath, true);
|
||||
|
||||
LoadedCheats.Add(currentGroup);
|
||||
}
|
||||
|
||||
var model = new CheatModel(cheat.Name, buildId, enabled.Contains($"{buildId}-{cheat.Name}"));
|
||||
currentGroup?.Add(model);
|
||||
var model = new CheatNode(cheat.Name, buildId, "", false, enabled.Contains($"{buildId}-{cheat.Name}"));
|
||||
currentGroup?.SubNodes.Add(model);
|
||||
|
||||
cheatAdded++;
|
||||
}
|
||||
@ -104,7 +104,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
foreach (var cheats in LoadedCheats)
|
||||
{
|
||||
foreach (var cheat in cheats)
|
||||
foreach (var cheat in cheats.SubNodes)
|
||||
{
|
||||
if (cheat.IsEnabled)
|
||||
{
|
||||
|
@ -158,7 +158,7 @@
|
||||
FontWeight="Bold"
|
||||
IsVisible="{Binding ShowLoadProgress}"
|
||||
Text="{Binding LoadHeading}"
|
||||
TextAlignment="Left"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="Wrap"
|
||||
MaxWidth="500" />
|
||||
<Border
|
||||
@ -192,7 +192,7 @@
|
||||
FontSize="18"
|
||||
IsVisible="{Binding ShowLoadProgress}"
|
||||
Text="{Binding CacheLoadStatus}"
|
||||
TextAlignment="Left"
|
||||
TextAlignment="Start"
|
||||
MaxWidth="500" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
@ -202,4 +202,4 @@
|
||||
Grid.Row="2" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</window:StyleableWindow>
|
||||
</window:StyleableWindow>
|
||||
|
@ -436,10 +436,11 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
if (!volumeSplitButton.IsChecked)
|
||||
{
|
||||
ViewModel.AppHost.Device.SetVolume(ConfigurationState.Instance.System.AudioVolume);
|
||||
ViewModel.AppHost.Device.SetVolume(ViewModel.VolumeBeforeMute);
|
||||
}
|
||||
else
|
||||
{
|
||||
ViewModel.VolumeBeforeMute = ViewModel.AppHost.Device.GetVolume();
|
||||
ViewModel.AppHost.Device.SetVolume(0);
|
||||
}
|
||||
|
||||
|
@ -101,6 +101,9 @@
|
||||
<Style Selector="Grid#PlaceholderGrid">
|
||||
<Setter Property="Height" Value="40" />
|
||||
</Style>
|
||||
<Style Selector="ui|NavigationViewItem ui|SymbolIcon">
|
||||
<Setter Property="FlowDirection" Value="LeftToRight" />
|
||||
</Style>
|
||||
</ui:NavigationView.Styles>
|
||||
</ui:NavigationView>
|
||||
<ReversibleStackPanel
|
||||
|
@ -1,7 +1,9 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Platform;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ui.Common.Configuration;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
@ -22,6 +24,14 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
Icon = new WindowIcon(stream);
|
||||
stream.Position = 0;
|
||||
IconImage = new Bitmap(stream);
|
||||
|
||||
LocaleManager.Instance.LocaleChanged += LocaleChanged;
|
||||
LocaleChanged();
|
||||
}
|
||||
|
||||
private void LocaleChanged()
|
||||
{
|
||||
FlowDirection = LocaleManager.Instance.IsRTL() ? FlowDirection.RightToLeft : FlowDirection.LeftToRight;
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
|
@ -130,4 +130,4 @@
|
||||
</StackPanel>
|
||||
</Panel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
|
@ -63,6 +63,17 @@ namespace Ryujinx.Common.Configuration
|
||||
string userProfilePath = Path.Combine(appDataPath, DefaultBaseDir);
|
||||
string portablePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, DefaultPortableDir);
|
||||
|
||||
// On macOS, check for a portable directory next to the app bundle as well.
|
||||
if (OperatingSystem.IsMacOS() && !Directory.Exists(portablePath))
|
||||
{
|
||||
string bundlePath = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..", ".."));
|
||||
// Make sure we're actually running within an app bundle.
|
||||
if (bundlePath.EndsWith(".app"))
|
||||
{
|
||||
portablePath = Path.GetFullPath(Path.Combine(bundlePath, "..", DefaultPortableDir));
|
||||
}
|
||||
}
|
||||
|
||||
if (Directory.Exists(portablePath))
|
||||
{
|
||||
BaseDirPath = portablePath;
|
||||
|
@ -1,62 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
|
||||
namespace Ryujinx.Cpu.AppleHv
|
||||
{
|
||||
static class HvCodePatcher
|
||||
{
|
||||
private const uint XMask = 0x3f808000u;
|
||||
private const uint XValue = 0x8000000u;
|
||||
|
||||
private const uint ZrIndex = 31u;
|
||||
|
||||
public static void RewriteUnorderedExclusiveInstructions(Span<byte> code)
|
||||
{
|
||||
Span<uint> codeUint = MemoryMarshal.Cast<byte, uint>(code);
|
||||
Span<Vector128<uint>> codeVector = MemoryMarshal.Cast<byte, Vector128<uint>>(code);
|
||||
|
||||
Vector128<uint> mask = Vector128.Create(XMask);
|
||||
Vector128<uint> value = Vector128.Create(XValue);
|
||||
|
||||
for (int index = 0; index < codeVector.Length; index++)
|
||||
{
|
||||
Vector128<uint> v = codeVector[index];
|
||||
|
||||
if (Vector128.EqualsAny(Vector128.BitwiseAnd(v, mask), value))
|
||||
{
|
||||
int baseIndex = index * 4;
|
||||
|
||||
for (int instIndex = baseIndex; instIndex < baseIndex + 4; instIndex++)
|
||||
{
|
||||
ref uint inst = ref codeUint[instIndex];
|
||||
|
||||
if ((inst & XMask) != XValue)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bool isPair = (inst & (1u << 21)) != 0;
|
||||
bool isLoad = (inst & (1u << 22)) != 0;
|
||||
|
||||
uint rt2 = (inst >> 10) & 0x1fu;
|
||||
uint rs = (inst >> 16) & 0x1fu;
|
||||
|
||||
if (isLoad && rs != ZrIndex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isPair && rt2 != ZrIndex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set the ordered flag.
|
||||
inst |= 1u << 15;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -40,5 +40,9 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
public void PrepareCodeRange(ulong address, ulong size)
|
||||
{
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -724,18 +724,6 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
/// <inheritdoc/>
|
||||
public void Reprotect(ulong va, ulong size, MemoryPermission protection)
|
||||
{
|
||||
if (protection.HasFlag(MemoryPermission.Execute))
|
||||
{
|
||||
// Some applications use unordered exclusive memory access instructions
|
||||
// where it is not valid to do so, leading to memory re-ordering that
|
||||
// makes the code behave incorrectly on some CPUs.
|
||||
// To work around this, we force all such accesses to be ordered.
|
||||
|
||||
using WritableRegion writableRegion = GetWritableRegion(va, (int)size);
|
||||
|
||||
HvCodePatcher.RewriteUnorderedExclusiveInstructions(writableRegion.Memory.Span);
|
||||
}
|
||||
|
||||
// TODO
|
||||
}
|
||||
|
||||
|
17
src/Ryujinx.Cpu/DummyDiskCacheLoadState.cs
Normal file
17
src/Ryujinx.Cpu/DummyDiskCacheLoadState.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Cpu
|
||||
{
|
||||
public class DummyDiskCacheLoadState : IDiskCacheLoadState
|
||||
{
|
||||
#pragma warning disable CS0067 // The event is never used
|
||||
/// <inheritdoc/>
|
||||
public event Action<LoadState, int, int> StateChanged;
|
||||
#pragma warning restore CS0067
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Cancel()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Cpu
|
||||
{
|
||||
/// <summary>
|
||||
/// CPU context interface.
|
||||
/// </summary>
|
||||
public interface ICpuContext
|
||||
public interface ICpuContext : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new execution context that will store thread CPU register state when executing guest code.
|
||||
|
@ -1,5 +1,7 @@
|
||||
using ARMeilleure.Memory;
|
||||
using ARMeilleure.Translation;
|
||||
using Ryujinx.Cpu.Signal;
|
||||
using Ryujinx.Memory;
|
||||
|
||||
namespace Ryujinx.Cpu.Jit
|
||||
{
|
||||
@ -11,7 +13,13 @@ namespace Ryujinx.Cpu.Jit
|
||||
public JitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit)
|
||||
{
|
||||
_tickSource = tickSource;
|
||||
_translator = new Translator(new JitMemoryAllocator(), memory, for64Bit);
|
||||
_translator = new Translator(new JitMemoryAllocator(forJit: true), memory, for64Bit);
|
||||
|
||||
if (memory.Type.IsHostMapped())
|
||||
{
|
||||
NativeSignalHandler.InitializeSignalHandler(MemoryBlock.GetPageSize());
|
||||
}
|
||||
|
||||
memory.UnmapEvent += UnmapHandler;
|
||||
}
|
||||
|
||||
@ -49,5 +57,9 @@ namespace Ryujinx.Cpu.Jit
|
||||
{
|
||||
_translator.PrepareCodeRange(address, size);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,9 +5,14 @@ namespace Ryujinx.Cpu.Jit
|
||||
{
|
||||
public class JitMemoryAllocator : IJitMemoryAllocator
|
||||
{
|
||||
public IJitMemoryBlock Allocate(ulong size) => new JitMemoryBlock(size, MemoryAllocationFlags.None);
|
||||
public IJitMemoryBlock Reserve(ulong size) => new JitMemoryBlock(size, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.Jit);
|
||||
private readonly MemoryAllocationFlags _jitFlag;
|
||||
|
||||
public ulong GetPageSize() => MemoryBlock.GetPageSize();
|
||||
public JitMemoryAllocator(bool forJit = false)
|
||||
{
|
||||
_jitFlag = forJit ? MemoryAllocationFlags.Jit : MemoryAllocationFlags.None;
|
||||
}
|
||||
|
||||
public IJitMemoryBlock Allocate(ulong size) => new JitMemoryBlock(size, MemoryAllocationFlags.None);
|
||||
public IJitMemoryBlock Reserve(ulong size) => new JitMemoryBlock(size, MemoryAllocationFlags.Reserve | _jitFlag);
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ namespace Ryujinx.Cpu.Jit
|
||||
}
|
||||
|
||||
public void Commit(ulong offset, ulong size) => _impl.Commit(offset, size);
|
||||
public void MapAsRw(ulong offset, ulong size) => _impl.Reprotect(offset, size, MemoryPermission.ReadAndWrite);
|
||||
public void MapAsRx(ulong offset, ulong size) => _impl.Reprotect(offset, size, MemoryPermission.ReadAndExecute);
|
||||
public void MapAsRwx(ulong offset, ulong size) => _impl.Reprotect(offset, size, MemoryPermission.ReadWriteExecute);
|
||||
|
||||
|
32
src/Ryujinx.Cpu/LightningJit/AarchCompiler.cs
Normal file
32
src/Ryujinx.Cpu/LightningJit/AarchCompiler.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using ARMeilleure.Common;
|
||||
using ARMeilleure.Memory;
|
||||
using Ryujinx.Cpu.LightningJit.Arm32;
|
||||
using Ryujinx.Cpu.LightningJit.Arm64;
|
||||
using Ryujinx.Cpu.LightningJit.State;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit
|
||||
{
|
||||
class AarchCompiler
|
||||
{
|
||||
public static CompiledFunction Compile(
|
||||
CpuPreset cpuPreset,
|
||||
IMemoryManager memoryManager,
|
||||
ulong address,
|
||||
AddressTable<ulong> funcTable,
|
||||
IntPtr dispatchStubPtr,
|
||||
ExecutionMode executionMode,
|
||||
Architecture targetArch)
|
||||
{
|
||||
if (executionMode == ExecutionMode.Aarch64)
|
||||
{
|
||||
return A64Compiler.Compile(cpuPreset, memoryManager, address, funcTable, dispatchStubPtr, targetArch);
|
||||
}
|
||||
else
|
||||
{
|
||||
return A32Compiler.Compile(cpuPreset, memoryManager, address, funcTable, dispatchStubPtr, executionMode == ExecutionMode.Aarch32Thumb, targetArch);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
18
src/Ryujinx.Cpu/LightningJit/AddressForm.cs
Normal file
18
src/Ryujinx.Cpu/LightningJit/AddressForm.cs
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit
|
||||
{
|
||||
enum AddressForm : byte
|
||||
{
|
||||
None,
|
||||
OffsetReg,
|
||||
PostIndexed,
|
||||
PreIndexed,
|
||||
SignedScaled,
|
||||
UnsignedScaled,
|
||||
BaseRegister,
|
||||
BasePlusOffset,
|
||||
Literal,
|
||||
StructNoOffset,
|
||||
StructPostIndexedReg,
|
||||
}
|
||||
}
|
30
src/Ryujinx.Cpu/LightningJit/Arm32/A32Compiler.cs
Normal file
30
src/Ryujinx.Cpu/LightningJit/Arm32/A32Compiler.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using ARMeilleure.Common;
|
||||
using ARMeilleure.Memory;
|
||||
using Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit.Arm32
|
||||
{
|
||||
static class A32Compiler
|
||||
{
|
||||
public static CompiledFunction Compile(
|
||||
CpuPreset cpuPreset,
|
||||
IMemoryManager memoryManager,
|
||||
ulong address,
|
||||
AddressTable<ulong> funcTable,
|
||||
IntPtr dispatchStubPtr,
|
||||
bool isThumb,
|
||||
Architecture targetArch)
|
||||
{
|
||||
if (targetArch == Architecture.Arm64)
|
||||
{
|
||||
return Compiler.Compile(cpuPreset, memoryManager, address, funcTable, dispatchStubPtr, isThumb);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
101
src/Ryujinx.Cpu/LightningJit/Arm32/Block.cs
Normal file
101
src/Ryujinx.Cpu/LightningJit/Arm32/Block.cs
Normal file
@ -0,0 +1,101 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit.Arm32
|
||||
{
|
||||
class Block
|
||||
{
|
||||
public readonly ulong Address;
|
||||
public readonly ulong EndAddress;
|
||||
public readonly List<InstInfo> Instructions;
|
||||
public readonly bool EndsWithBranch;
|
||||
public readonly bool HasHostCall;
|
||||
public readonly bool IsTruncated;
|
||||
public readonly bool IsLoopEnd;
|
||||
public readonly bool IsThumb;
|
||||
|
||||
public Block(
|
||||
ulong address,
|
||||
ulong endAddress,
|
||||
List<InstInfo> instructions,
|
||||
bool endsWithBranch,
|
||||
bool hasHostCall,
|
||||
bool isTruncated,
|
||||
bool isLoopEnd,
|
||||
bool isThumb)
|
||||
{
|
||||
Debug.Assert(isThumb || (int)((endAddress - address) / 4) == instructions.Count);
|
||||
|
||||
Address = address;
|
||||
EndAddress = endAddress;
|
||||
Instructions = instructions;
|
||||
EndsWithBranch = endsWithBranch;
|
||||
HasHostCall = hasHostCall;
|
||||
IsTruncated = isTruncated;
|
||||
IsLoopEnd = isLoopEnd;
|
||||
IsThumb = isThumb;
|
||||
}
|
||||
|
||||
public (Block, Block) SplitAtAddress(ulong address)
|
||||
{
|
||||
int splitIndex = FindSplitIndex(address);
|
||||
|
||||
if (splitIndex < 0)
|
||||
{
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
int splitCount = Instructions.Count - splitIndex;
|
||||
|
||||
// Technically those are valid, but we don't want to create empty blocks.
|
||||
Debug.Assert(splitIndex != 0);
|
||||
Debug.Assert(splitCount != 0);
|
||||
|
||||
Block leftBlock = new(
|
||||
Address,
|
||||
address,
|
||||
Instructions.GetRange(0, splitIndex),
|
||||
false,
|
||||
HasHostCall,
|
||||
false,
|
||||
false,
|
||||
IsThumb);
|
||||
|
||||
Block rightBlock = new(
|
||||
address,
|
||||
EndAddress,
|
||||
Instructions.GetRange(splitIndex, splitCount),
|
||||
EndsWithBranch,
|
||||
HasHostCall,
|
||||
IsTruncated,
|
||||
IsLoopEnd,
|
||||
IsThumb);
|
||||
|
||||
return (leftBlock, rightBlock);
|
||||
}
|
||||
|
||||
private int FindSplitIndex(ulong address)
|
||||
{
|
||||
if (IsThumb)
|
||||
{
|
||||
ulong pc = Address;
|
||||
|
||||
for (int index = 0; index < Instructions.Count; index++)
|
||||
{
|
||||
if (pc == address)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
pc += Instructions[index].Flags.HasFlag(InstFlags.Thumb16) ? 2UL : 4UL;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (int)((address - Address) / 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
15
src/Ryujinx.Cpu/LightningJit/Arm32/BranchType.cs
Normal file
15
src/Ryujinx.Cpu/LightningJit/Arm32/BranchType.cs
Normal file
@ -0,0 +1,15 @@
|
||||
namespace Ryujinx.Cpu.LightningJit.Arm32
|
||||
{
|
||||
enum BranchType
|
||||
{
|
||||
Branch,
|
||||
Call,
|
||||
IndirectBranch,
|
||||
TableBranchByte,
|
||||
TableBranchHalfword,
|
||||
IndirectCall,
|
||||
SyncPoint,
|
||||
SoftwareInterrupt,
|
||||
ReadCntpct,
|
||||
}
|
||||
}
|
198
src/Ryujinx.Cpu/LightningJit/Arm32/CodeGenContext.cs
Normal file
198
src/Ryujinx.Cpu/LightningJit/Arm32/CodeGenContext.cs
Normal file
@ -0,0 +1,198 @@
|
||||
using ARMeilleure.Memory;
|
||||
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit.Arm32
|
||||
{
|
||||
class CodeGenContext
|
||||
{
|
||||
public CodeWriter CodeWriter { get; }
|
||||
public Assembler Arm64Assembler { get; }
|
||||
public RegisterAllocator RegisterAllocator { get; }
|
||||
|
||||
public MemoryManagerType MemoryManagerType { get; }
|
||||
|
||||
private uint _instructionAddress;
|
||||
|
||||
public bool IsThumb { get; }
|
||||
public uint Pc { get; private set; }
|
||||
public bool InITBlock { get; private set; }
|
||||
|
||||
private InstInfo _nextInstruction;
|
||||
private bool _skipNextInstruction;
|
||||
|
||||
private readonly ArmCondition[] _itConditions;
|
||||
private int _itCount;
|
||||
|
||||
private readonly List<PendingBranch> _pendingBranches;
|
||||
|
||||
private bool _nzcvModified;
|
||||
|
||||
public CodeGenContext(CodeWriter codeWriter, Assembler arm64Assembler, RegisterAllocator registerAllocator, MemoryManagerType mmType, bool isThumb)
|
||||
{
|
||||
CodeWriter = codeWriter;
|
||||
Arm64Assembler = arm64Assembler;
|
||||
RegisterAllocator = registerAllocator;
|
||||
MemoryManagerType = mmType;
|
||||
_itConditions = new ArmCondition[4];
|
||||
_pendingBranches = new();
|
||||
IsThumb = isThumb;
|
||||
}
|
||||
|
||||
public void SetPc(uint address)
|
||||
{
|
||||
// Due to historical reasons, the PC value is always 2 instructions ahead on 32-bit Arm CPUs.
|
||||
Pc = address + (IsThumb ? 4u : 8u);
|
||||
_instructionAddress = address;
|
||||
}
|
||||
|
||||
public void SetNextInstruction(InstInfo info)
|
||||
{
|
||||
_nextInstruction = info;
|
||||
}
|
||||
|
||||
public InstInfo PeekNextInstruction()
|
||||
{
|
||||
return _nextInstruction;
|
||||
}
|
||||
|
||||
public void SetSkipNextInstruction()
|
||||
{
|
||||
_skipNextInstruction = true;
|
||||
}
|
||||
|
||||
public bool ConsumeSkipNextInstruction()
|
||||
{
|
||||
bool skip = _skipNextInstruction;
|
||||
_skipNextInstruction = false;
|
||||
|
||||
return skip;
|
||||
}
|
||||
|
||||
public void AddPendingBranch(InstName name, int offset)
|
||||
{
|
||||
_pendingBranches.Add(new(BranchType.Branch, Pc + (uint)offset, 0u, name, CodeWriter.InstructionPointer));
|
||||
}
|
||||
|
||||
public void AddPendingCall(uint targetAddress, uint nextAddress)
|
||||
{
|
||||
_pendingBranches.Add(new(BranchType.Call, targetAddress, nextAddress, InstName.BlI, CodeWriter.InstructionPointer));
|
||||
|
||||
RegisterAllocator.EnsureTempGprRegisters(1);
|
||||
RegisterAllocator.MarkGprAsUsed(RegisterUtils.LrRegister);
|
||||
}
|
||||
|
||||
public void AddPendingIndirectBranch(InstName name, uint targetRegister)
|
||||
{
|
||||
_pendingBranches.Add(new(BranchType.IndirectBranch, targetRegister, 0u, name, CodeWriter.InstructionPointer));
|
||||
|
||||
RegisterAllocator.MarkGprAsUsed((int)targetRegister);
|
||||
}
|
||||
|
||||
public void AddPendingTableBranch(uint rn, uint rm, bool halfword)
|
||||
{
|
||||
_pendingBranches.Add(new(halfword ? BranchType.TableBranchHalfword : BranchType.TableBranchByte, rn, rm, InstName.Tbb, CodeWriter.InstructionPointer));
|
||||
|
||||
RegisterAllocator.EnsureTempGprRegisters(2);
|
||||
RegisterAllocator.MarkGprAsUsed((int)rn);
|
||||
RegisterAllocator.MarkGprAsUsed((int)rm);
|
||||
}
|
||||
|
||||
public void AddPendingIndirectCall(uint targetRegister, uint nextAddress)
|
||||
{
|
||||
_pendingBranches.Add(new(BranchType.IndirectCall, targetRegister, nextAddress, InstName.BlxR, CodeWriter.InstructionPointer));
|
||||
|
||||
RegisterAllocator.EnsureTempGprRegisters(targetRegister == RegisterUtils.LrRegister ? 1 : 0);
|
||||
RegisterAllocator.MarkGprAsUsed((int)targetRegister);
|
||||
RegisterAllocator.MarkGprAsUsed(RegisterUtils.LrRegister);
|
||||
}
|
||||
|
||||
public void AddPendingSyncPoint()
|
||||
{
|
||||
_pendingBranches.Add(new(BranchType.SyncPoint, 0, 0, default, CodeWriter.InstructionPointer));
|
||||
|
||||
RegisterAllocator.EnsureTempGprRegisters(1);
|
||||
}
|
||||
|
||||
public void AddPendingBkpt(uint imm)
|
||||
{
|
||||
_pendingBranches.Add(new(BranchType.SoftwareInterrupt, imm, _instructionAddress, InstName.Bkpt, CodeWriter.InstructionPointer));
|
||||
|
||||
RegisterAllocator.EnsureTempGprRegisters(1);
|
||||
}
|
||||
|
||||
public void AddPendingSvc(uint imm)
|
||||
{
|
||||
_pendingBranches.Add(new(BranchType.SoftwareInterrupt, imm, _instructionAddress, InstName.Svc, CodeWriter.InstructionPointer));
|
||||
|
||||
RegisterAllocator.EnsureTempGprRegisters(1);
|
||||
}
|
||||
|
||||
public void AddPendingUdf(uint imm)
|
||||
{
|
||||
_pendingBranches.Add(new(BranchType.SoftwareInterrupt, imm, _instructionAddress, InstName.Udf, CodeWriter.InstructionPointer));
|
||||
|
||||
RegisterAllocator.EnsureTempGprRegisters(1);
|
||||
}
|
||||
|
||||
public void AddPendingReadCntpct(uint rt, uint rt2)
|
||||
{
|
||||
_pendingBranches.Add(new(BranchType.ReadCntpct, rt, rt2, InstName.Mrrc, CodeWriter.InstructionPointer));
|
||||
|
||||
RegisterAllocator.EnsureTempGprRegisters(1);
|
||||
}
|
||||
|
||||
public IEnumerable<PendingBranch> GetPendingBranches()
|
||||
{
|
||||
return _pendingBranches;
|
||||
}
|
||||
|
||||
public void SetItBlockStart(ReadOnlySpan<ArmCondition> conditions)
|
||||
{
|
||||
_itCount = conditions.Length;
|
||||
|
||||
for (int index = 0; index < conditions.Length; index++)
|
||||
{
|
||||
_itConditions[index] = conditions[index];
|
||||
}
|
||||
|
||||
InITBlock = true;
|
||||
}
|
||||
|
||||
public bool ConsumeItCondition(out ArmCondition condition)
|
||||
{
|
||||
if (_itCount != 0)
|
||||
{
|
||||
condition = _itConditions[--_itCount];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
condition = ArmCondition.Al;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void UpdateItState()
|
||||
{
|
||||
if (_itCount == 0)
|
||||
{
|
||||
InITBlock = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetNzcvModified()
|
||||
{
|
||||
_nzcvModified = true;
|
||||
}
|
||||
|
||||
public bool ConsumeNzcvModified()
|
||||
{
|
||||
bool modified = _nzcvModified;
|
||||
_nzcvModified = false;
|
||||
|
||||
return modified;
|
||||
}
|
||||
}
|
||||
}
|
546
src/Ryujinx.Cpu/LightningJit/Arm32/Decoder.cs
Normal file
546
src/Ryujinx.Cpu/LightningJit/Arm32/Decoder.cs
Normal file
@ -0,0 +1,546 @@
|
||||
using ARMeilleure.Memory;
|
||||
using Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64;
|
||||
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit.Arm32
|
||||
{
|
||||
static class Decoder<T> where T : IInstEmit
|
||||
{
|
||||
public static MultiBlock DecodeMulti(CpuPreset cpuPreset, IMemoryManager memoryManager, ulong address, bool isThumb)
|
||||
{
|
||||
List<Block> blocks = new();
|
||||
List<ulong> branchTargets = new();
|
||||
|
||||
while (true)
|
||||
{
|
||||
Block block = Decode(cpuPreset, memoryManager, address, isThumb);
|
||||
|
||||
if (!block.IsTruncated && TryGetBranchTarget(block, out ulong targetAddress))
|
||||
{
|
||||
branchTargets.Add(targetAddress);
|
||||
}
|
||||
|
||||
blocks.Add(block);
|
||||
|
||||
if (block.IsTruncated || !HasNextBlock(block, block.EndAddress - 4UL, branchTargets))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
address = block.EndAddress;
|
||||
}
|
||||
|
||||
branchTargets.Sort();
|
||||
SplitBlocks(blocks, branchTargets);
|
||||
|
||||
return new(blocks);
|
||||
}
|
||||
|
||||
private static bool TryGetBranchTarget(Block block, out ulong targetAddress)
|
||||
{
|
||||
// PC is 2 instructions ahead, since the end address is already one instruction after the last one, we just need to add
|
||||
// another instruction.
|
||||
|
||||
ulong pc = block.EndAddress + (block.IsThumb ? 2UL : 4UL);
|
||||
|
||||
return TryGetBranchTarget(block.Instructions[^1].Name, block.Instructions[^1].Flags, pc, block.Instructions[^1].Encoding, block.IsThumb, out targetAddress);
|
||||
}
|
||||
|
||||
private static bool TryGetBranchTarget(InstName name, InstFlags flags, ulong pc, uint encoding, bool isThumb, out ulong targetAddress)
|
||||
{
|
||||
int originalOffset;
|
||||
|
||||
switch (name)
|
||||
{
|
||||
case InstName.B:
|
||||
if (isThumb)
|
||||
{
|
||||
if (flags.HasFlag(InstFlags.Thumb16))
|
||||
{
|
||||
if ((encoding & (1u << 29)) != 0)
|
||||
{
|
||||
InstImm11b16w11 inst = new(encoding);
|
||||
|
||||
originalOffset = ImmUtils.ExtractT16SImm11Times2(inst.Imm11);
|
||||
}
|
||||
else
|
||||
{
|
||||
InstCondb24w4Imm8b16w8 inst = new(encoding);
|
||||
|
||||
originalOffset = ImmUtils.ExtractT16SImm8Times2(inst.Imm8);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((encoding & (1u << 12)) != 0)
|
||||
{
|
||||
InstSb26w1Imm10b16w10J1b13w1J2b11w1Imm11b0w11 inst = new(encoding);
|
||||
|
||||
originalOffset = ImmUtils.CombineSImm24Times2(inst.Imm11, inst.Imm10, inst.J1, inst.J2, inst.S);
|
||||
}
|
||||
else
|
||||
{
|
||||
InstSb26w1Condb22w4Imm6b16w6J1b13w1J2b11w1Imm11b0w11 inst = new(encoding);
|
||||
|
||||
originalOffset = ImmUtils.CombineSImm20Times2(inst.Imm11, inst.Imm6, inst.J1, inst.J2, inst.S);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
originalOffset = ImmUtils.ExtractSImm24Times4(encoding);
|
||||
}
|
||||
|
||||
targetAddress = pc + (ulong)originalOffset;
|
||||
Debug.Assert((targetAddress & 1) == 0);
|
||||
|
||||
return true;
|
||||
|
||||
case InstName.Cbnz:
|
||||
originalOffset = ImmUtils.ExtractT16UImm5Times2(encoding);
|
||||
targetAddress = pc + (ulong)originalOffset;
|
||||
Debug.Assert((targetAddress & 1) == 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
targetAddress = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void SplitBlocks(List<Block> blocks, List<ulong> branchTargets)
|
||||
{
|
||||
int btIndex = 0;
|
||||
|
||||
while (btIndex < branchTargets.Count)
|
||||
{
|
||||
for (int blockIndex = 0; blockIndex < blocks.Count && btIndex < branchTargets.Count; blockIndex++)
|
||||
{
|
||||
Block block = blocks[blockIndex];
|
||||
ulong currentBranchTarget = branchTargets[btIndex];
|
||||
|
||||
while (currentBranchTarget >= block.Address && currentBranchTarget < block.EndAddress)
|
||||
{
|
||||
if (block.Address != currentBranchTarget)
|
||||
{
|
||||
(Block leftBlock, Block rightBlock) = block.SplitAtAddress(currentBranchTarget);
|
||||
|
||||
if (leftBlock != null && rightBlock != null)
|
||||
{
|
||||
blocks.Insert(blockIndex, leftBlock);
|
||||
blocks[blockIndex + 1] = rightBlock;
|
||||
|
||||
block = leftBlock;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Split can only fail in thumb mode, where the instruction size is not fixed.
|
||||
|
||||
Debug.Assert(block.IsThumb);
|
||||
}
|
||||
}
|
||||
|
||||
btIndex++;
|
||||
|
||||
while (btIndex < branchTargets.Count && branchTargets[btIndex] == currentBranchTarget)
|
||||
{
|
||||
btIndex++;
|
||||
}
|
||||
|
||||
if (btIndex >= branchTargets.Count)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
currentBranchTarget = branchTargets[btIndex];
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Assert(btIndex < int.MaxValue);
|
||||
btIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool HasNextBlock(in Block block, ulong pc, List<ulong> branchTargets)
|
||||
{
|
||||
InstFlags lastInstFlags = block.Instructions[^1].Flags;
|
||||
|
||||
// Thumb has separate encodings for conditional and unconditional branch instructions.
|
||||
if (lastInstFlags.HasFlag(InstFlags.Cond) && (block.IsThumb || (ArmCondition)(block.Instructions[^1].Encoding >> 28) < ArmCondition.Al))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (block.Instructions[^1].Name)
|
||||
{
|
||||
case InstName.B:
|
||||
return branchTargets.Contains(pc + 4UL) ||
|
||||
(TryGetBranchTarget(block, out ulong targetAddress) && targetAddress >= pc && targetAddress < pc + 0x1000);
|
||||
|
||||
case InstName.Bx:
|
||||
case InstName.Bxj:
|
||||
return branchTargets.Contains(pc + 4UL);
|
||||
|
||||
case InstName.Cbnz:
|
||||
case InstName.BlI:
|
||||
case InstName.BlxR:
|
||||
return true;
|
||||
}
|
||||
|
||||
if (WritesToPC(block.Instructions[^1].Encoding, block.Instructions[^1].Name, lastInstFlags, block.IsThumb))
|
||||
{
|
||||
return branchTargets.Contains(pc + 4UL);
|
||||
}
|
||||
|
||||
return !block.EndsWithBranch;
|
||||
}
|
||||
|
||||
private static Block Decode(CpuPreset cpuPreset, IMemoryManager memoryManager, ulong address, bool isThumb)
|
||||
{
|
||||
ulong startAddress = address;
|
||||
|
||||
List<InstInfo> insts = new();
|
||||
|
||||
uint encoding;
|
||||
InstMeta meta;
|
||||
InstFlags extraFlags = InstFlags.None;
|
||||
bool hasHostCall = false;
|
||||
bool isTruncated = false;
|
||||
|
||||
do
|
||||
{
|
||||
if (!memoryManager.IsMapped(address))
|
||||
{
|
||||
encoding = 0;
|
||||
meta = default;
|
||||
isTruncated = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (isThumb)
|
||||
{
|
||||
encoding = (uint)memoryManager.Read<ushort>(address) << 16;
|
||||
address += 2UL;
|
||||
|
||||
extraFlags = InstFlags.Thumb16;
|
||||
|
||||
if (!InstTableT16<T>.TryGetMeta(encoding, cpuPreset.Version, cpuPreset.Features, out meta))
|
||||
{
|
||||
encoding |= memoryManager.Read<ushort>(address);
|
||||
|
||||
if (InstTableT32<T>.TryGetMeta(encoding, cpuPreset.Version, cpuPreset.Features, out meta))
|
||||
{
|
||||
address += 2UL;
|
||||
extraFlags = InstFlags.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
encoding = memoryManager.Read<uint>(address);
|
||||
address += 4UL;
|
||||
|
||||
meta = InstTableA32<T>.GetMeta(encoding, cpuPreset.Version, cpuPreset.Features);
|
||||
}
|
||||
|
||||
if (meta.Name.IsSystemOrCall() && !hasHostCall)
|
||||
{
|
||||
hasHostCall = meta.Name.IsCall() || InstEmitSystem.NeedsCall(meta.Name);
|
||||
}
|
||||
|
||||
insts.Add(new(encoding, meta.Name, meta.EmitFunc, meta.Flags | extraFlags));
|
||||
}
|
||||
while (!IsControlFlow(encoding, meta.Name, meta.Flags | extraFlags, isThumb));
|
||||
|
||||
bool isLoopEnd = false;
|
||||
|
||||
if (!isTruncated && IsBackwardsBranch(meta.Name, encoding))
|
||||
{
|
||||
hasHostCall = true;
|
||||
isLoopEnd = true;
|
||||
}
|
||||
|
||||
return new(
|
||||
startAddress,
|
||||
address,
|
||||
insts,
|
||||
!isTruncated,
|
||||
hasHostCall,
|
||||
isTruncated,
|
||||
isLoopEnd,
|
||||
isThumb);
|
||||
}
|
||||
|
||||
private static bool IsControlFlow(uint encoding, InstName name, InstFlags flags, bool isThumb)
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case InstName.B:
|
||||
case InstName.BlI:
|
||||
case InstName.BlxR:
|
||||
case InstName.Bx:
|
||||
case InstName.Bxj:
|
||||
case InstName.Cbnz:
|
||||
case InstName.Tbb:
|
||||
return true;
|
||||
}
|
||||
|
||||
return WritesToPC(encoding, name, flags, isThumb);
|
||||
}
|
||||
|
||||
public static bool WritesToPC(uint encoding, InstName name, InstFlags flags, bool isThumb)
|
||||
{
|
||||
return (GetRegisterWriteMask(encoding, name, flags, isThumb) & (1u << RegisterUtils.PcRegister)) != 0;
|
||||
}
|
||||
|
||||
private static uint GetRegisterWriteMask(uint encoding, InstName name, InstFlags flags, bool isThumb)
|
||||
{
|
||||
uint mask = 0;
|
||||
|
||||
if (isThumb)
|
||||
{
|
||||
if (flags.HasFlag(InstFlags.Thumb16))
|
||||
{
|
||||
if (flags.HasFlag(InstFlags.Rdn))
|
||||
{
|
||||
mask |= 1u << RegisterUtils.ExtractRdn(flags, encoding);
|
||||
}
|
||||
|
||||
if (flags.HasFlag(InstFlags.Rd))
|
||||
{
|
||||
mask |= 1u << RegisterUtils.ExtractRdT16(flags, encoding);
|
||||
}
|
||||
|
||||
Debug.Assert(!flags.HasFlag(InstFlags.RdHi));
|
||||
|
||||
if (IsRegisterWrite(flags, InstFlags.Rt))
|
||||
{
|
||||
mask |= 1u << RegisterUtils.ExtractRtT16(flags, encoding);
|
||||
}
|
||||
|
||||
Debug.Assert(!flags.HasFlag(InstFlags.Rt2));
|
||||
|
||||
if (IsRegisterWrite(flags, InstFlags.Rlist))
|
||||
{
|
||||
mask |= (byte)(encoding >> 16);
|
||||
|
||||
if (name == InstName.Push)
|
||||
{
|
||||
mask |= (encoding >> 10) & 0x4000; // LR
|
||||
}
|
||||
else if (name == InstName.Pop)
|
||||
{
|
||||
mask |= (encoding >> 9) & 0x8000; // PC
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Assert(!flags.HasFlag(InstFlags.WBack));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (flags.HasFlag(InstFlags.Rd))
|
||||
{
|
||||
mask |= 1u << RegisterUtils.ExtractRdT32(flags, encoding);
|
||||
}
|
||||
|
||||
if (flags.HasFlag(InstFlags.RdLo))
|
||||
{
|
||||
mask |= 1u << RegisterUtils.ExtractRdLoT32(encoding);
|
||||
}
|
||||
|
||||
if (flags.HasFlag(InstFlags.RdHi))
|
||||
{
|
||||
mask |= 1u << RegisterUtils.ExtractRdHiT32(encoding);
|
||||
}
|
||||
|
||||
if (IsRegisterWrite(flags, InstFlags.Rt) && IsRtWrite(name, encoding) && !IsR15RtEncodingSpecial(name, encoding))
|
||||
{
|
||||
mask |= 1u << RegisterUtils.ExtractRtT32(encoding);
|
||||
}
|
||||
|
||||
if (IsRegisterWrite(flags, InstFlags.Rt2) && IsRtWrite(name, encoding))
|
||||
{
|
||||
mask |= 1u << RegisterUtils.ExtractRt2T32(encoding);
|
||||
}
|
||||
|
||||
if (IsRegisterWrite(flags, InstFlags.Rlist))
|
||||
{
|
||||
mask |= (ushort)encoding;
|
||||
}
|
||||
|
||||
if (flags.HasFlag(InstFlags.WBack) && HasWriteBackT32(name, encoding))
|
||||
{
|
||||
mask |= 1u << RegisterUtils.ExtractRn(encoding); // This is at the same bit position as A32.
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (flags.HasFlag(InstFlags.Rd))
|
||||
{
|
||||
mask |= 1u << RegisterUtils.ExtractRd(flags, encoding);
|
||||
}
|
||||
|
||||
if (flags.HasFlag(InstFlags.RdHi))
|
||||
{
|
||||
mask |= 1u << RegisterUtils.ExtractRdHi(encoding);
|
||||
}
|
||||
|
||||
if (IsRegisterWrite(flags, InstFlags.Rt) && IsRtWrite(name, encoding) && !IsR15RtEncodingSpecial(name, encoding))
|
||||
{
|
||||
mask |= 1u << RegisterUtils.ExtractRt(encoding);
|
||||
}
|
||||
|
||||
if (IsRegisterWrite(flags, InstFlags.Rt2) && IsRtWrite(name, encoding))
|
||||
{
|
||||
mask |= 1u << RegisterUtils.ExtractRt2(encoding);
|
||||
}
|
||||
|
||||
if (IsRegisterWrite(flags, InstFlags.Rlist))
|
||||
{
|
||||
mask |= (ushort)encoding;
|
||||
}
|
||||
|
||||
if (flags.HasFlag(InstFlags.WBack) && HasWriteBack(name, encoding))
|
||||
{
|
||||
mask |= 1u << RegisterUtils.ExtractRn(encoding);
|
||||
}
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
private static bool IsRtWrite(InstName name, uint encoding)
|
||||
{
|
||||
// Some instructions can move GPR to FP/SIMD or FP/SIMD to GPR depending on the encoding.
|
||||
// Detect those cases so that we can tell if we're actually doing a register write.
|
||||
|
||||
switch (name)
|
||||
{
|
||||
case InstName.VmovD:
|
||||
case InstName.VmovH:
|
||||
case InstName.VmovS:
|
||||
case InstName.VmovSs:
|
||||
return (encoding & (1u << 20)) != 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool HasWriteBack(InstName name, uint encoding)
|
||||
{
|
||||
if (IsLoadStoreMultiple(name))
|
||||
{
|
||||
return (encoding & (1u << 21)) != 0;
|
||||
}
|
||||
|
||||
if (IsVLDnVSTn(name))
|
||||
{
|
||||
return (encoding & 0xf) != RegisterUtils.PcRegister;
|
||||
}
|
||||
|
||||
bool w = (encoding & (1u << 21)) != 0;
|
||||
bool p = (encoding & (1u << 24)) != 0;
|
||||
|
||||
return !p || w;
|
||||
}
|
||||
|
||||
private static bool HasWriteBackT32(InstName name, uint encoding)
|
||||
{
|
||||
if (IsLoadStoreMultiple(name))
|
||||
{
|
||||
return (encoding & (1u << 21)) != 0;
|
||||
}
|
||||
|
||||
if (IsVLDnVSTn(name))
|
||||
{
|
||||
return (encoding & 0xf) != RegisterUtils.PcRegister;
|
||||
}
|
||||
|
||||
return (encoding & (1u << 8)) != 0;
|
||||
}
|
||||
|
||||
private static bool IsLoadStoreMultiple(InstName name)
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case InstName.Ldm:
|
||||
case InstName.Ldmda:
|
||||
case InstName.Ldmdb:
|
||||
case InstName.LdmE:
|
||||
case InstName.Ldmib:
|
||||
case InstName.LdmU:
|
||||
case InstName.Stm:
|
||||
case InstName.Stmda:
|
||||
case InstName.Stmdb:
|
||||
case InstName.Stmib:
|
||||
case InstName.StmU:
|
||||
case InstName.Fldmx:
|
||||
case InstName.Fstmx:
|
||||
case InstName.Vldm:
|
||||
case InstName.Vstm:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsVLDnVSTn(InstName name)
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case InstName.Vld11:
|
||||
case InstName.Vld1A:
|
||||
case InstName.Vld1M:
|
||||
case InstName.Vld21:
|
||||
case InstName.Vld2A:
|
||||
case InstName.Vld2M:
|
||||
case InstName.Vld31:
|
||||
case InstName.Vld3A:
|
||||
case InstName.Vld3M:
|
||||
case InstName.Vld41:
|
||||
case InstName.Vld4A:
|
||||
case InstName.Vld4M:
|
||||
case InstName.Vst11:
|
||||
case InstName.Vst1M:
|
||||
case InstName.Vst21:
|
||||
case InstName.Vst2M:
|
||||
case InstName.Vst31:
|
||||
case InstName.Vst3M:
|
||||
case InstName.Vst41:
|
||||
case InstName.Vst4M:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsR15RtEncodingSpecial(InstName name, uint encoding)
|
||||
{
|
||||
if (name == InstName.Vmrs)
|
||||
{
|
||||
return ((encoding >> 16) & 0xf) == 1;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsRegisterWrite(InstFlags flags, InstFlags testFlag)
|
||||
{
|
||||
return flags.HasFlag(testFlag) && !flags.HasFlag(InstFlags.ReadRd);
|
||||
}
|
||||
|
||||
private static bool IsBackwardsBranch(InstName name, uint encoding)
|
||||
{
|
||||
if (name == InstName.B)
|
||||
{
|
||||
return ImmUtils.ExtractSImm24Times4(encoding) < 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
1231
src/Ryujinx.Cpu/LightningJit/Arm32/IInstEmit.cs
Normal file
1231
src/Ryujinx.Cpu/LightningJit/Arm32/IInstEmit.cs
Normal file
File diff suppressed because it is too large
Load Diff
137
src/Ryujinx.Cpu/LightningJit/Arm32/ImmUtils.cs
Normal file
137
src/Ryujinx.Cpu/LightningJit/Arm32/ImmUtils.cs
Normal file
@ -0,0 +1,137 @@
|
||||
using System.Numerics;
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit.Arm32
|
||||
{
|
||||
static class ImmUtils
|
||||
{
|
||||
public static uint ExpandImm(uint imm)
|
||||
{
|
||||
return BitOperations.RotateRight((byte)imm, (int)(imm >> 8) * 2);
|
||||
}
|
||||
|
||||
public static bool ExpandedImmRotated(uint imm)
|
||||
{
|
||||
return (imm >> 8) != 0;
|
||||
}
|
||||
|
||||
public static uint ExpandImm(uint imm8, uint imm3, uint i)
|
||||
{
|
||||
uint imm = CombineImmU12(imm8, imm3, i);
|
||||
|
||||
if (imm >> 10 == 0)
|
||||
{
|
||||
return ((imm >> 8) & 3) switch
|
||||
{
|
||||
0 => (byte)imm,
|
||||
1 => (byte)imm * 0x00010001u,
|
||||
2 => (byte)imm * 0x01000100u,
|
||||
3 => (byte)imm * 0x01010101u,
|
||||
_ => 0,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
return BitOperations.RotateRight(0x80u | (byte)imm, (int)(imm >> 7));
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ExpandedImmRotated(uint imm8, uint imm3, uint i)
|
||||
{
|
||||
uint imm = CombineImmU12(imm8, imm3, i);
|
||||
|
||||
return (imm >> 7) != 0;
|
||||
}
|
||||
|
||||
public static uint CombineImmU5(uint imm2, uint imm3)
|
||||
{
|
||||
return imm2 | (imm3 << 2);
|
||||
}
|
||||
|
||||
public static uint CombineImmU5IImm4(uint i, uint imm4)
|
||||
{
|
||||
return i | (imm4 << 1);
|
||||
}
|
||||
|
||||
public static uint CombineImmU8(uint imm4l, uint imm4h)
|
||||
{
|
||||
return imm4l | (imm4h << 4);
|
||||
}
|
||||
|
||||
public static uint CombineImmU8(uint imm4, uint imm3, uint i)
|
||||
{
|
||||
return imm4 | (imm3 << 4) | (i << 7);
|
||||
}
|
||||
|
||||
public static uint CombineImmU12(uint imm8, uint imm3, uint i)
|
||||
{
|
||||
return imm8 | (imm3 << 8) | (i << 11);
|
||||
}
|
||||
|
||||
public static uint CombineImmU16(uint imm12, uint imm4)
|
||||
{
|
||||
return imm12 | (imm4 << 12);
|
||||
}
|
||||
|
||||
public static uint CombineImmU16(uint imm8, uint imm3, uint i, uint imm4)
|
||||
{
|
||||
return imm8 | (imm3 << 8) | (i << 11) | (imm4 << 12);
|
||||
}
|
||||
|
||||
public static int CombineSImm20Times2(uint imm11, uint imm6, uint j1, uint j2, uint s)
|
||||
{
|
||||
int imm32 = (int)(imm11 | (imm6 << 11) | (j1 << 17) | (j2 << 18) | (s << 19));
|
||||
|
||||
return (imm32 << 13) >> 12;
|
||||
}
|
||||
|
||||
public static int CombineSImm24Times2(uint imm11, uint imm10, uint j1, uint j2, uint s)
|
||||
{
|
||||
uint i1 = j1 ^ s ^ 1;
|
||||
uint i2 = j2 ^ s ^ 1;
|
||||
|
||||
int imm32 = (int)(imm11 | (imm10 << 11) | (i2 << 21) | (i1 << 22) | (s << 23));
|
||||
|
||||
return (imm32 << 8) >> 7;
|
||||
}
|
||||
|
||||
public static int CombineSImm24Times4(uint imm10L, uint imm10H, uint j1, uint j2, uint s)
|
||||
{
|
||||
uint i1 = j1 ^ s ^ 1;
|
||||
uint i2 = j2 ^ s ^ 1;
|
||||
|
||||
int imm32 = (int)(imm10L | (imm10H << 10) | (i2 << 20) | (i1 << 21) | (s << 22));
|
||||
|
||||
return (imm32 << 9) >> 7;
|
||||
}
|
||||
|
||||
public static uint CombineRegisterList(uint registerList, uint m)
|
||||
{
|
||||
return registerList | (m << 14);
|
||||
}
|
||||
|
||||
public static uint CombineRegisterList(uint registerList, uint m, uint p)
|
||||
{
|
||||
return registerList | (m << 14) | (p << 15);
|
||||
}
|
||||
|
||||
public static int ExtractSImm24Times4(uint encoding)
|
||||
{
|
||||
return (int)(encoding << 8) >> 6;
|
||||
}
|
||||
|
||||
public static int ExtractT16UImm5Times2(uint encoding)
|
||||
{
|
||||
return (int)(encoding >> 18) & 0x3e;
|
||||
}
|
||||
|
||||
public static int ExtractT16SImm8Times2(uint encoding)
|
||||
{
|
||||
return (int)(encoding << 24) >> 23;
|
||||
}
|
||||
|
||||
public static int ExtractT16SImm11Times2(uint encoding)
|
||||
{
|
||||
return (int)(encoding << 21) >> 20;
|
||||
}
|
||||
}
|
||||
}
|
2927
src/Ryujinx.Cpu/LightningJit/Arm32/InstDecoders.cs
Normal file
2927
src/Ryujinx.Cpu/LightningJit/Arm32/InstDecoders.cs
Normal file
File diff suppressed because it is too large
Load Diff
63
src/Ryujinx.Cpu/LightningJit/Arm32/InstFlags.cs
Normal file
63
src/Ryujinx.Cpu/LightningJit/Arm32/InstFlags.cs
Normal file
@ -0,0 +1,63 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit.Arm32
|
||||
{
|
||||
[Flags]
|
||||
enum InstFlags
|
||||
{
|
||||
None = 0,
|
||||
Cond = 1 << 0,
|
||||
Rd = 1 << 1,
|
||||
RdLo = 1 << 2,
|
||||
RdHi = 1 << 3,
|
||||
Rdn = 1 << 4,
|
||||
Dn = 1 << 5,
|
||||
Rt = 1 << 6,
|
||||
Rt2 = 1 << 7,
|
||||
Rlist = 1 << 8,
|
||||
Rd16 = 1 << 9,
|
||||
ReadRd = 1 << 10,
|
||||
WBack = 1 << 11,
|
||||
Thumb16 = 1 << 12,
|
||||
|
||||
RdnDn = Rdn | Dn,
|
||||
RdRd16 = Rd | Rd16,
|
||||
RtRt2 = Rt | Rt2,
|
||||
RdLoRdHi = RdLo | RdHi,
|
||||
RdLoHi = Rd | RdHi,
|
||||
RdRtRead = Rd | RtRead,
|
||||
RdRtReadRd16 = Rd | RtRead | Rd16,
|
||||
RdRt2Read = Rd | Rt2 | RtRead,
|
||||
RdRt2ReadRd16 = Rd | Rt2 | RtRead | Rd16,
|
||||
RtRd16 = Rt | Rd16,
|
||||
RtWBack = Rt | WBack,
|
||||
Rt2WBack = Rt2 | RtWBack,
|
||||
RtRead = Rt | ReadRd,
|
||||
RtReadRd16 = Rt | ReadRd | Rd16,
|
||||
Rt2Read = Rt2 | RtRead,
|
||||
RtReadWBack = RtRead | WBack,
|
||||
Rt2ReadWBack = Rt2 | RtReadWBack,
|
||||
RlistWBack = Rlist | WBack,
|
||||
RlistRead = Rlist | ReadRd,
|
||||
RlistReadWBack = Rlist | ReadRd | WBack,
|
||||
|
||||
CondRd = Cond | Rd,
|
||||
CondRdLoHi = Cond | Rd | RdHi,
|
||||
CondRt = Cond | Rt,
|
||||
CondRt2 = Cond | Rt | Rt2,
|
||||
CondRd16 = Cond | Rd | Rd16,
|
||||
CondWBack = Cond | WBack,
|
||||
CondRdRtRead = Cond | Rd | RtRead,
|
||||
CondRdRt2Read = Cond | Rd | Rt2 | RtRead,
|
||||
CondRtWBack = Cond | RtWBack,
|
||||
CondRt2WBack = Cond | Rt2 | RtWBack,
|
||||
CondRtRead = Cond | RtRead,
|
||||
CondRt2Read = Cond | Rt2 | RtRead,
|
||||
CondRtReadWBack = Cond | RtReadWBack,
|
||||
CondRt2ReadWBack = Cond | Rt2 | RtReadWBack,
|
||||
CondRlist = Cond | Rlist,
|
||||
CondRlistWBack = Cond | Rlist | WBack,
|
||||
CondRlistRead = Cond | Rlist | ReadRd,
|
||||
CondRlistReadWBack = Cond | Rlist | ReadRd | WBack,
|
||||
}
|
||||
}
|
20
src/Ryujinx.Cpu/LightningJit/Arm32/InstInfo.cs
Normal file
20
src/Ryujinx.Cpu/LightningJit/Arm32/InstInfo.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit.Arm32
|
||||
{
|
||||
readonly struct InstInfo
|
||||
{
|
||||
public readonly uint Encoding;
|
||||
public readonly InstName Name;
|
||||
public readonly Action<CodeGenContext, uint> EmitFunc;
|
||||
public readonly InstFlags Flags;
|
||||
|
||||
public InstInfo(uint encoding, InstName name, Action<CodeGenContext, uint> emitFunc, InstFlags flags)
|
||||
{
|
||||
Encoding = encoding;
|
||||
Name = name;
|
||||
EmitFunc = emitFunc;
|
||||
Flags = flags;
|
||||
}
|
||||
}
|
||||
}
|
79
src/Ryujinx.Cpu/LightningJit/Arm32/InstInfoForTable.cs
Normal file
79
src/Ryujinx.Cpu/LightningJit/Arm32/InstInfoForTable.cs
Normal file
@ -0,0 +1,79 @@
|
||||
using Ryujinx.Cpu.LightningJit.Table;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit.Arm32
|
||||
{
|
||||
readonly struct InstInfoForTable : IInstInfo
|
||||
{
|
||||
public uint Encoding { get; }
|
||||
public uint EncodingMask { get; }
|
||||
public InstEncoding[] Constraints { get; }
|
||||
public InstMeta Meta { get; }
|
||||
public IsaVersion Version => Meta.Version;
|
||||
public IsaFeature Feature => Meta.Feature;
|
||||
|
||||
public InstInfoForTable(
|
||||
uint encoding,
|
||||
uint encodingMask,
|
||||
InstEncoding[] constraints,
|
||||
InstName name,
|
||||
Action<CodeGenContext, uint> emitFunc,
|
||||
IsaVersion isaVersion,
|
||||
IsaFeature isaFeature,
|
||||
InstFlags flags)
|
||||
{
|
||||
Encoding = encoding;
|
||||
EncodingMask = encodingMask;
|
||||
Constraints = constraints;
|
||||
Meta = new(name, emitFunc, isaVersion, isaFeature, flags);
|
||||
}
|
||||
|
||||
public InstInfoForTable(
|
||||
uint encoding,
|
||||
uint encodingMask,
|
||||
InstEncoding[] constraints,
|
||||
InstName name,
|
||||
Action<CodeGenContext, uint> emitFunc,
|
||||
IsaVersion isaVersion,
|
||||
InstFlags flags) : this(encoding, encodingMask, constraints, name, emitFunc, isaVersion, IsaFeature.None, flags)
|
||||
{
|
||||
}
|
||||
|
||||
public InstInfoForTable(
|
||||
uint encoding,
|
||||
uint encodingMask,
|
||||
InstName name,
|
||||
Action<CodeGenContext, uint> emitFunc,
|
||||
IsaVersion isaVersion,
|
||||
IsaFeature isaFeature,
|
||||
InstFlags flags) : this(encoding, encodingMask, null, name, emitFunc, isaVersion, isaFeature, flags)
|
||||
{
|
||||
}
|
||||
|
||||
public InstInfoForTable(
|
||||
uint encoding,
|
||||
uint encodingMask,
|
||||
InstName name,
|
||||
Action<CodeGenContext, uint> emitFunc,
|
||||
IsaVersion isaVersion,
|
||||
InstFlags flags) : this(encoding, encodingMask, null, name, emitFunc, isaVersion, IsaFeature.None, flags)
|
||||
{
|
||||
}
|
||||
|
||||
public bool IsConstrained(uint encoding)
|
||||
{
|
||||
if (Constraints != null)
|
||||
{
|
||||
foreach (InstEncoding constraint in Constraints)
|
||||
{
|
||||
if ((encoding & constraint.EncodingMask) == constraint.Encoding)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
22
src/Ryujinx.Cpu/LightningJit/Arm32/InstMeta.cs
Normal file
22
src/Ryujinx.Cpu/LightningJit/Arm32/InstMeta.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit.Arm32
|
||||
{
|
||||
readonly struct InstMeta
|
||||
{
|
||||
public readonly InstName Name;
|
||||
public readonly Action<CodeGenContext, uint> EmitFunc;
|
||||
public readonly IsaVersion Version;
|
||||
public readonly IsaFeature Feature;
|
||||
public readonly InstFlags Flags;
|
||||
|
||||
public InstMeta(InstName name, Action<CodeGenContext, uint> emitFunc, IsaVersion isaVersion, IsaFeature isaFeature, InstFlags flags)
|
||||
{
|
||||
Name = name;
|
||||
EmitFunc = emitFunc;
|
||||
Version = isaVersion;
|
||||
Feature = isaFeature;
|
||||
Flags = flags;
|
||||
}
|
||||
}
|
||||
}
|
562
src/Ryujinx.Cpu/LightningJit/Arm32/InstName.cs
Normal file
562
src/Ryujinx.Cpu/LightningJit/Arm32/InstName.cs
Normal file
@ -0,0 +1,562 @@
|
||||
namespace Ryujinx.Cpu.LightningJit.Arm32
|
||||
{
|
||||
enum InstName
|
||||
{
|
||||
AdcI,
|
||||
AdcR,
|
||||
AdcRr,
|
||||
AddI,
|
||||
AddR,
|
||||
AddRr,
|
||||
AddSpI,
|
||||
AddSpR,
|
||||
Adr,
|
||||
Aesd,
|
||||
Aese,
|
||||
Aesimc,
|
||||
Aesmc,
|
||||
AndI,
|
||||
AndR,
|
||||
AndRr,
|
||||
B,
|
||||
Bfc,
|
||||
Bfi,
|
||||
BicI,
|
||||
BicR,
|
||||
BicRr,
|
||||
Bkpt,
|
||||
BlxR,
|
||||
BlI,
|
||||
Bx,
|
||||
Bxj,
|
||||
Cbnz,
|
||||
Clrbhb,
|
||||
Clrex,
|
||||
Clz,
|
||||
CmnI,
|
||||
CmnR,
|
||||
CmnRr,
|
||||
CmpI,
|
||||
CmpR,
|
||||
CmpRr,
|
||||
Cps,
|
||||
Crc32,
|
||||
Crc32c,
|
||||
Csdb,
|
||||
Dbg,
|
||||
Dcps1,
|
||||
Dcps2,
|
||||
Dcps3,
|
||||
Dmb,
|
||||
Dsb,
|
||||
EorI,
|
||||
EorR,
|
||||
EorRr,
|
||||
Eret,
|
||||
Esb,
|
||||
Fldmx,
|
||||
Fstmx,
|
||||
Hlt,
|
||||
Hvc,
|
||||
Isb,
|
||||
It,
|
||||
Lda,
|
||||
Ldab,
|
||||
Ldaex,
|
||||
Ldaexb,
|
||||
Ldaexd,
|
||||
Ldaexh,
|
||||
Ldah,
|
||||
LdcI,
|
||||
LdcL,
|
||||
Ldm,
|
||||
Ldmda,
|
||||
Ldmdb,
|
||||
Ldmib,
|
||||
LdmE,
|
||||
LdmU,
|
||||
Ldrbt,
|
||||
LdrbI,
|
||||
LdrbL,
|
||||
LdrbR,
|
||||
LdrdI,
|
||||
LdrdL,
|
||||
LdrdR,
|
||||
Ldrex,
|
||||
Ldrexb,
|
||||
Ldrexd,
|
||||
Ldrexh,
|
||||
Ldrht,
|
||||
LdrhI,
|
||||
LdrhL,
|
||||
LdrhR,
|
||||
Ldrsbt,
|
||||
LdrsbI,
|
||||
LdrsbL,
|
||||
LdrsbR,
|
||||
Ldrsht,
|
||||
LdrshI,
|
||||
LdrshL,
|
||||
LdrshR,
|
||||
Ldrt,
|
||||
LdrI,
|
||||
LdrL,
|
||||
LdrR,
|
||||
Mcr,
|
||||
Mcrr,
|
||||
Mla,
|
||||
Mls,
|
||||
Movt,
|
||||
MovI,
|
||||
MovR,
|
||||
MovRr,
|
||||
Mrc,
|
||||
Mrrc,
|
||||
Mrs,
|
||||
MrsBr,
|
||||
MsrBr,
|
||||
MsrI,
|
||||
MsrR,
|
||||
Mul,
|
||||
MvnI,
|
||||
MvnR,
|
||||
MvnRr,
|
||||
Nop,
|
||||
OrnI,
|
||||
OrnR,
|
||||
OrrI,
|
||||
OrrR,
|
||||
OrrRr,
|
||||
Pkh,
|
||||
PldI,
|
||||
PldL,
|
||||
PldR,
|
||||
PliI,
|
||||
PliR,
|
||||
Pop,
|
||||
Pssbb,
|
||||
Push,
|
||||
Qadd,
|
||||
Qadd16,
|
||||
Qadd8,
|
||||
Qasx,
|
||||
Qdadd,
|
||||
Qdsub,
|
||||
Qsax,
|
||||
Qsub,
|
||||
Qsub16,
|
||||
Qsub8,
|
||||
Rbit,
|
||||
Rev,
|
||||
Rev16,
|
||||
Revsh,
|
||||
Rfe,
|
||||
RsbI,
|
||||
RsbR,
|
||||
RsbRr,
|
||||
RscI,
|
||||
RscR,
|
||||
RscRr,
|
||||
Sadd16,
|
||||
Sadd8,
|
||||
Sasx,
|
||||
Sb,
|
||||
SbcI,
|
||||
SbcR,
|
||||
SbcRr,
|
||||
Sbfx,
|
||||
Sdiv,
|
||||
Sel,
|
||||
Setend,
|
||||
Setpan,
|
||||
Sev,
|
||||
Sevl,
|
||||
Sha1c,
|
||||
Sha1h,
|
||||
Sha1m,
|
||||
Sha1p,
|
||||
Sha1su0,
|
||||
Sha1su1,
|
||||
Sha256h,
|
||||
Sha256h2,
|
||||
Sha256su0,
|
||||
Sha256su1,
|
||||
Shadd16,
|
||||
Shadd8,
|
||||
Shasx,
|
||||
Shsax,
|
||||
Shsub16,
|
||||
Shsub8,
|
||||
Smc,
|
||||
Smlabb,
|
||||
Smlad,
|
||||
Smlal,
|
||||
Smlalbb,
|
||||
Smlald,
|
||||
Smlawb,
|
||||
Smlsd,
|
||||
Smlsld,
|
||||
Smmla,
|
||||
Smmls,
|
||||
Smmul,
|
||||
Smuad,
|
||||
Smulbb,
|
||||
Smull,
|
||||
Smulwb,
|
||||
Smusd,
|
||||
Srs,
|
||||
Ssat,
|
||||
Ssat16,
|
||||
Ssax,
|
||||
Ssbb,
|
||||
Ssub16,
|
||||
Ssub8,
|
||||
Stc,
|
||||
Stl,
|
||||
Stlb,
|
||||
Stlex,
|
||||
Stlexb,
|
||||
Stlexd,
|
||||
Stlexh,
|
||||
Stlh,
|
||||
Stm,
|
||||
Stmda,
|
||||
Stmdb,
|
||||
Stmib,
|
||||
StmU,
|
||||
Strbt,
|
||||
StrbI,
|
||||
StrbR,
|
||||
StrdI,
|
||||
StrdR,
|
||||
Strex,
|
||||
Strexb,
|
||||
Strexd,
|
||||
Strexh,
|
||||
Strht,
|
||||
StrhI,
|
||||
StrhR,
|
||||
Strt,
|
||||
StrI,
|
||||
StrR,
|
||||
SubI,
|
||||
SubR,
|
||||
SubRr,
|
||||
SubSpI,
|
||||
SubSpR,
|
||||
Svc,
|
||||
Sxtab,
|
||||
Sxtab16,
|
||||
Sxtah,
|
||||
Sxtb,
|
||||
Sxtb16,
|
||||
Sxth,
|
||||
Tbb,
|
||||
TeqI,
|
||||
TeqR,
|
||||
TeqRr,
|
||||
Tsb,
|
||||
TstI,
|
||||
TstR,
|
||||
TstRr,
|
||||
Uadd16,
|
||||
Uadd8,
|
||||
Uasx,
|
||||
Ubfx,
|
||||
Udf,
|
||||
Udiv,
|
||||
Uhadd16,
|
||||
Uhadd8,
|
||||
Uhasx,
|
||||
Uhsax,
|
||||
Uhsub16,
|
||||
Uhsub8,
|
||||
Umaal,
|
||||
Umlal,
|
||||
Umull,
|
||||
Uqadd16,
|
||||
Uqadd8,
|
||||
Uqasx,
|
||||
Uqsax,
|
||||
Uqsub16,
|
||||
Uqsub8,
|
||||
Usad8,
|
||||
Usada8,
|
||||
Usat,
|
||||
Usat16,
|
||||
Usax,
|
||||
Usub16,
|
||||
Usub8,
|
||||
Uxtab,
|
||||
Uxtab16,
|
||||
Uxtah,
|
||||
Uxtb,
|
||||
Uxtb16,
|
||||
Uxth,
|
||||
Vaba,
|
||||
Vabal,
|
||||
VabdlI,
|
||||
VabdF,
|
||||
VabdI,
|
||||
Vabs,
|
||||
Vacge,
|
||||
Vacgt,
|
||||
Vaddhn,
|
||||
Vaddl,
|
||||
Vaddw,
|
||||
VaddF,
|
||||
VaddI,
|
||||
VandR,
|
||||
VbicI,
|
||||
VbicR,
|
||||
Vbif,
|
||||
Vbit,
|
||||
Vbsl,
|
||||
Vcadd,
|
||||
VceqI,
|
||||
VceqR,
|
||||
VcgeI,
|
||||
VcgeR,
|
||||
VcgtI,
|
||||
VcgtR,
|
||||
VcleI,
|
||||
Vcls,
|
||||
VcltI,
|
||||
Vclz,
|
||||
Vcmla,
|
||||
VcmlaS,
|
||||
Vcmp,
|
||||
Vcmpe,
|
||||
Vcnt,
|
||||
VcvtaAsimd,
|
||||
VcvtaVfp,
|
||||
Vcvtb,
|
||||
VcvtbBfs,
|
||||
VcvtmAsimd,
|
||||
VcvtmVfp,
|
||||
VcvtnAsimd,
|
||||
VcvtnVfp,
|
||||
VcvtpAsimd,
|
||||
VcvtpVfp,
|
||||
VcvtrIv,
|
||||
Vcvtt,
|
||||
VcvttBfs,
|
||||
VcvtBfs,
|
||||
VcvtDs,
|
||||
VcvtHs,
|
||||
VcvtIs,
|
||||
VcvtIv,
|
||||
VcvtVi,
|
||||
VcvtXs,
|
||||
VcvtXv,
|
||||
Vdiv,
|
||||
Vdot,
|
||||
VdotS,
|
||||
VdupR,
|
||||
VdupS,
|
||||
Veor,
|
||||
Vext,
|
||||
Vfma,
|
||||
Vfmal,
|
||||
VfmalS,
|
||||
VfmaBf,
|
||||
VfmaBfs,
|
||||
Vfms,
|
||||
Vfmsl,
|
||||
VfmslS,
|
||||
Vfnma,
|
||||
Vfnms,
|
||||
Vhadd,
|
||||
Vhsub,
|
||||
Vins,
|
||||
Vjcvt,
|
||||
Vld11,
|
||||
Vld1A,
|
||||
Vld1M,
|
||||
Vld21,
|
||||
Vld2A,
|
||||
Vld2M,
|
||||
Vld31,
|
||||
Vld3A,
|
||||
Vld3M,
|
||||
Vld41,
|
||||
Vld4A,
|
||||
Vld4M,
|
||||
Vldm,
|
||||
VldrI,
|
||||
VldrL,
|
||||
Vmaxnm,
|
||||
VmaxF,
|
||||
VmaxI,
|
||||
Vminnm,
|
||||
VminF,
|
||||
VminI,
|
||||
VmlalI,
|
||||
VmlalS,
|
||||
VmlaF,
|
||||
VmlaI,
|
||||
VmlaS,
|
||||
VmlslI,
|
||||
VmlslS,
|
||||
VmlsF,
|
||||
VmlsI,
|
||||
VmlsS,
|
||||
Vmmla,
|
||||
Vmovl,
|
||||
Vmovn,
|
||||
Vmovx,
|
||||
VmovD,
|
||||
VmovH,
|
||||
VmovI,
|
||||
VmovR,
|
||||
VmovRs,
|
||||
VmovS,
|
||||
VmovSr,
|
||||
VmovSs,
|
||||
Vmrs,
|
||||
Vmsr,
|
||||
VmullI,
|
||||
VmullS,
|
||||
VmulF,
|
||||
VmulI,
|
||||
VmulS,
|
||||
VmvnI,
|
||||
VmvnR,
|
||||
Vneg,
|
||||
Vnmla,
|
||||
Vnmls,
|
||||
Vnmul,
|
||||
VornR,
|
||||
VorrI,
|
||||
VorrR,
|
||||
Vpadal,
|
||||
Vpaddl,
|
||||
VpaddF,
|
||||
VpaddI,
|
||||
VpmaxF,
|
||||
VpmaxI,
|
||||
VpminF,
|
||||
VpminI,
|
||||
Vqabs,
|
||||
Vqadd,
|
||||
Vqdmlal,
|
||||
Vqdmlsl,
|
||||
Vqdmulh,
|
||||
Vqdmull,
|
||||
Vqmovn,
|
||||
Vqneg,
|
||||
Vqrdmlah,
|
||||
Vqrdmlsh,
|
||||
Vqrdmulh,
|
||||
Vqrshl,
|
||||
Vqrshrn,
|
||||
VqshlI,
|
||||
VqshlR,
|
||||
Vqshrn,
|
||||
Vqsub,
|
||||
Vraddhn,
|
||||
Vrecpe,
|
||||
Vrecps,
|
||||
Vrev16,
|
||||
Vrev32,
|
||||
Vrev64,
|
||||
Vrhadd,
|
||||
VrintaAsimd,
|
||||
VrintaVfp,
|
||||
VrintmAsimd,
|
||||
VrintmVfp,
|
||||
VrintnAsimd,
|
||||
VrintnVfp,
|
||||
VrintpAsimd,
|
||||
VrintpVfp,
|
||||
VrintrVfp,
|
||||
VrintxAsimd,
|
||||
VrintxVfp,
|
||||
VrintzAsimd,
|
||||
VrintzVfp,
|
||||
Vrshl,
|
||||
Vrshr,
|
||||
Vrshrn,
|
||||
Vrsqrte,
|
||||
Vrsqrts,
|
||||
Vrsra,
|
||||
Vrsubhn,
|
||||
Vsdot,
|
||||
VsdotS,
|
||||
Vsel,
|
||||
Vshll,
|
||||
VshlI,
|
||||
VshlR,
|
||||
Vshr,
|
||||
Vshrn,
|
||||
Vsli,
|
||||
Vsmmla,
|
||||
Vsqrt,
|
||||
Vsra,
|
||||
Vsri,
|
||||
Vst11,
|
||||
Vst1M,
|
||||
Vst21,
|
||||
Vst2M,
|
||||
Vst31,
|
||||
Vst3M,
|
||||
Vst41,
|
||||
Vst4M,
|
||||
Vstm,
|
||||
Vstr,
|
||||
Vsubhn,
|
||||
Vsubl,
|
||||
Vsubw,
|
||||
VsubF,
|
||||
VsubI,
|
||||
VsudotS,
|
||||
Vswp,
|
||||
Vtbl,
|
||||
Vtrn,
|
||||
Vtst,
|
||||
Vudot,
|
||||
VudotS,
|
||||
Vummla,
|
||||
Vusdot,
|
||||
VusdotS,
|
||||
Vusmmla,
|
||||
Vuzp,
|
||||
Vzip,
|
||||
Wfe,
|
||||
Wfi,
|
||||
Yield,
|
||||
}
|
||||
|
||||
static class InstNameExtensions
|
||||
{
|
||||
public static bool IsCall(this InstName name)
|
||||
{
|
||||
return name == InstName.BlI || name == InstName.BlxR;
|
||||
}
|
||||
|
||||
public static bool IsSystem(this InstName name)
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case InstName.Mcr:
|
||||
case InstName.Mcrr:
|
||||
case InstName.Mrc:
|
||||
case InstName.Mrs:
|
||||
case InstName.MrsBr:
|
||||
case InstName.MsrBr:
|
||||
case InstName.MsrI:
|
||||
case InstName.MsrR:
|
||||
case InstName.Mrrc:
|
||||
case InstName.Svc:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsSystemOrCall(this InstName name)
|
||||
{
|
||||
return name.IsSystem() || name.IsCall();
|
||||
}
|
||||
}
|
||||
}
|
1194
src/Ryujinx.Cpu/LightningJit/Arm32/InstTableA32.cs
Normal file
1194
src/Ryujinx.Cpu/LightningJit/Arm32/InstTableA32.cs
Normal file
File diff suppressed because it is too large
Load Diff
146
src/Ryujinx.Cpu/LightningJit/Arm32/InstTableT16.cs
Normal file
146
src/Ryujinx.Cpu/LightningJit/Arm32/InstTableT16.cs
Normal file
@ -0,0 +1,146 @@
|
||||
using Ryujinx.Cpu.LightningJit.Table;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit.Arm32
|
||||
{
|
||||
static class InstTableT16<T> where T : IInstEmit
|
||||
{
|
||||
private static readonly InstTableLevel<InstInfoForTable> _table;
|
||||
|
||||
static InstTableT16()
|
||||
{
|
||||
InstEncoding[] rmRdndnConstraints = new InstEncoding[]
|
||||
{
|
||||
new(0x00680000, 0x00780000),
|
||||
new(0x00850000, 0x00870000),
|
||||
};
|
||||
|
||||
InstEncoding[] rmConstraints = new InstEncoding[]
|
||||
{
|
||||
new(0x00680000, 0x00780000),
|
||||
};
|
||||
|
||||
InstEncoding[] condCondConstraints = new InstEncoding[]
|
||||
{
|
||||
new(0x0E000000, 0x0F000000),
|
||||
new(0x0F000000, 0x0F000000),
|
||||
};
|
||||
|
||||
InstEncoding[] maskConstraints = new InstEncoding[]
|
||||
{
|
||||
new(0x00000000, 0x000F0000),
|
||||
};
|
||||
|
||||
InstEncoding[] opConstraints = new InstEncoding[]
|
||||
{
|
||||
new(0x18000000, 0x18000000),
|
||||
};
|
||||
|
||||
InstEncoding[] opOpOpOpConstraints = new InstEncoding[]
|
||||
{
|
||||
new(0x00000000, 0x03C00000),
|
||||
new(0x00400000, 0x03C00000),
|
||||
new(0x01400000, 0x03C00000),
|
||||
new(0x01800000, 0x03C00000),
|
||||
};
|
||||
|
||||
List<InstInfoForTable> insts = new()
|
||||
{
|
||||
new(0x41400000, 0xFFC00000, InstName.AdcR, T.AdcRT1, IsaVersion.v80, InstFlags.Rdn),
|
||||
new(0x1C000000, 0xFE000000, InstName.AddI, T.AddIT1, IsaVersion.v80, InstFlags.Rd),
|
||||
new(0x30000000, 0xF8000000, InstName.AddI, T.AddIT2, IsaVersion.v80, InstFlags.Rdn),
|
||||
new(0x18000000, 0xFE000000, InstName.AddR, T.AddRT1, IsaVersion.v80, InstFlags.Rd),
|
||||
new(0x44000000, 0xFF000000, rmRdndnConstraints, InstName.AddR, T.AddRT2, IsaVersion.v80, InstFlags.RdnDn),
|
||||
new(0xA8000000, 0xF8000000, InstName.AddSpI, T.AddSpIT1, IsaVersion.v80, InstFlags.RdRd16),
|
||||
new(0xB0000000, 0xFF800000, InstName.AddSpI, T.AddSpIT2, IsaVersion.v80, InstFlags.None),
|
||||
new(0x44680000, 0xFF780000, InstName.AddSpR, T.AddSpRT1, IsaVersion.v80, InstFlags.None),
|
||||
new(0x44850000, 0xFF870000, rmConstraints, InstName.AddSpR, T.AddSpRT2, IsaVersion.v80, InstFlags.None),
|
||||
new(0xA0000000, 0xF8000000, InstName.Adr, T.AdrT1, IsaVersion.v80, InstFlags.RdRd16),
|
||||
new(0x40000000, 0xFFC00000, InstName.AndR, T.AndRT1, IsaVersion.v80, InstFlags.Rdn),
|
||||
new(0xD0000000, 0xF0000000, condCondConstraints, InstName.B, T.BT1, IsaVersion.v80, InstFlags.Cond),
|
||||
new(0xE0000000, 0xF8000000, InstName.B, T.BT2, IsaVersion.v80, InstFlags.None),
|
||||
new(0x43800000, 0xFFC00000, InstName.BicR, T.BicRT1, IsaVersion.v80, InstFlags.Rdn),
|
||||
new(0xBE000000, 0xFF000000, InstName.Bkpt, T.BkptT1, IsaVersion.v80, InstFlags.None),
|
||||
new(0x47800000, 0xFF870000, InstName.BlxR, T.BlxRT1, IsaVersion.v80, InstFlags.None),
|
||||
new(0x47000000, 0xFF870000, InstName.Bx, T.BxT1, IsaVersion.v80, InstFlags.None),
|
||||
new(0xB1000000, 0xF5000000, InstName.Cbnz, T.CbnzT1, IsaVersion.v80, InstFlags.None),
|
||||
new(0x42C00000, 0xFFC00000, InstName.CmnR, T.CmnRT1, IsaVersion.v80, InstFlags.None),
|
||||
new(0x28000000, 0xF8000000, InstName.CmpI, T.CmpIT1, IsaVersion.v80, InstFlags.None),
|
||||
new(0x42800000, 0xFFC00000, InstName.CmpR, T.CmpRT1, IsaVersion.v80, InstFlags.None),
|
||||
new(0x45000000, 0xFF000000, InstName.CmpR, T.CmpRT2, IsaVersion.v80, InstFlags.None),
|
||||
new(0xB6600000, 0xFFE80000, InstName.Cps, T.CpsT1, IsaVersion.v80, InstFlags.None),
|
||||
new(0x40400000, 0xFFC00000, InstName.EorR, T.EorRT1, IsaVersion.v80, InstFlags.Rdn),
|
||||
new(0xBA800000, 0xFFC00000, InstName.Hlt, T.HltT1, IsaVersion.v80, InstFlags.None),
|
||||
new(0xBF000000, 0xFF000000, maskConstraints, InstName.It, T.ItT1, IsaVersion.v80, InstFlags.None),
|
||||
new(0xC8000000, 0xF8000000, InstName.Ldm, T.LdmT1, IsaVersion.v80, InstFlags.Rlist),
|
||||
new(0x78000000, 0xF8000000, InstName.LdrbI, T.LdrbIT1, IsaVersion.v80, InstFlags.Rt),
|
||||
new(0x5C000000, 0xFE000000, InstName.LdrbR, T.LdrbRT1, IsaVersion.v80, InstFlags.Rt),
|
||||
new(0x88000000, 0xF8000000, InstName.LdrhI, T.LdrhIT1, IsaVersion.v80, InstFlags.Rt),
|
||||
new(0x5A000000, 0xFE000000, InstName.LdrhR, T.LdrhRT1, IsaVersion.v80, InstFlags.Rt),
|
||||
new(0x56000000, 0xFE000000, InstName.LdrsbR, T.LdrsbRT1, IsaVersion.v80, InstFlags.Rt),
|
||||
new(0x5E000000, 0xFE000000, InstName.LdrshR, T.LdrshRT1, IsaVersion.v80, InstFlags.Rt),
|
||||
new(0x68000000, 0xF8000000, InstName.LdrI, T.LdrIT1, IsaVersion.v80, InstFlags.Rt),
|
||||
new(0x98000000, 0xF8000000, InstName.LdrI, T.LdrIT2, IsaVersion.v80, InstFlags.RtRd16),
|
||||
new(0x48000000, 0xF8000000, InstName.LdrL, T.LdrLT1, IsaVersion.v80, InstFlags.RtRd16),
|
||||
new(0x58000000, 0xFE000000, InstName.LdrR, T.LdrRT1, IsaVersion.v80, InstFlags.Rt),
|
||||
new(0x20000000, 0xF8000000, InstName.MovI, T.MovIT1, IsaVersion.v80, InstFlags.RdRd16),
|
||||
new(0x46000000, 0xFF000000, InstName.MovR, T.MovRT1, IsaVersion.v80, InstFlags.Rd),
|
||||
new(0x00000000, 0xE0000000, opConstraints, InstName.MovR, T.MovRT2, IsaVersion.v80, InstFlags.Rd),
|
||||
new(0x40000000, 0xFE000000, opOpOpOpConstraints, InstName.MovRr, T.MovRrT1, IsaVersion.v80, InstFlags.None),
|
||||
new(0x43400000, 0xFFC00000, InstName.Mul, T.MulT1, IsaVersion.v80, InstFlags.None),
|
||||
new(0x43C00000, 0xFFC00000, InstName.MvnR, T.MvnRT1, IsaVersion.v80, InstFlags.Rd),
|
||||
new(0xBF000000, 0xFFFF0000, InstName.Nop, T.NopT1, IsaVersion.v80, InstFlags.None),
|
||||
new(0x43000000, 0xFFC00000, InstName.OrrR, T.OrrRT1, IsaVersion.v80, InstFlags.Rdn),
|
||||
new(0xBC000000, 0xFE000000, InstName.Pop, T.PopT1, IsaVersion.v80, InstFlags.Rlist),
|
||||
new(0xB4000000, 0xFE000000, InstName.Push, T.PushT1, IsaVersion.v80, InstFlags.RlistRead),
|
||||
new(0xBA000000, 0xFFC00000, InstName.Rev, T.RevT1, IsaVersion.v80, InstFlags.Rd),
|
||||
new(0xBA400000, 0xFFC00000, InstName.Rev16, T.Rev16T1, IsaVersion.v80, InstFlags.Rd),
|
||||
new(0xBAC00000, 0xFFC00000, InstName.Revsh, T.RevshT1, IsaVersion.v80, InstFlags.Rd),
|
||||
new(0x42400000, 0xFFC00000, InstName.RsbI, T.RsbIT1, IsaVersion.v80, InstFlags.Rd),
|
||||
new(0x41800000, 0xFFC00000, InstName.SbcR, T.SbcRT1, IsaVersion.v80, InstFlags.Rdn),
|
||||
new(0xB6500000, 0xFFF70000, InstName.Setend, T.SetendT1, IsaVersion.v80, InstFlags.None),
|
||||
new(0xB6100000, 0xFFF70000, InstName.Setpan, T.SetpanT1, IsaVersion.v81, IsaFeature.FeatPan, InstFlags.None),
|
||||
new(0xBF400000, 0xFFFF0000, InstName.Sev, T.SevT1, IsaVersion.v80, InstFlags.None),
|
||||
new(0xBF500000, 0xFFFF0000, InstName.Sevl, T.SevlT1, IsaVersion.v80, InstFlags.None),
|
||||
new(0xC0000000, 0xF8000000, InstName.Stm, T.StmT1, IsaVersion.v80, InstFlags.RlistRead),
|
||||
new(0x70000000, 0xF8000000, InstName.StrbI, T.StrbIT1, IsaVersion.v80, InstFlags.RtRead),
|
||||
new(0x54000000, 0xFE000000, InstName.StrbR, T.StrbRT1, IsaVersion.v80, InstFlags.RtRead),
|
||||
new(0x80000000, 0xF8000000, InstName.StrhI, T.StrhIT1, IsaVersion.v80, InstFlags.RtRead),
|
||||
new(0x52000000, 0xFE000000, InstName.StrhR, T.StrhRT1, IsaVersion.v80, InstFlags.RtRead),
|
||||
new(0x60000000, 0xF8000000, InstName.StrI, T.StrIT1, IsaVersion.v80, InstFlags.RtRead),
|
||||
new(0x90000000, 0xF8000000, InstName.StrI, T.StrIT2, IsaVersion.v80, InstFlags.RtReadRd16),
|
||||
new(0x50000000, 0xFE000000, InstName.StrR, T.StrRT1, IsaVersion.v80, InstFlags.RtRead),
|
||||
new(0x1E000000, 0xFE000000, InstName.SubI, T.SubIT1, IsaVersion.v80, InstFlags.Rd),
|
||||
new(0x38000000, 0xF8000000, InstName.SubI, T.SubIT2, IsaVersion.v80, InstFlags.Rdn),
|
||||
new(0x1A000000, 0xFE000000, InstName.SubR, T.SubRT1, IsaVersion.v80, InstFlags.Rd),
|
||||
new(0xB0800000, 0xFF800000, InstName.SubSpI, T.SubSpIT1, IsaVersion.v80, InstFlags.None),
|
||||
new(0xDF000000, 0xFF000000, InstName.Svc, T.SvcT1, IsaVersion.v80, InstFlags.None),
|
||||
new(0xB2400000, 0xFFC00000, InstName.Sxtb, T.SxtbT1, IsaVersion.v80, InstFlags.Rd),
|
||||
new(0xB2000000, 0xFFC00000, InstName.Sxth, T.SxthT1, IsaVersion.v80, InstFlags.Rd),
|
||||
new(0x42000000, 0xFFC00000, InstName.TstR, T.TstRT1, IsaVersion.v80, InstFlags.None),
|
||||
new(0xDE000000, 0xFF000000, InstName.Udf, T.UdfT1, IsaVersion.v80, InstFlags.None),
|
||||
new(0xB2C00000, 0xFFC00000, InstName.Uxtb, T.UxtbT1, IsaVersion.v80, InstFlags.Rd),
|
||||
new(0xB2800000, 0xFFC00000, InstName.Uxth, T.UxthT1, IsaVersion.v80, InstFlags.Rd),
|
||||
new(0xBF200000, 0xFFFF0000, InstName.Wfe, T.WfeT1, IsaVersion.v80, InstFlags.None),
|
||||
new(0xBF300000, 0xFFFF0000, InstName.Wfi, T.WfiT1, IsaVersion.v80, InstFlags.None),
|
||||
new(0xBF100000, 0xFFFF0000, InstName.Yield, T.YieldT1, IsaVersion.v80, InstFlags.None),
|
||||
};
|
||||
|
||||
_table = new(insts);
|
||||
}
|
||||
|
||||
public static bool TryGetMeta(uint encoding, IsaVersion version, IsaFeature features, out InstMeta meta)
|
||||
{
|
||||
if (_table.TryFind(encoding, version, features, out InstInfoForTable info))
|
||||
{
|
||||
meta = info.Meta;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
meta = new(InstName.Udf, T.UdfA1, IsaVersion.v80, IsaFeature.None, InstFlags.None);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
1212
src/Ryujinx.Cpu/LightningJit/Arm32/InstTableT32.cs
Normal file
1212
src/Ryujinx.Cpu/LightningJit/Arm32/InstTableT32.cs
Normal file
File diff suppressed because it is too large
Load Diff
31
src/Ryujinx.Cpu/LightningJit/Arm32/MultiBlock.cs
Normal file
31
src/Ryujinx.Cpu/LightningJit/Arm32/MultiBlock.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit.Arm32
|
||||
{
|
||||
class MultiBlock
|
||||
{
|
||||
public readonly List<Block> Blocks;
|
||||
public readonly bool HasHostCall;
|
||||
public readonly bool IsTruncated;
|
||||
|
||||
public MultiBlock(List<Block> blocks)
|
||||
{
|
||||
Blocks = blocks;
|
||||
|
||||
Block block = blocks[0];
|
||||
|
||||
HasHostCall = block.HasHostCall;
|
||||
|
||||
for (int index = 1; index < blocks.Count; index++)
|
||||
{
|
||||
block = blocks[index];
|
||||
|
||||
HasHostCall |= block.HasHostCall;
|
||||
}
|
||||
|
||||
block = blocks[^1];
|
||||
|
||||
IsTruncated = block.IsTruncated;
|
||||
}
|
||||
}
|
||||
}
|
20
src/Ryujinx.Cpu/LightningJit/Arm32/PendingBranch.cs
Normal file
20
src/Ryujinx.Cpu/LightningJit/Arm32/PendingBranch.cs
Normal file
@ -0,0 +1,20 @@
|
||||
namespace Ryujinx.Cpu.LightningJit.Arm32
|
||||
{
|
||||
readonly struct PendingBranch
|
||||
{
|
||||
public readonly BranchType BranchType;
|
||||
public readonly uint TargetAddress;
|
||||
public readonly uint NextAddress;
|
||||
public readonly InstName Name;
|
||||
public readonly int WriterPointer;
|
||||
|
||||
public PendingBranch(BranchType branchType, uint targetAddress, uint nextAddress, InstName name, int writerPointer)
|
||||
{
|
||||
BranchType = branchType;
|
||||
TargetAddress = targetAddress;
|
||||
NextAddress = nextAddress;
|
||||
Name = name;
|
||||
WriterPointer = writerPointer;
|
||||
}
|
||||
}
|
||||
}
|
169
src/Ryujinx.Cpu/LightningJit/Arm32/RegisterAllocator.cs
Normal file
169
src/Ryujinx.Cpu/LightningJit/Arm32/RegisterAllocator.cs
Normal file
@ -0,0 +1,169 @@
|
||||
using Ryujinx.Cpu.LightningJit.CodeGen;
|
||||
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
|
||||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit.Arm32
|
||||
{
|
||||
class RegisterAllocator
|
||||
{
|
||||
public const int MaxTemps = 1;
|
||||
|
||||
private uint _gprMask;
|
||||
private uint _fpSimdMask;
|
||||
|
||||
public int FixedContextRegister { get; }
|
||||
public int FixedPageTableRegister { get; }
|
||||
|
||||
public uint UsedGprsMask { get; private set; }
|
||||
public uint UsedFpSimdMask { get; private set; }
|
||||
|
||||
public RegisterAllocator()
|
||||
{
|
||||
_gprMask = ushort.MaxValue;
|
||||
_fpSimdMask = ushort.MaxValue;
|
||||
|
||||
FixedContextRegister = AllocateTempRegisterWithPreferencing();
|
||||
FixedPageTableRegister = AllocateTempRegisterWithPreferencing();
|
||||
}
|
||||
|
||||
public void MarkGprAsUsed(int index)
|
||||
{
|
||||
UsedGprsMask |= 1u << index;
|
||||
}
|
||||
|
||||
public void MarkFpSimdAsUsed(int index)
|
||||
{
|
||||
UsedFpSimdMask |= 1u << index;
|
||||
}
|
||||
|
||||
public void MarkFpSimdRangeAsUsed(int index, int count)
|
||||
{
|
||||
UsedFpSimdMask |= (uint.MaxValue >> (32 - count)) << index;
|
||||
}
|
||||
|
||||
public Operand RemapGprRegister(int index)
|
||||
{
|
||||
MarkGprAsUsed(index);
|
||||
|
||||
return new Operand(OperandKind.Register, OperandType.I32, (ulong)index);
|
||||
}
|
||||
|
||||
public Operand RemapFpRegister(int index, bool isFP32)
|
||||
{
|
||||
MarkFpSimdAsUsed(index);
|
||||
|
||||
return new Operand(OperandKind.Register, isFP32 ? OperandType.FP32 : OperandType.FP64, (ulong)index);
|
||||
}
|
||||
|
||||
public Operand RemapSimdRegister(int index)
|
||||
{
|
||||
MarkFpSimdAsUsed(index);
|
||||
|
||||
return new Operand(OperandKind.Register, OperandType.V128, (ulong)index);
|
||||
}
|
||||
|
||||
public Operand RemapSimdRegister(int index, int count)
|
||||
{
|
||||
MarkFpSimdRangeAsUsed(index, count);
|
||||
|
||||
return new Operand(OperandKind.Register, OperandType.V128, (ulong)index);
|
||||
}
|
||||
|
||||
public void EnsureTempGprRegisters(int count)
|
||||
{
|
||||
if (count != 0)
|
||||
{
|
||||
Span<int> registers = stackalloc int[count];
|
||||
|
||||
for (int index = 0; index < count; index++)
|
||||
{
|
||||
registers[index] = AllocateTempGprRegister();
|
||||
}
|
||||
|
||||
for (int index = 0; index < count; index++)
|
||||
{
|
||||
FreeTempGprRegister(registers[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int AllocateTempGprRegister()
|
||||
{
|
||||
int index = AllocateTempRegister(ref _gprMask, AbiConstants.ReservedRegsMask);
|
||||
|
||||
MarkGprAsUsed(index);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
private int AllocateTempRegisterWithPreferencing()
|
||||
{
|
||||
int firstCalleeSaved = BitOperations.TrailingZeroCount(~_gprMask & AbiConstants.GprCalleeSavedRegsMask);
|
||||
if (firstCalleeSaved < 32)
|
||||
{
|
||||
uint regMask = 1u << firstCalleeSaved;
|
||||
if ((regMask & AbiConstants.ReservedRegsMask) == 0)
|
||||
{
|
||||
_gprMask |= regMask;
|
||||
|
||||
return firstCalleeSaved;
|
||||
}
|
||||
}
|
||||
|
||||
return AllocateTempRegister(ref _gprMask, AbiConstants.ReservedRegsMask);
|
||||
}
|
||||
|
||||
public int AllocateTempFpSimdRegister()
|
||||
{
|
||||
int index = AllocateTempRegister(ref _fpSimdMask, 0);
|
||||
|
||||
MarkFpSimdAsUsed(index);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
public ScopedRegister AllocateTempGprRegisterScoped()
|
||||
{
|
||||
return new(this, new(OperandKind.Register, OperandType.I32, (ulong)AllocateTempGprRegister()));
|
||||
}
|
||||
|
||||
public ScopedRegister AllocateTempFpRegisterScoped(bool isFP32)
|
||||
{
|
||||
return new(this, new(OperandKind.Register, isFP32 ? OperandType.FP32 : OperandType.FP64, (ulong)AllocateTempFpSimdRegister()));
|
||||
}
|
||||
|
||||
public ScopedRegister AllocateTempSimdRegisterScoped()
|
||||
{
|
||||
return new(this, new(OperandKind.Register, OperandType.V128, (ulong)AllocateTempFpSimdRegister()));
|
||||
}
|
||||
|
||||
public void FreeTempGprRegister(int index)
|
||||
{
|
||||
FreeTempRegister(ref _gprMask, index);
|
||||
}
|
||||
|
||||
public void FreeTempFpSimdRegister(int index)
|
||||
{
|
||||
FreeTempRegister(ref _fpSimdMask, index);
|
||||
}
|
||||
|
||||
private static int AllocateTempRegister(ref uint mask, uint reservedMask)
|
||||
{
|
||||
int index = BitOperations.TrailingZeroCount(~(mask | reservedMask));
|
||||
if (index == sizeof(uint) * 8)
|
||||
{
|
||||
throw new InvalidOperationException("No free registers.");
|
||||
}
|
||||
|
||||
mask |= 1u << index;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
private static void FreeTempRegister(ref uint mask, int index)
|
||||
{
|
||||
mask &= ~(1u << index);
|
||||
}
|
||||
}
|
||||
}
|
109
src/Ryujinx.Cpu/LightningJit/Arm32/RegisterUtils.cs
Normal file
109
src/Ryujinx.Cpu/LightningJit/Arm32/RegisterUtils.cs
Normal file
@ -0,0 +1,109 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit.Arm32
|
||||
{
|
||||
static class RegisterUtils
|
||||
{
|
||||
public const int SpRegister = 13;
|
||||
public const int LrRegister = 14;
|
||||
public const int PcRegister = 15;
|
||||
|
||||
private const int RmBit = 0;
|
||||
private const int RdRtBit = 12;
|
||||
private const int RdHiRnBit = 16;
|
||||
|
||||
private const int RdRtT16Bit = 16;
|
||||
private const int RdRtT16AltBit = 24;
|
||||
|
||||
private const int RdRt2RdHiT32Bit = 8;
|
||||
private const int RdT32AltBit = 0;
|
||||
private const int RtRdLoT32Bit = 12;
|
||||
|
||||
public static int ExtractRt(uint encoding)
|
||||
{
|
||||
return (int)(encoding >> RdRtBit) & 0xf;
|
||||
}
|
||||
|
||||
public static int ExtractRt2(uint encoding)
|
||||
{
|
||||
return (int)GetRt2((uint)ExtractRt(encoding));
|
||||
}
|
||||
|
||||
public static int ExtractRd(InstFlags flags, uint encoding)
|
||||
{
|
||||
return flags.HasFlag(InstFlags.Rd16) ? ExtractRn(encoding) : ExtractRd(encoding);
|
||||
}
|
||||
|
||||
public static int ExtractRd(uint encoding)
|
||||
{
|
||||
return (int)(encoding >> RdRtBit) & 0xf;
|
||||
}
|
||||
|
||||
public static int ExtractRdHi(uint encoding)
|
||||
{
|
||||
return (int)(encoding >> RdHiRnBit) & 0xf;
|
||||
}
|
||||
|
||||
public static int ExtractRn(uint encoding)
|
||||
{
|
||||
return (int)(encoding >> RdHiRnBit) & 0xf;
|
||||
}
|
||||
|
||||
public static int ExtractRm(uint encoding)
|
||||
{
|
||||
return (int)(encoding >> RmBit) & 0xf;
|
||||
}
|
||||
|
||||
public static uint GetRt2(uint rt)
|
||||
{
|
||||
return Math.Min(rt + 1, PcRegister);
|
||||
}
|
||||
|
||||
public static int ExtractRdn(InstFlags flags, uint encoding)
|
||||
{
|
||||
if (flags.HasFlag(InstFlags.Dn))
|
||||
{
|
||||
return ((int)(encoding >> RdRtT16Bit) & 7) | (int)((encoding >> 4) & 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ExtractRdT16(flags, encoding);
|
||||
}
|
||||
}
|
||||
|
||||
public static int ExtractRdT16(InstFlags flags, uint encoding)
|
||||
{
|
||||
return flags.HasFlag(InstFlags.Rd16) ? (int)(encoding >> RdRtT16AltBit) & 7 : (int)(encoding >> RdRtT16Bit) & 7;
|
||||
}
|
||||
|
||||
public static int ExtractRtT16(InstFlags flags, uint encoding)
|
||||
{
|
||||
return flags.HasFlag(InstFlags.Rd16) ? (int)(encoding >> RdRtT16AltBit) & 7 : (int)(encoding >> RdRtT16Bit) & 7;
|
||||
}
|
||||
|
||||
public static int ExtractRdT32(InstFlags flags, uint encoding)
|
||||
{
|
||||
return flags.HasFlag(InstFlags.Rd16) ? (int)(encoding >> RdT32AltBit) & 0xf : (int)(encoding >> RdRt2RdHiT32Bit) & 0xf;
|
||||
}
|
||||
|
||||
public static int ExtractRdLoT32(uint encoding)
|
||||
{
|
||||
return (int)(encoding >> RtRdLoT32Bit) & 0xf;
|
||||
}
|
||||
|
||||
public static int ExtractRdHiT32(uint encoding)
|
||||
{
|
||||
return (int)(encoding >> RdRt2RdHiT32Bit) & 0xf;
|
||||
}
|
||||
|
||||
public static int ExtractRtT32(uint encoding)
|
||||
{
|
||||
return (int)(encoding >> RtRdLoT32Bit) & 0xf;
|
||||
}
|
||||
|
||||
public static int ExtractRt2T32(uint encoding)
|
||||
{
|
||||
return (int)(encoding >> RdRt2RdHiT32Bit) & 0xf;
|
||||
}
|
||||
}
|
||||
}
|
39
src/Ryujinx.Cpu/LightningJit/Arm32/ScopedRegister.cs
Normal file
39
src/Ryujinx.Cpu/LightningJit/Arm32/ScopedRegister.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using Ryujinx.Cpu.LightningJit.CodeGen;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit.Arm32
|
||||
{
|
||||
readonly struct ScopedRegister : IDisposable
|
||||
{
|
||||
private readonly RegisterAllocator _registerAllocator;
|
||||
private readonly Operand _operand;
|
||||
private readonly bool _isAllocated;
|
||||
|
||||
public readonly Operand Operand => _operand;
|
||||
public readonly bool IsAllocated => _isAllocated;
|
||||
|
||||
public ScopedRegister(RegisterAllocator registerAllocator, Operand operand, bool isAllocated = true)
|
||||
{
|
||||
_registerAllocator = registerAllocator;
|
||||
_operand = operand;
|
||||
_isAllocated = isAllocated;
|
||||
}
|
||||
|
||||
public readonly void Dispose()
|
||||
{
|
||||
if (!_isAllocated)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_operand.Type.IsInteger())
|
||||
{
|
||||
_registerAllocator.FreeTempGprRegister(_operand.AsInt32());
|
||||
}
|
||||
else
|
||||
{
|
||||
_registerAllocator.FreeTempFpSimdRegister(_operand.AsInt32());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
789
src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/Compiler.cs
Normal file
789
src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/Compiler.cs
Normal file
@ -0,0 +1,789 @@
|
||||
using ARMeilleure.Common;
|
||||
using ARMeilleure.Memory;
|
||||
using Ryujinx.Cpu.LightningJit.CodeGen;
|
||||
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||
{
|
||||
static class Compiler
|
||||
{
|
||||
public const uint UsableGprsMask = 0x7fff;
|
||||
public const uint UsableFpSimdMask = 0xffff;
|
||||
public const uint UsablePStateMask = 0xf0000000;
|
||||
|
||||
private const int Encodable26BitsOffsetLimit = 0x2000000;
|
||||
|
||||
private readonly struct Context
|
||||
{
|
||||
public readonly CodeWriter Writer;
|
||||
public readonly RegisterAllocator RegisterAllocator;
|
||||
public readonly MemoryManagerType MemoryManagerType;
|
||||
public readonly TailMerger TailMerger;
|
||||
public readonly AddressTable<ulong> FuncTable;
|
||||
public readonly IntPtr DispatchStubPointer;
|
||||
|
||||
private readonly RegisterSaveRestore _registerSaveRestore;
|
||||
private readonly IntPtr _pageTablePointer;
|
||||
|
||||
public Context(
|
||||
CodeWriter writer,
|
||||
RegisterAllocator registerAllocator,
|
||||
MemoryManagerType mmType,
|
||||
TailMerger tailMerger,
|
||||
AddressTable<ulong> funcTable,
|
||||
RegisterSaveRestore registerSaveRestore,
|
||||
IntPtr dispatchStubPointer,
|
||||
IntPtr pageTablePointer)
|
||||
{
|
||||
Writer = writer;
|
||||
RegisterAllocator = registerAllocator;
|
||||
MemoryManagerType = mmType;
|
||||
TailMerger = tailMerger;
|
||||
FuncTable = funcTable;
|
||||
_registerSaveRestore = registerSaveRestore;
|
||||
DispatchStubPointer = dispatchStubPointer;
|
||||
_pageTablePointer = pageTablePointer;
|
||||
}
|
||||
|
||||
public readonly int GetReservedStackOffset()
|
||||
{
|
||||
return _registerSaveRestore.GetReservedStackOffset();
|
||||
}
|
||||
|
||||
public readonly void WritePrologueAt(int instructionPointer)
|
||||
{
|
||||
CodeWriter writer = new();
|
||||
Assembler asm = new(writer);
|
||||
|
||||
_registerSaveRestore.WritePrologue(ref asm);
|
||||
|
||||
// If needed, set up the fixed registers with the pointers we will use.
|
||||
// First one is the context pointer (passed as first argument),
|
||||
// second one is the page table or address space base, it is at a fixed memory location and considered constant.
|
||||
|
||||
if (RegisterAllocator.FixedContextRegister != 0)
|
||||
{
|
||||
asm.Mov(Register(RegisterAllocator.FixedContextRegister), Register(0));
|
||||
}
|
||||
|
||||
asm.Mov(Register(RegisterAllocator.FixedPageTableRegister), (ulong)_pageTablePointer);
|
||||
|
||||
LoadFromContext(ref asm);
|
||||
|
||||
// Write the prologue at the specified position in our writer.
|
||||
Writer.WriteInstructionsAt(instructionPointer, writer);
|
||||
}
|
||||
|
||||
public readonly void WriteEpilogueWithoutContext()
|
||||
{
|
||||
Assembler asm = new(Writer);
|
||||
|
||||
_registerSaveRestore.WriteEpilogue(ref asm);
|
||||
}
|
||||
|
||||
public void LoadFromContext()
|
||||
{
|
||||
Assembler asm = new(Writer);
|
||||
|
||||
LoadFromContext(ref asm);
|
||||
}
|
||||
|
||||
private void LoadFromContext(ref Assembler asm)
|
||||
{
|
||||
LoadGprFromContext(ref asm, RegisterAllocator.UsedGprsMask & UsableGprsMask, NativeContextOffsets.GprBaseOffset);
|
||||
LoadFpSimdFromContext(ref asm, RegisterAllocator.UsedFpSimdMask & UsableFpSimdMask, NativeContextOffsets.FpSimdBaseOffset);
|
||||
LoadPStateFromContext(ref asm, UsablePStateMask, NativeContextOffsets.FlagsBaseOffset);
|
||||
}
|
||||
|
||||
public void StoreToContext()
|
||||
{
|
||||
Assembler asm = new(Writer);
|
||||
|
||||
StoreToContext(ref asm);
|
||||
}
|
||||
|
||||
private void StoreToContext(ref Assembler asm)
|
||||
{
|
||||
StoreGprToContext(ref asm, RegisterAllocator.UsedGprsMask & UsableGprsMask, NativeContextOffsets.GprBaseOffset);
|
||||
StoreFpSimdToContext(ref asm, RegisterAllocator.UsedFpSimdMask & UsableFpSimdMask, NativeContextOffsets.FpSimdBaseOffset);
|
||||
StorePStateToContext(ref asm, UsablePStateMask, NativeContextOffsets.FlagsBaseOffset);
|
||||
}
|
||||
|
||||
private void LoadGprFromContext(ref Assembler asm, uint mask, int baseOffset)
|
||||
{
|
||||
Operand contextPtr = Register(RegisterAllocator.FixedContextRegister);
|
||||
|
||||
while (mask != 0)
|
||||
{
|
||||
int reg = BitOperations.TrailingZeroCount(mask);
|
||||
int offset = baseOffset + reg * 8;
|
||||
|
||||
if (reg < 31 && (mask & (2u << reg)) != 0 && offset < RegisterSaveRestore.Encodable9BitsOffsetLimit)
|
||||
{
|
||||
mask &= ~(3u << reg);
|
||||
|
||||
asm.LdpRiUn(Register(reg), Register(reg + 1), contextPtr, offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
mask &= ~(1u << reg);
|
||||
|
||||
asm.LdrRiUn(Register(reg), contextPtr, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadFpSimdFromContext(ref Assembler asm, uint mask, int baseOffset)
|
||||
{
|
||||
Operand contextPtr = Register(RegisterAllocator.FixedContextRegister);
|
||||
|
||||
while (mask != 0)
|
||||
{
|
||||
int reg = BitOperations.TrailingZeroCount(mask);
|
||||
int offset = baseOffset + reg * 16;
|
||||
|
||||
mask &= ~(1u << reg);
|
||||
|
||||
asm.LdrRiUn(Register(reg, OperandType.V128), contextPtr, offset);
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadPStateFromContext(ref Assembler asm, uint mask, int baseOffset)
|
||||
{
|
||||
if (mask == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Operand contextPtr = Register(RegisterAllocator.FixedContextRegister);
|
||||
|
||||
using ScopedRegister tempRegister = RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
|
||||
asm.LdrRiUn(tempRegister.Operand, contextPtr, baseOffset);
|
||||
asm.MsrNzcv(tempRegister.Operand);
|
||||
}
|
||||
|
||||
private void StoreGprToContext(ref Assembler asm, uint mask, int baseOffset)
|
||||
{
|
||||
Operand contextPtr = Register(RegisterAllocator.FixedContextRegister);
|
||||
|
||||
while (mask != 0)
|
||||
{
|
||||
int reg = BitOperations.TrailingZeroCount(mask);
|
||||
int offset = baseOffset + reg * 8;
|
||||
|
||||
if (reg < 31 && (mask & (2u << reg)) != 0 && offset < RegisterSaveRestore.Encodable9BitsOffsetLimit)
|
||||
{
|
||||
mask &= ~(3u << reg);
|
||||
|
||||
asm.StpRiUn(Register(reg), Register(reg + 1), contextPtr, offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
mask &= ~(1u << reg);
|
||||
|
||||
asm.StrRiUn(Register(reg), contextPtr, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void StoreFpSimdToContext(ref Assembler asm, uint mask, int baseOffset)
|
||||
{
|
||||
Operand contextPtr = Register(RegisterAllocator.FixedContextRegister);
|
||||
|
||||
while (mask != 0)
|
||||
{
|
||||
int reg = BitOperations.TrailingZeroCount(mask);
|
||||
int offset = baseOffset + reg * 16;
|
||||
|
||||
mask &= ~(1u << reg);
|
||||
|
||||
asm.StrRiUn(Register(reg, OperandType.V128), contextPtr, offset);
|
||||
}
|
||||
}
|
||||
|
||||
private void StorePStateToContext(ref Assembler asm, uint mask, int baseOffset)
|
||||
{
|
||||
if (mask == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Operand contextPtr = Register(RegisterAllocator.FixedContextRegister);
|
||||
|
||||
using ScopedRegister tempRegister = RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
using ScopedRegister tempRegister2 = RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
|
||||
asm.LdrRiUn(tempRegister.Operand, contextPtr, baseOffset);
|
||||
asm.MrsNzcv(tempRegister2.Operand);
|
||||
asm.And(tempRegister.Operand, tempRegister.Operand, InstEmitCommon.Const(0xfffffff));
|
||||
asm.Orr(tempRegister.Operand, tempRegister.Operand, tempRegister2.Operand);
|
||||
asm.StrRiUn(tempRegister.Operand, contextPtr, baseOffset);
|
||||
}
|
||||
}
|
||||
|
||||
public static CompiledFunction Compile(CpuPreset cpuPreset, IMemoryManager memoryManager, ulong address, AddressTable<ulong> funcTable, IntPtr dispatchStubPtr, bool isThumb)
|
||||
{
|
||||
MultiBlock multiBlock = Decoder<InstEmit>.DecodeMulti(cpuPreset, memoryManager, address, isThumb);
|
||||
|
||||
Dictionary<ulong, int> targets = new();
|
||||
|
||||
CodeWriter writer = new();
|
||||
RegisterAllocator regAlloc = new();
|
||||
Assembler asm = new(writer);
|
||||
CodeGenContext cgContext = new(writer, asm, regAlloc, memoryManager.Type, isThumb);
|
||||
ArmCondition lastCondition = ArmCondition.Al;
|
||||
int lastConditionIp = 0;
|
||||
|
||||
// Required for load/store to context.
|
||||
regAlloc.EnsureTempGprRegisters(2);
|
||||
|
||||
ulong pc = address;
|
||||
|
||||
for (int blockIndex = 0; blockIndex < multiBlock.Blocks.Count; blockIndex++)
|
||||
{
|
||||
Block block = multiBlock.Blocks[blockIndex];
|
||||
|
||||
Debug.Assert(block.Address == pc);
|
||||
|
||||
targets.Add(pc, writer.InstructionPointer);
|
||||
|
||||
for (int index = 0; index < block.Instructions.Count; index++)
|
||||
{
|
||||
InstInfo instInfo = block.Instructions[index];
|
||||
|
||||
if (index < block.Instructions.Count - 1)
|
||||
{
|
||||
cgContext.SetNextInstruction(block.Instructions[index + 1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
cgContext.SetNextInstruction(default);
|
||||
}
|
||||
|
||||
SetConditionalStart(cgContext, ref lastCondition, ref lastConditionIp, instInfo.Name, instInfo.Flags, instInfo.Encoding);
|
||||
|
||||
if (block.IsLoopEnd && index == block.Instructions.Count - 1)
|
||||
{
|
||||
// If this is a loop, the code might run for a long time uninterrupted.
|
||||
// We insert a "sync point" here to ensure the loop can be interrupted if needed.
|
||||
|
||||
cgContext.AddPendingSyncPoint();
|
||||
|
||||
asm.B(0);
|
||||
}
|
||||
|
||||
cgContext.SetPc((uint)pc);
|
||||
|
||||
instInfo.EmitFunc(cgContext, instInfo.Encoding);
|
||||
|
||||
if (cgContext.ConsumeNzcvModified())
|
||||
{
|
||||
ForceConditionalEnd(cgContext, ref lastCondition, lastConditionIp);
|
||||
}
|
||||
|
||||
cgContext.UpdateItState();
|
||||
|
||||
pc += instInfo.Flags.HasFlag(InstFlags.Thumb16) ? 2UL : 4UL;
|
||||
}
|
||||
|
||||
if (Decoder<InstEmit>.WritesToPC(block.Instructions[^1].Encoding, block.Instructions[^1].Name, block.Instructions[^1].Flags, block.IsThumb))
|
||||
{
|
||||
// If the block ends with a PC register write, then we have a branch from register.
|
||||
|
||||
InstEmitCommon.SetThumbFlag(cgContext, regAlloc.RemapGprRegister(RegisterUtils.PcRegister));
|
||||
|
||||
cgContext.AddPendingIndirectBranch(block.Instructions[^1].Name, RegisterUtils.PcRegister);
|
||||
|
||||
asm.B(0);
|
||||
}
|
||||
|
||||
ForceConditionalEnd(cgContext, ref lastCondition, lastConditionIp);
|
||||
}
|
||||
|
||||
RegisterSaveRestore rsr = new(
|
||||
regAlloc.UsedGprsMask & AbiConstants.GprCalleeSavedRegsMask,
|
||||
regAlloc.UsedFpSimdMask & AbiConstants.FpSimdCalleeSavedRegsMask,
|
||||
OperandType.FP64,
|
||||
multiBlock.HasHostCall,
|
||||
multiBlock.HasHostCall ? CalculateStackSizeForCallSpill(regAlloc.UsedGprsMask, regAlloc.UsedFpSimdMask, UsablePStateMask) : 0);
|
||||
|
||||
TailMerger tailMerger = new();
|
||||
|
||||
Context context = new(writer, regAlloc, memoryManager.Type, tailMerger, funcTable, rsr, dispatchStubPtr, memoryManager.PageTablePointer);
|
||||
|
||||
InstInfo lastInstruction = multiBlock.Blocks[^1].Instructions[^1];
|
||||
bool lastInstIsConditional = GetCondition(lastInstruction, isThumb) != ArmCondition.Al;
|
||||
|
||||
if (multiBlock.IsTruncated || lastInstIsConditional || lastInstruction.Name.IsCall() || IsConditionalBranch(lastInstruction))
|
||||
{
|
||||
WriteTailCallConstant(context, ref asm, (uint)pc);
|
||||
}
|
||||
|
||||
IEnumerable<PendingBranch> pendingBranches = cgContext.GetPendingBranches();
|
||||
|
||||
foreach (PendingBranch pendingBranch in pendingBranches)
|
||||
{
|
||||
RewriteBranchInstructionWithTarget(context, pendingBranch, targets);
|
||||
}
|
||||
|
||||
tailMerger.WriteReturn(writer, context.WriteEpilogueWithoutContext);
|
||||
|
||||
context.WritePrologueAt(0);
|
||||
|
||||
return new(writer.AsByteSpan(), (int)(pc - address));
|
||||
}
|
||||
|
||||
private static int CalculateStackSizeForCallSpill(uint gprUseMask, uint fpSimdUseMask, uint pStateUseMask)
|
||||
{
|
||||
// Note that we don't discard callee saved FP/SIMD register because only the lower 64 bits is callee saved,
|
||||
// so if the function is using the full register, that won't be enough.
|
||||
// We could do better, but it's likely not worth it since this case happens very rarely in practice.
|
||||
|
||||
return BitOperations.PopCount(gprUseMask & ~AbiConstants.GprCalleeSavedRegsMask) * 8 +
|
||||
BitOperations.PopCount(fpSimdUseMask) * 16 +
|
||||
(pStateUseMask != 0 ? 8 : 0);
|
||||
}
|
||||
|
||||
private static void SetConditionalStart(
|
||||
CodeGenContext context,
|
||||
ref ArmCondition condition,
|
||||
ref int instructionPointer,
|
||||
InstName name,
|
||||
InstFlags flags,
|
||||
uint encoding)
|
||||
{
|
||||
if (!context.ConsumeItCondition(out ArmCondition currentCond))
|
||||
{
|
||||
currentCond = GetCondition(name, flags, encoding, context.IsThumb);
|
||||
}
|
||||
|
||||
if (currentCond != condition)
|
||||
{
|
||||
WriteConditionalEnd(context, condition, instructionPointer);
|
||||
|
||||
condition = currentCond;
|
||||
|
||||
if (currentCond != ArmCondition.Al)
|
||||
{
|
||||
instructionPointer = context.CodeWriter.InstructionPointer;
|
||||
context.Arm64Assembler.B(currentCond.Invert(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsConditionalBranch(in InstInfo instInfo)
|
||||
{
|
||||
return instInfo.Name == InstName.B && (ArmCondition)(instInfo.Encoding >> 28) != ArmCondition.Al;
|
||||
}
|
||||
|
||||
private static ArmCondition GetCondition(in InstInfo instInfo, bool isThumb)
|
||||
{
|
||||
return GetCondition(instInfo.Name, instInfo.Flags, instInfo.Encoding, isThumb);
|
||||
}
|
||||
|
||||
private static ArmCondition GetCondition(InstName name, InstFlags flags, uint encoding, bool isThumb)
|
||||
{
|
||||
// For branch, we handle conditional execution on the instruction itself.
|
||||
bool hasCond = flags.HasFlag(InstFlags.Cond) && !CanHandleConditionalInstruction(name, encoding, isThumb);
|
||||
|
||||
return hasCond ? (ArmCondition)(encoding >> 28) : ArmCondition.Al;
|
||||
}
|
||||
|
||||
private static bool CanHandleConditionalInstruction(InstName name, uint encoding, bool isThumb)
|
||||
{
|
||||
if (name == InstName.B)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// We can use CSEL for conditional MOV from registers, as long the instruction is not setting flags.
|
||||
// We don't handle thumb right now because the condition comes from the IT block which would be more complicated to handle.
|
||||
if (name == InstName.MovR && !isThumb && (encoding & (1u << 20)) == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void ForceConditionalEnd(CodeGenContext context, ref ArmCondition condition, int instructionPointer)
|
||||
{
|
||||
WriteConditionalEnd(context, condition, instructionPointer);
|
||||
|
||||
condition = ArmCondition.Al;
|
||||
}
|
||||
|
||||
private static void WriteConditionalEnd(CodeGenContext context, ArmCondition condition, int instructionPointer)
|
||||
{
|
||||
if (condition != ArmCondition.Al)
|
||||
{
|
||||
int delta = context.CodeWriter.InstructionPointer - instructionPointer;
|
||||
uint branchInst = context.CodeWriter.ReadInstructionAt(instructionPointer) | (((uint)delta & 0x7ffff) << 5);
|
||||
Debug.Assert((int)((branchInst & ~0x1fu) << 8) >> 11 == delta * 4);
|
||||
|
||||
context.CodeWriter.WriteInstructionAt(instructionPointer, branchInst);
|
||||
}
|
||||
}
|
||||
|
||||
private static void RewriteBranchInstructionWithTarget(in Context context, in PendingBranch pendingBranch, Dictionary<ulong, int> targets)
|
||||
{
|
||||
switch (pendingBranch.BranchType)
|
||||
{
|
||||
case BranchType.Branch:
|
||||
RewriteBranchInstructionWithTarget(context, pendingBranch.Name, pendingBranch.TargetAddress, pendingBranch.WriterPointer, targets);
|
||||
break;
|
||||
case BranchType.Call:
|
||||
RewriteCallInstructionWithTarget(context, pendingBranch.TargetAddress, pendingBranch.NextAddress, pendingBranch.WriterPointer);
|
||||
break;
|
||||
case BranchType.IndirectBranch:
|
||||
RewriteIndirectBranchInstructionWithTarget(context, pendingBranch.Name, pendingBranch.TargetAddress, pendingBranch.WriterPointer);
|
||||
break;
|
||||
case BranchType.TableBranchByte:
|
||||
case BranchType.TableBranchHalfword:
|
||||
RewriteTableBranchInstructionWithTarget(
|
||||
context,
|
||||
pendingBranch.BranchType == BranchType.TableBranchHalfword,
|
||||
pendingBranch.TargetAddress,
|
||||
pendingBranch.NextAddress,
|
||||
pendingBranch.WriterPointer);
|
||||
break;
|
||||
case BranchType.IndirectCall:
|
||||
RewriteIndirectCallInstructionWithTarget(context, pendingBranch.TargetAddress, pendingBranch.NextAddress, pendingBranch.WriterPointer);
|
||||
break;
|
||||
case BranchType.SyncPoint:
|
||||
case BranchType.SoftwareInterrupt:
|
||||
case BranchType.ReadCntpct:
|
||||
RewriteHostCall(context, pendingBranch.Name, pendingBranch.BranchType, pendingBranch.TargetAddress, pendingBranch.NextAddress, pendingBranch.WriterPointer);
|
||||
break;
|
||||
default:
|
||||
Debug.Fail($"Invalid branch type '{pendingBranch.BranchType}'");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void RewriteBranchInstructionWithTarget(in Context context, InstName name, uint targetAddress, int branchIndex, Dictionary<ulong, int> targets)
|
||||
{
|
||||
CodeWriter writer = context.Writer;
|
||||
Assembler asm = new(writer);
|
||||
|
||||
int delta;
|
||||
int targetIndex;
|
||||
uint encoding = writer.ReadInstructionAt(branchIndex);
|
||||
|
||||
if (encoding == 0x14000000)
|
||||
{
|
||||
// Unconditional branch.
|
||||
|
||||
if (targets.TryGetValue(targetAddress, out targetIndex))
|
||||
{
|
||||
delta = targetIndex - branchIndex;
|
||||
|
||||
if (delta >= -Encodable26BitsOffsetLimit && delta < Encodable26BitsOffsetLimit)
|
||||
{
|
||||
writer.WriteInstructionAt(branchIndex, encoding | (uint)(delta & 0x3ffffff));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
targetIndex = writer.InstructionPointer;
|
||||
delta = targetIndex - branchIndex;
|
||||
|
||||
writer.WriteInstructionAt(branchIndex, encoding | (uint)(delta & 0x3ffffff));
|
||||
WriteTailCallConstant(context, ref asm, targetAddress);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Conditional branch.
|
||||
|
||||
uint branchMask = 0x7ffff;
|
||||
int branchMax = (int)(branchMask + 1) / 2;
|
||||
|
||||
if (targets.TryGetValue(targetAddress, out targetIndex))
|
||||
{
|
||||
delta = targetIndex - branchIndex;
|
||||
|
||||
if (delta >= -branchMax && delta < branchMax)
|
||||
{
|
||||
writer.WriteInstructionAt(branchIndex, encoding | (uint)((delta & branchMask) << 5));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
targetIndex = writer.InstructionPointer;
|
||||
delta = targetIndex - branchIndex;
|
||||
|
||||
if (delta >= -branchMax && delta < branchMax)
|
||||
{
|
||||
writer.WriteInstructionAt(branchIndex, encoding | (uint)((delta & branchMask) << 5));
|
||||
WriteTailCallConstant(context, ref asm, targetAddress);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the branch target is too far away, we use a regular unconditional branch
|
||||
// instruction instead which has a much higher range.
|
||||
// We branch directly to the end of the function, where we put the conditional branch,
|
||||
// and then branch back to the next instruction or return the branch target depending
|
||||
// on the branch being taken or not.
|
||||
|
||||
uint branchInst = 0x14000000u | ((uint)delta & 0x3ffffff);
|
||||
Debug.Assert((int)(branchInst << 6) >> 4 == delta * 4);
|
||||
|
||||
writer.WriteInstructionAt(branchIndex, branchInst);
|
||||
|
||||
int movedBranchIndex = writer.InstructionPointer;
|
||||
|
||||
writer.WriteInstruction(0u); // Placeholder
|
||||
asm.B((branchIndex + 1 - writer.InstructionPointer) * 4);
|
||||
|
||||
delta = writer.InstructionPointer - movedBranchIndex;
|
||||
|
||||
writer.WriteInstructionAt(movedBranchIndex, encoding | (uint)((delta & branchMask) << 5));
|
||||
WriteTailCallConstant(context, ref asm, targetAddress);
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Assert(name == InstName.B || name == InstName.Cbnz, $"Unknown branch instruction \"{name}\".");
|
||||
}
|
||||
|
||||
private static void RewriteCallInstructionWithTarget(in Context context, uint targetAddress, uint nextAddress, int branchIndex)
|
||||
{
|
||||
CodeWriter writer = context.Writer;
|
||||
Assembler asm = new(writer);
|
||||
|
||||
WriteBranchToCurrentPosition(context, branchIndex);
|
||||
|
||||
asm.Mov(context.RegisterAllocator.RemapGprRegister(RegisterUtils.LrRegister), nextAddress);
|
||||
|
||||
context.StoreToContext();
|
||||
InstEmitFlow.WriteCallWithGuestAddress(
|
||||
writer,
|
||||
ref asm,
|
||||
context.RegisterAllocator,
|
||||
context.TailMerger,
|
||||
context.WriteEpilogueWithoutContext,
|
||||
context.FuncTable,
|
||||
context.DispatchStubPointer,
|
||||
context.GetReservedStackOffset(),
|
||||
nextAddress,
|
||||
InstEmitCommon.Const((int)targetAddress));
|
||||
context.LoadFromContext();
|
||||
|
||||
// Branch back to the next instruction (after the call).
|
||||
asm.B((branchIndex + 1 - writer.InstructionPointer) * 4);
|
||||
}
|
||||
|
||||
private static void RewriteIndirectBranchInstructionWithTarget(in Context context, InstName name, uint targetRegister, int branchIndex)
|
||||
{
|
||||
CodeWriter writer = context.Writer;
|
||||
Assembler asm = new(writer);
|
||||
|
||||
WriteBranchToCurrentPosition(context, branchIndex);
|
||||
|
||||
using ScopedRegister target = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
|
||||
asm.And(target.Operand, context.RegisterAllocator.RemapGprRegister((int)targetRegister), InstEmitCommon.Const(~1));
|
||||
|
||||
context.StoreToContext();
|
||||
|
||||
if ((name == InstName.Bx && targetRegister == RegisterUtils.LrRegister) ||
|
||||
name == InstName.Ldm ||
|
||||
name == InstName.Ldmda ||
|
||||
name == InstName.Ldmdb ||
|
||||
name == InstName.Ldmib)
|
||||
{
|
||||
// Arm32 does not have a return instruction, instead returns are implemented
|
||||
// either using BX LR (for leaf functions), or POP { ... PC }.
|
||||
|
||||
asm.Mov(Register(0), target.Operand);
|
||||
|
||||
context.TailMerger.AddUnconditionalReturn(writer, asm);
|
||||
}
|
||||
else
|
||||
{
|
||||
InstEmitFlow.WriteCallWithGuestAddress(
|
||||
writer,
|
||||
ref asm,
|
||||
context.RegisterAllocator,
|
||||
context.TailMerger,
|
||||
context.WriteEpilogueWithoutContext,
|
||||
context.FuncTable,
|
||||
context.DispatchStubPointer,
|
||||
context.GetReservedStackOffset(),
|
||||
0u,
|
||||
target.Operand,
|
||||
isTail: true);
|
||||
}
|
||||
}
|
||||
|
||||
private static void RewriteTableBranchInstructionWithTarget(in Context context, bool halfword, uint rn, uint rm, int branchIndex)
|
||||
{
|
||||
CodeWriter writer = context.Writer;
|
||||
Assembler asm = new(writer);
|
||||
|
||||
WriteBranchToCurrentPosition(context, branchIndex);
|
||||
|
||||
using ScopedRegister target = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
|
||||
asm.Add(
|
||||
target.Operand,
|
||||
context.RegisterAllocator.RemapGprRegister((int)rn),
|
||||
context.RegisterAllocator.RemapGprRegister((int)rm),
|
||||
ArmShiftType.Lsl,
|
||||
halfword ? 1 : 0);
|
||||
|
||||
InstEmitMemory.WriteAddressTranslation(context.MemoryManagerType, context.RegisterAllocator, asm, target.Operand, target.Operand);
|
||||
|
||||
if (halfword)
|
||||
{
|
||||
asm.LdrhRiUn(target.Operand, target.Operand, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
asm.LdrbRiUn(target.Operand, target.Operand, 0);
|
||||
}
|
||||
|
||||
asm.Add(target.Operand, context.RegisterAllocator.RemapGprRegister(RegisterUtils.PcRegister), target.Operand, ArmShiftType.Lsl, 1);
|
||||
|
||||
context.StoreToContext();
|
||||
|
||||
InstEmitFlow.WriteCallWithGuestAddress(
|
||||
writer,
|
||||
ref asm,
|
||||
context.RegisterAllocator,
|
||||
context.TailMerger,
|
||||
context.WriteEpilogueWithoutContext,
|
||||
context.FuncTable,
|
||||
context.DispatchStubPointer,
|
||||
context.GetReservedStackOffset(),
|
||||
0u,
|
||||
target.Operand,
|
||||
isTail: true);
|
||||
}
|
||||
|
||||
private static void RewriteIndirectCallInstructionWithTarget(in Context context, uint targetRegister, uint nextAddress, int branchIndex)
|
||||
{
|
||||
CodeWriter writer = context.Writer;
|
||||
Assembler asm = new(writer);
|
||||
|
||||
WriteBranchToCurrentPosition(context, branchIndex);
|
||||
|
||||
using ScopedRegister target = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
|
||||
asm.And(target.Operand, context.RegisterAllocator.RemapGprRegister((int)targetRegister), InstEmitCommon.Const(~1));
|
||||
asm.Mov(context.RegisterAllocator.RemapGprRegister(RegisterUtils.LrRegister), nextAddress);
|
||||
|
||||
context.StoreToContext();
|
||||
InstEmitFlow.WriteCallWithGuestAddress(
|
||||
writer,
|
||||
ref asm,
|
||||
context.RegisterAllocator,
|
||||
context.TailMerger,
|
||||
context.WriteEpilogueWithoutContext,
|
||||
context.FuncTable,
|
||||
context.DispatchStubPointer,
|
||||
context.GetReservedStackOffset(),
|
||||
nextAddress & ~1u,
|
||||
target.Operand);
|
||||
context.LoadFromContext();
|
||||
|
||||
// Branch back to the next instruction (after the call).
|
||||
asm.B((branchIndex + 1 - writer.InstructionPointer) * 4);
|
||||
}
|
||||
|
||||
private static void RewriteHostCall(in Context context, InstName name, BranchType type, uint imm, uint pc, int branchIndex)
|
||||
{
|
||||
CodeWriter writer = context.Writer;
|
||||
Assembler asm = new(writer);
|
||||
|
||||
uint encoding = writer.ReadInstructionAt(branchIndex);
|
||||
int targetIndex = writer.InstructionPointer;
|
||||
int delta = targetIndex - branchIndex;
|
||||
|
||||
writer.WriteInstructionAt(branchIndex, encoding | (uint)(delta & 0x3ffffff));
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case BranchType.SyncPoint:
|
||||
InstEmitSystem.WriteSyncPoint(context.Writer, context.RegisterAllocator, context.TailMerger, context.GetReservedStackOffset());
|
||||
break;
|
||||
case BranchType.SoftwareInterrupt:
|
||||
context.StoreToContext();
|
||||
switch (name)
|
||||
{
|
||||
case InstName.Bkpt:
|
||||
InstEmitSystem.WriteBkpt(context.Writer, context.RegisterAllocator, context.TailMerger, context.GetReservedStackOffset(), pc, imm);
|
||||
break;
|
||||
case InstName.Svc:
|
||||
InstEmitSystem.WriteSvc(context.Writer, context.RegisterAllocator, context.TailMerger, context.GetReservedStackOffset(), pc, imm);
|
||||
break;
|
||||
case InstName.Udf:
|
||||
InstEmitSystem.WriteUdf(context.Writer, context.RegisterAllocator, context.TailMerger, context.GetReservedStackOffset(), pc, imm);
|
||||
break;
|
||||
}
|
||||
context.LoadFromContext();
|
||||
break;
|
||||
case BranchType.ReadCntpct:
|
||||
InstEmitSystem.WriteReadCntpct(context.Writer, context.RegisterAllocator, context.GetReservedStackOffset(), (int)imm, (int)pc);
|
||||
break;
|
||||
default:
|
||||
Debug.Fail($"Invalid branch type '{type}'");
|
||||
break;
|
||||
}
|
||||
|
||||
// Branch back to the next instruction.
|
||||
asm.B((branchIndex + 1 - writer.InstructionPointer) * 4);
|
||||
}
|
||||
|
||||
private static void WriteBranchToCurrentPosition(in Context context, int branchIndex)
|
||||
{
|
||||
CodeWriter writer = context.Writer;
|
||||
|
||||
int targetIndex = writer.InstructionPointer;
|
||||
|
||||
if (branchIndex + 1 == targetIndex)
|
||||
{
|
||||
writer.RemoveLastInstruction();
|
||||
}
|
||||
else
|
||||
{
|
||||
uint encoding = writer.ReadInstructionAt(branchIndex);
|
||||
int delta = targetIndex - branchIndex;
|
||||
|
||||
writer.WriteInstructionAt(branchIndex, encoding | (uint)(delta & 0x3ffffff));
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteTailCallConstant(in Context context, ref Assembler asm, uint address)
|
||||
{
|
||||
context.StoreToContext();
|
||||
InstEmitFlow.WriteCallWithGuestAddress(
|
||||
context.Writer,
|
||||
ref asm,
|
||||
context.RegisterAllocator,
|
||||
context.TailMerger,
|
||||
context.WriteEpilogueWithoutContext,
|
||||
context.FuncTable,
|
||||
context.DispatchStubPointer,
|
||||
context.GetReservedStackOffset(),
|
||||
0u,
|
||||
InstEmitCommon.Const((int)address),
|
||||
isTail: true);
|
||||
}
|
||||
|
||||
private static Operand Register(int register, OperandType type = OperandType.I64)
|
||||
{
|
||||
return new Operand(register, RegisterType.Integer, type);
|
||||
}
|
||||
|
||||
public static void PrintStats()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
8502
src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmit.cs
Normal file
8502
src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmit.cs
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,87 @@
|
||||
using Ryujinx.Cpu.LightningJit.CodeGen;
|
||||
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||
{
|
||||
static class InstEmitAbsDiff
|
||||
{
|
||||
public static void Usad8(CodeGenContext context, uint rd, uint rn, uint rm)
|
||||
{
|
||||
using ScopedRegister tempD = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
using ScopedRegister tempD2 = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
|
||||
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
||||
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
||||
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
||||
|
||||
for (int b = 0; b < 4; b++)
|
||||
{
|
||||
context.Arm64Assembler.Ubfx(tempN.Operand, rnOperand, b * 8, 8);
|
||||
context.Arm64Assembler.Ubfx(tempM.Operand, rmOperand, b * 8, 8);
|
||||
|
||||
Operand dest = b == 0 ? tempD.Operand : tempD2.Operand;
|
||||
|
||||
context.Arm64Assembler.Sub(dest, tempN.Operand, tempM.Operand);
|
||||
|
||||
EmitAbs(context, dest);
|
||||
|
||||
if (b > 0)
|
||||
{
|
||||
if (b < 3)
|
||||
{
|
||||
context.Arm64Assembler.Add(tempD.Operand, tempD.Operand, dest);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Arm64Assembler.Add(rdOperand, tempD.Operand, dest);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Usada8(CodeGenContext context, uint rd, uint rn, uint rm, uint ra)
|
||||
{
|
||||
using ScopedRegister tempD = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
using ScopedRegister tempD2 = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
|
||||
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
||||
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
||||
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
||||
Operand raOperand = InstEmitCommon.GetInputGpr(context, ra);
|
||||
|
||||
for (int b = 0; b < 4; b++)
|
||||
{
|
||||
context.Arm64Assembler.Ubfx(tempN.Operand, rnOperand, b * 8, 8);
|
||||
context.Arm64Assembler.Ubfx(tempM.Operand, rmOperand, b * 8, 8);
|
||||
|
||||
Operand dest = b == 0 ? tempD.Operand : tempD2.Operand;
|
||||
|
||||
context.Arm64Assembler.Sub(dest, tempN.Operand, tempM.Operand);
|
||||
|
||||
EmitAbs(context, dest);
|
||||
|
||||
if (b > 0)
|
||||
{
|
||||
context.Arm64Assembler.Add(tempD.Operand, tempD.Operand, dest);
|
||||
}
|
||||
}
|
||||
|
||||
context.Arm64Assembler.Add(rdOperand, tempD.Operand, raOperand);
|
||||
}
|
||||
|
||||
private static void EmitAbs(CodeGenContext context, Operand value)
|
||||
{
|
||||
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
|
||||
// r = (value + ((int)value >> 31)) ^ ((int)value >> 31).
|
||||
// Subtracts 1 and then inverts the value if the sign bit is set, same as a conditional negation.
|
||||
|
||||
context.Arm64Assembler.Add(tempRegister.Operand, value, value, ArmShiftType.Asr, 31);
|
||||
context.Arm64Assembler.Eor(value, tempRegister.Operand, value, ArmShiftType.Asr, 31);
|
||||
}
|
||||
}
|
||||
}
|
1105
src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitAlu.cs
Normal file
1105
src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitAlu.cs
Normal file
File diff suppressed because it is too large
Load Diff
103
src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitBit.cs
Normal file
103
src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitBit.cs
Normal file
@ -0,0 +1,103 @@
|
||||
using Ryujinx.Cpu.LightningJit.CodeGen;
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||
{
|
||||
static class InstEmitBit
|
||||
{
|
||||
public static void Bfc(CodeGenContext context, uint rd, uint lsb, uint msb)
|
||||
{
|
||||
// This is documented as "unpredictable".
|
||||
if (msb < lsb)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
||||
|
||||
context.Arm64Assembler.Bfc(rdOperand, (int)lsb, (int)(msb - lsb + 1));
|
||||
}
|
||||
|
||||
public static void Bfi(CodeGenContext context, uint rd, uint rn, uint lsb, uint msb)
|
||||
{
|
||||
// This is documented as "unpredictable".
|
||||
if (msb < lsb)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
||||
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
||||
|
||||
context.Arm64Assembler.Bfi(rdOperand, rnOperand, (int)lsb, (int)(msb - lsb + 1));
|
||||
}
|
||||
|
||||
public static void Clz(CodeGenContext context, uint rd, uint rm)
|
||||
{
|
||||
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
||||
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
||||
|
||||
context.Arm64Assembler.Clz(rdOperand, rmOperand);
|
||||
}
|
||||
|
||||
public static void Rbit(CodeGenContext context, uint rd, uint rm)
|
||||
{
|
||||
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
||||
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
||||
|
||||
context.Arm64Assembler.Rbit(rdOperand, rmOperand);
|
||||
}
|
||||
|
||||
public static void Rev(CodeGenContext context, uint rd, uint rm)
|
||||
{
|
||||
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
||||
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
||||
|
||||
context.Arm64Assembler.Rev(rdOperand, rmOperand);
|
||||
}
|
||||
|
||||
public static void Rev16(CodeGenContext context, uint rd, uint rm)
|
||||
{
|
||||
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
||||
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
||||
|
||||
context.Arm64Assembler.Rev16(rdOperand, rmOperand);
|
||||
}
|
||||
|
||||
public static void Revsh(CodeGenContext context, uint rd, uint rm)
|
||||
{
|
||||
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
||||
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
||||
|
||||
context.Arm64Assembler.Rev16(rdOperand, rmOperand);
|
||||
context.Arm64Assembler.Sxth(rdOperand, rdOperand);
|
||||
}
|
||||
|
||||
public static void Sbfx(CodeGenContext context, uint rd, uint rn, uint lsb, uint widthMinus1)
|
||||
{
|
||||
// This is documented as "unpredictable".
|
||||
if (lsb + widthMinus1 > 31)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
||||
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
||||
|
||||
context.Arm64Assembler.Sbfx(rdOperand, rnOperand, (int)lsb, (int)widthMinus1 + 1);
|
||||
}
|
||||
|
||||
public static void Ubfx(CodeGenContext context, uint rd, uint rn, uint lsb, uint widthMinus1)
|
||||
{
|
||||
// This is documented as "unpredictable".
|
||||
if (lsb + widthMinus1 > 31)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
||||
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
||||
|
||||
context.Arm64Assembler.Ubfx(rdOperand, rnOperand, (int)lsb, (int)widthMinus1 + 1);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,263 @@
|
||||
using Ryujinx.Cpu.LightningJit.CodeGen;
|
||||
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||
{
|
||||
static class InstEmitCommon
|
||||
{
|
||||
public static Operand Const(int value)
|
||||
{
|
||||
return new(OperandKind.Constant, OperandType.I32, (uint)value);
|
||||
}
|
||||
|
||||
public static Operand GetInputGpr(CodeGenContext context, uint register)
|
||||
{
|
||||
Operand operand = context.RegisterAllocator.RemapGprRegister((int)register);
|
||||
|
||||
if (register == RegisterUtils.PcRegister)
|
||||
{
|
||||
context.Arm64Assembler.Mov(operand, context.Pc);
|
||||
}
|
||||
|
||||
return operand;
|
||||
}
|
||||
|
||||
public static Operand GetOutputGpr(CodeGenContext context, uint register)
|
||||
{
|
||||
return context.RegisterAllocator.RemapGprRegister((int)register);
|
||||
}
|
||||
|
||||
public static void GetCurrentFlags(CodeGenContext context, Operand flagsOut)
|
||||
{
|
||||
context.Arm64Assembler.MrsNzcv(flagsOut);
|
||||
context.Arm64Assembler.Lsr(flagsOut, flagsOut, Const(28));
|
||||
}
|
||||
|
||||
public static void RestoreNzcvFlags(CodeGenContext context, Operand nzcvFlags)
|
||||
{
|
||||
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
|
||||
context.Arm64Assembler.Lsl(tempRegister.Operand, nzcvFlags, Const(28));
|
||||
context.Arm64Assembler.MsrNzcv(tempRegister.Operand);
|
||||
}
|
||||
|
||||
public static void RestoreCvFlags(CodeGenContext context, Operand cvFlags)
|
||||
{
|
||||
// Arm64 zeros the carry and overflow flags for logical operations, but Arm32 keeps them unchanged.
|
||||
// This will restore carry and overflow after a operation has zeroed them.
|
||||
|
||||
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
|
||||
context.Arm64Assembler.MrsNzcv(tempRegister.Operand);
|
||||
context.Arm64Assembler.Bfi(tempRegister.Operand, cvFlags, 28, 2);
|
||||
context.Arm64Assembler.MsrNzcv(tempRegister.Operand);
|
||||
}
|
||||
|
||||
public static void SetThumbFlag(CodeGenContext context)
|
||||
{
|
||||
Operand ctx = InstEmitSystem.Register(context.RegisterAllocator.FixedContextRegister);
|
||||
|
||||
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
|
||||
context.Arm64Assembler.LdrRiUn(tempRegister.Operand, ctx, NativeContextOffsets.FlagsBaseOffset);
|
||||
context.Arm64Assembler.Orr(tempRegister.Operand, tempRegister.Operand, Const(1 << 5));
|
||||
context.Arm64Assembler.StrRiUn(tempRegister.Operand, ctx, NativeContextOffsets.FlagsBaseOffset);
|
||||
}
|
||||
|
||||
public static void SetThumbFlag(CodeGenContext context, Operand value)
|
||||
{
|
||||
Operand ctx = InstEmitSystem.Register(context.RegisterAllocator.FixedContextRegister);
|
||||
|
||||
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
|
||||
context.Arm64Assembler.LdrRiUn(tempRegister.Operand, ctx, NativeContextOffsets.FlagsBaseOffset);
|
||||
context.Arm64Assembler.Bfi(tempRegister.Operand, value, 5, 1);
|
||||
context.Arm64Assembler.StrRiUn(tempRegister.Operand, ctx, NativeContextOffsets.FlagsBaseOffset);
|
||||
}
|
||||
|
||||
public static void ClearThumbFlag(CodeGenContext context)
|
||||
{
|
||||
Operand ctx = InstEmitSystem.Register(context.RegisterAllocator.FixedContextRegister);
|
||||
|
||||
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
|
||||
context.Arm64Assembler.LdrRiUn(tempRegister.Operand, ctx, NativeContextOffsets.FlagsBaseOffset);
|
||||
context.Arm64Assembler.Bfc(tempRegister.Operand, 5, 1);
|
||||
context.Arm64Assembler.StrRiUn(tempRegister.Operand, ctx, NativeContextOffsets.FlagsBaseOffset);
|
||||
}
|
||||
|
||||
public static void EmitSigned16BitPair(CodeGenContext context, uint rd, uint rn, Action<Operand, Operand> elementAction)
|
||||
{
|
||||
using ScopedRegister tempD = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
using ScopedRegister tempD2 = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
|
||||
Operand rdOperand = GetOutputGpr(context, rd);
|
||||
Operand rnOperand = GetInputGpr(context, rn);
|
||||
|
||||
context.Arm64Assembler.Sxth(tempN.Operand, rnOperand);
|
||||
elementAction(tempD.Operand, tempN.Operand);
|
||||
context.Arm64Assembler.Uxth(tempD2.Operand, tempD.Operand);
|
||||
|
||||
context.Arm64Assembler.Asr(tempN.Operand, rnOperand, Const(16));
|
||||
elementAction(tempD.Operand, tempN.Operand);
|
||||
context.Arm64Assembler.Orr(rdOperand, tempD2.Operand, tempD.Operand, ArmShiftType.Lsl, 16);
|
||||
}
|
||||
|
||||
public static void EmitSigned16BitPair(CodeGenContext context, uint rd, uint rn, uint rm, Action<Operand, Operand, Operand> elementAction)
|
||||
{
|
||||
using ScopedRegister tempD = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
using ScopedRegister tempD2 = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
|
||||
Operand rdOperand = GetOutputGpr(context, rd);
|
||||
Operand rnOperand = GetInputGpr(context, rn);
|
||||
Operand rmOperand = GetInputGpr(context, rm);
|
||||
|
||||
context.Arm64Assembler.Sxth(tempN.Operand, rnOperand);
|
||||
context.Arm64Assembler.Sxth(tempM.Operand, rmOperand);
|
||||
elementAction(tempD.Operand, tempN.Operand, tempM.Operand);
|
||||
context.Arm64Assembler.Uxth(tempD2.Operand, tempD.Operand);
|
||||
|
||||
context.Arm64Assembler.Asr(tempN.Operand, rnOperand, Const(16));
|
||||
context.Arm64Assembler.Asr(tempM.Operand, rmOperand, Const(16));
|
||||
elementAction(tempD.Operand, tempN.Operand, tempM.Operand);
|
||||
context.Arm64Assembler.Orr(rdOperand, tempD2.Operand, tempD.Operand, ArmShiftType.Lsl, 16);
|
||||
}
|
||||
|
||||
public static void EmitSigned16BitXPair(CodeGenContext context, uint rd, uint rn, uint rm, Action<Operand, Operand, Operand, int> elementAction)
|
||||
{
|
||||
using ScopedRegister tempD = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
using ScopedRegister tempD2 = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
|
||||
Operand rdOperand = GetOutputGpr(context, rd);
|
||||
Operand rnOperand = GetInputGpr(context, rn);
|
||||
Operand rmOperand = GetInputGpr(context, rm);
|
||||
|
||||
context.Arm64Assembler.Sxth(tempN.Operand, rnOperand);
|
||||
context.Arm64Assembler.Asr(tempM.Operand, rmOperand, Const(16));
|
||||
elementAction(tempD.Operand, tempN.Operand, tempM.Operand, 0);
|
||||
context.Arm64Assembler.Uxth(tempD2.Operand, tempD.Operand);
|
||||
|
||||
context.Arm64Assembler.Asr(tempN.Operand, rnOperand, Const(16));
|
||||
context.Arm64Assembler.Sxth(tempM.Operand, rmOperand);
|
||||
elementAction(tempD.Operand, tempN.Operand, tempM.Operand, 1);
|
||||
context.Arm64Assembler.Orr(rdOperand, tempD2.Operand, tempD.Operand, ArmShiftType.Lsl, 16);
|
||||
}
|
||||
|
||||
public static void EmitSigned8BitPair(CodeGenContext context, uint rd, uint rn, uint rm, Action<Operand, Operand, Operand> elementAction)
|
||||
{
|
||||
Emit8BitPair(context, rd, rn, rm, elementAction, unsigned: false);
|
||||
}
|
||||
|
||||
public static void EmitUnsigned16BitPair(CodeGenContext context, uint rd, uint rn, uint rm, Action<Operand, Operand, Operand> elementAction)
|
||||
{
|
||||
using ScopedRegister tempD = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
using ScopedRegister tempD2 = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
|
||||
Operand rdOperand = GetOutputGpr(context, rd);
|
||||
Operand rnOperand = GetInputGpr(context, rn);
|
||||
Operand rmOperand = GetInputGpr(context, rm);
|
||||
|
||||
context.Arm64Assembler.Uxth(tempN.Operand, rnOperand);
|
||||
context.Arm64Assembler.Uxth(tempM.Operand, rmOperand);
|
||||
elementAction(tempD.Operand, tempN.Operand, tempM.Operand);
|
||||
context.Arm64Assembler.Uxth(tempD2.Operand, tempD.Operand);
|
||||
|
||||
context.Arm64Assembler.Lsr(tempN.Operand, rnOperand, Const(16));
|
||||
context.Arm64Assembler.Lsr(tempM.Operand, rmOperand, Const(16));
|
||||
elementAction(tempD.Operand, tempN.Operand, tempM.Operand);
|
||||
context.Arm64Assembler.Orr(rdOperand, tempD2.Operand, tempD.Operand, ArmShiftType.Lsl, 16);
|
||||
}
|
||||
|
||||
public static void EmitUnsigned16BitXPair(CodeGenContext context, uint rd, uint rn, uint rm, Action<Operand, Operand, Operand, int> elementAction)
|
||||
{
|
||||
using ScopedRegister tempD = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
using ScopedRegister tempD2 = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
|
||||
Operand rdOperand = GetOutputGpr(context, rd);
|
||||
Operand rnOperand = GetInputGpr(context, rn);
|
||||
Operand rmOperand = GetInputGpr(context, rm);
|
||||
|
||||
context.Arm64Assembler.Uxth(tempN.Operand, rnOperand);
|
||||
context.Arm64Assembler.Lsr(tempM.Operand, rmOperand, Const(16));
|
||||
elementAction(tempD.Operand, tempN.Operand, tempM.Operand, 0);
|
||||
context.Arm64Assembler.Uxth(tempD2.Operand, tempD.Operand);
|
||||
|
||||
context.Arm64Assembler.Lsr(tempN.Operand, rnOperand, Const(16));
|
||||
context.Arm64Assembler.Uxth(tempM.Operand, rmOperand);
|
||||
elementAction(tempD.Operand, tempN.Operand, tempM.Operand, 1);
|
||||
context.Arm64Assembler.Orr(rdOperand, tempD2.Operand, tempD.Operand, ArmShiftType.Lsl, 16);
|
||||
}
|
||||
|
||||
public static void EmitUnsigned8BitPair(CodeGenContext context, uint rd, uint rn, uint rm, Action<Operand, Operand, Operand> elementAction)
|
||||
{
|
||||
Emit8BitPair(context, rd, rn, rm, elementAction, unsigned: true);
|
||||
}
|
||||
|
||||
private static void Emit8BitPair(CodeGenContext context, uint rd, uint rn, uint rm, Action<Operand, Operand, Operand> elementAction, bool unsigned)
|
||||
{
|
||||
using ScopedRegister tempD = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
using ScopedRegister tempD2 = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
|
||||
Operand rdOperand = GetOutputGpr(context, rd);
|
||||
Operand rnOperand = GetInputGpr(context, rn);
|
||||
Operand rmOperand = GetInputGpr(context, rm);
|
||||
|
||||
for (int b = 0; b < 4; b++)
|
||||
{
|
||||
if (unsigned)
|
||||
{
|
||||
context.Arm64Assembler.Ubfx(tempN.Operand, rnOperand, b * 8, 8);
|
||||
context.Arm64Assembler.Ubfx(tempM.Operand, rmOperand, b * 8, 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Arm64Assembler.Sbfx(tempN.Operand, rnOperand, b * 8, 8);
|
||||
context.Arm64Assembler.Sbfx(tempM.Operand, rmOperand, b * 8, 8);
|
||||
}
|
||||
|
||||
elementAction(tempD.Operand, tempN.Operand, tempM.Operand);
|
||||
|
||||
if (b == 0)
|
||||
{
|
||||
context.Arm64Assembler.Uxtb(tempD2.Operand, tempD.Operand);
|
||||
}
|
||||
else if (b < 3)
|
||||
{
|
||||
context.Arm64Assembler.Uxtb(tempD.Operand, tempD.Operand);
|
||||
context.Arm64Assembler.Orr(tempD2.Operand, tempD2.Operand, tempD.Operand, ArmShiftType.Lsl, b * 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Arm64Assembler.Orr(rdOperand, tempD2.Operand, tempD.Operand, ArmShiftType.Lsl, 24);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static uint CombineV(uint low4, uint high1, uint size)
|
||||
{
|
||||
return size == 3 ? CombineV(low4, high1) : CombineVF(high1, low4);
|
||||
}
|
||||
|
||||
public static uint CombineV(uint low4, uint high1)
|
||||
{
|
||||
return low4 | (high1 << 4);
|
||||
}
|
||||
|
||||
public static uint CombineVF(uint low1, uint high4)
|
||||
{
|
||||
return low1 | (high4 << 1);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
using Ryujinx.Cpu.LightningJit.CodeGen;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||
{
|
||||
static class InstEmitCrc32
|
||||
{
|
||||
public static void Crc32(CodeGenContext context, uint rd, uint rn, uint rm, uint sz)
|
||||
{
|
||||
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
||||
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
||||
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
||||
|
||||
context.Arm64Assembler.Crc32(rdOperand, rnOperand, rmOperand, Math.Min(2, sz));
|
||||
}
|
||||
|
||||
public static void Crc32c(CodeGenContext context, uint rd, uint rn, uint rm, uint sz)
|
||||
{
|
||||
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
||||
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
||||
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
||||
|
||||
context.Arm64Assembler.Crc32c(rdOperand, rnOperand, rmOperand, Math.Min(2, sz));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
using Ryujinx.Cpu.LightningJit.CodeGen;
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||
{
|
||||
static class InstEmitDivide
|
||||
{
|
||||
public static void Sdiv(CodeGenContext context, uint rd, uint rn, uint rm)
|
||||
{
|
||||
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
||||
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
||||
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
||||
|
||||
context.Arm64Assembler.Sdiv(rdOperand, rnOperand, rmOperand);
|
||||
}
|
||||
|
||||
public static void Udiv(CodeGenContext context, uint rd, uint rn, uint rm)
|
||||
{
|
||||
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
||||
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
||||
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
||||
|
||||
context.Arm64Assembler.Udiv(rdOperand, rnOperand, rmOperand);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,191 @@
|
||||
using Ryujinx.Cpu.LightningJit.CodeGen;
|
||||
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||
{
|
||||
static class InstEmitExtension
|
||||
{
|
||||
public static void Sxtab(CodeGenContext context, uint rd, uint rn, uint rm, uint rotate)
|
||||
{
|
||||
EmitRotated(context, ArmExtensionType.Sxtb, rd, rn, rm, rotate);
|
||||
}
|
||||
|
||||
public static void Sxtab16(CodeGenContext context, uint rd, uint rn, uint rm, uint rotate)
|
||||
{
|
||||
EmitExtendAccumulate8(context, rd, rn, rm, rotate, unsigned: false);
|
||||
}
|
||||
|
||||
public static void Sxtah(CodeGenContext context, uint rd, uint rn, uint rm, uint rotate)
|
||||
{
|
||||
EmitRotated(context, ArmExtensionType.Sxth, rd, rn, rm, rotate);
|
||||
}
|
||||
|
||||
public static void Sxtb(CodeGenContext context, uint rd, uint rm, uint rotate)
|
||||
{
|
||||
EmitRotated(context, context.Arm64Assembler.Sxtb, rd, rm, rotate);
|
||||
}
|
||||
|
||||
public static void Sxtb16(CodeGenContext context, uint rd, uint rm, uint rotate)
|
||||
{
|
||||
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
||||
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
||||
|
||||
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
using ScopedRegister tempRegister2 = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
|
||||
if (rotate != 0)
|
||||
{
|
||||
context.Arm64Assembler.Ror(tempRegister.Operand, rmOperand, InstEmitCommon.Const((int)rotate * 8));
|
||||
context.Arm64Assembler.And(rdOperand, tempRegister.Operand, InstEmitCommon.Const(0xff00ff));
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Arm64Assembler.And(rdOperand, rmOperand, InstEmitCommon.Const(0xff00ff));
|
||||
}
|
||||
|
||||
// Sign-extend by broadcasting sign bits.
|
||||
context.Arm64Assembler.And(tempRegister.Operand, rdOperand, InstEmitCommon.Const(0x800080));
|
||||
context.Arm64Assembler.Lsl(tempRegister2.Operand, tempRegister.Operand, InstEmitCommon.Const(9));
|
||||
context.Arm64Assembler.Sub(tempRegister.Operand, tempRegister2.Operand, tempRegister.Operand);
|
||||
context.Arm64Assembler.Orr(rdOperand, rdOperand, tempRegister.Operand);
|
||||
}
|
||||
|
||||
public static void Sxth(CodeGenContext context, uint rd, uint rm, uint rotate)
|
||||
{
|
||||
EmitRotated(context, context.Arm64Assembler.Sxth, rd, rm, rotate);
|
||||
}
|
||||
|
||||
public static void Uxtab(CodeGenContext context, uint rd, uint rn, uint rm, uint rotate)
|
||||
{
|
||||
EmitRotated(context, ArmExtensionType.Uxtb, rd, rn, rm, rotate);
|
||||
}
|
||||
|
||||
public static void Uxtab16(CodeGenContext context, uint rd, uint rn, uint rm, uint rotate)
|
||||
{
|
||||
EmitExtendAccumulate8(context, rd, rn, rm, rotate, unsigned: true);
|
||||
}
|
||||
|
||||
public static void Uxtah(CodeGenContext context, uint rd, uint rn, uint rm, uint rotate)
|
||||
{
|
||||
EmitRotated(context, ArmExtensionType.Uxth, rd, rn, rm, rotate);
|
||||
}
|
||||
|
||||
public static void Uxtb(CodeGenContext context, uint rd, uint rm, uint rotate)
|
||||
{
|
||||
EmitRotated(context, context.Arm64Assembler.Uxtb, rd, rm, rotate);
|
||||
}
|
||||
|
||||
public static void Uxtb16(CodeGenContext context, uint rd, uint rm, uint rotate)
|
||||
{
|
||||
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
||||
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
||||
|
||||
if (rotate != 0)
|
||||
{
|
||||
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
|
||||
context.Arm64Assembler.Ror(tempRegister.Operand, rmOperand, InstEmitCommon.Const((int)rotate * 8));
|
||||
context.Arm64Assembler.And(rdOperand, tempRegister.Operand, InstEmitCommon.Const(0xff00ff));
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Arm64Assembler.And(rdOperand, rmOperand, InstEmitCommon.Const(0xff00ff));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Uxth(CodeGenContext context, uint rd, uint rm, uint rotate)
|
||||
{
|
||||
EmitRotated(context, context.Arm64Assembler.Uxth, rd, rm, rotate);
|
||||
}
|
||||
|
||||
private static void EmitRotated(CodeGenContext context, Action<Operand, Operand> action, uint rd, uint rm, uint rotate)
|
||||
{
|
||||
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
||||
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
||||
|
||||
if (rotate != 0)
|
||||
{
|
||||
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
|
||||
context.Arm64Assembler.Ror(tempRegister.Operand, rmOperand, InstEmitCommon.Const((int)rotate * 8));
|
||||
action(rdOperand, tempRegister.Operand);
|
||||
}
|
||||
else
|
||||
{
|
||||
action(rdOperand, rmOperand);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitRotated(CodeGenContext context, ArmExtensionType extensionType, uint rd, uint rn, uint rm, uint rotate)
|
||||
{
|
||||
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
||||
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
||||
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
||||
|
||||
if (rotate != 0)
|
||||
{
|
||||
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
|
||||
context.Arm64Assembler.Ror(tempRegister.Operand, rmOperand, InstEmitCommon.Const((int)rotate * 8));
|
||||
context.Arm64Assembler.Add(rdOperand, rnOperand, tempRegister.Operand, extensionType);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Arm64Assembler.Add(rdOperand, rnOperand, rmOperand, extensionType);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitExtendAccumulate8(CodeGenContext context, uint rd, uint rn, uint rm, uint rotate, bool unsigned)
|
||||
{
|
||||
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
||||
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
||||
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
||||
|
||||
if (rotate != 0)
|
||||
{
|
||||
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
|
||||
context.Arm64Assembler.Ror(tempRegister.Operand, rmOperand, InstEmitCommon.Const((int)rotate * 8));
|
||||
|
||||
EmitExtendAccumulate8Core(context, rdOperand, rnOperand, tempRegister.Operand, unsigned);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitExtendAccumulate8Core(context, rdOperand, rnOperand, rmOperand, unsigned);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitExtendAccumulate8Core(CodeGenContext context, Operand rd, Operand rn, Operand rm, bool unsigned)
|
||||
{
|
||||
using ScopedRegister tempD = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
using ScopedRegister tempD2 = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
|
||||
if (unsigned)
|
||||
{
|
||||
context.Arm64Assembler.Uxth(tempN.Operand, rn);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Arm64Assembler.Sxth(tempN.Operand, rn);
|
||||
}
|
||||
|
||||
context.Arm64Assembler.Add(tempD.Operand, tempN.Operand, rm, unsigned ? ArmExtensionType.Uxtb : ArmExtensionType.Sxtb);
|
||||
context.Arm64Assembler.Uxth(tempD2.Operand, tempD.Operand);
|
||||
|
||||
if (unsigned)
|
||||
{
|
||||
context.Arm64Assembler.Lsr(tempN.Operand, rn, InstEmitCommon.Const(16));
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Arm64Assembler.Asr(tempN.Operand, rn, InstEmitCommon.Const(16));
|
||||
}
|
||||
|
||||
context.Arm64Assembler.Lsr(tempD.Operand, rm, InstEmitCommon.Const(16));
|
||||
context.Arm64Assembler.Add(tempD.Operand, tempN.Operand, tempD.Operand, unsigned ? ArmExtensionType.Uxtb : ArmExtensionType.Sxtb);
|
||||
context.Arm64Assembler.Orr(rd, tempD2.Operand, tempD.Operand, ArmShiftType.Lsl, 16);
|
||||
}
|
||||
}
|
||||
}
|
256
src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitFlow.cs
Normal file
256
src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitFlow.cs
Normal file
@ -0,0 +1,256 @@
|
||||
using ARMeilleure.Common;
|
||||
using Ryujinx.Cpu.LightningJit.CodeGen;
|
||||
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||
{
|
||||
static class InstEmitFlow
|
||||
{
|
||||
private const int SpIndex = 31;
|
||||
|
||||
public static void B(CodeGenContext context, int imm, ArmCondition condition)
|
||||
{
|
||||
context.AddPendingBranch(InstName.B, imm);
|
||||
|
||||
if (condition == ArmCondition.Al)
|
||||
{
|
||||
context.Arm64Assembler.B(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Arm64Assembler.B(condition, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Bl(CodeGenContext context, int imm, bool sourceIsThumb, bool targetIsThumb)
|
||||
{
|
||||
uint nextAddress = sourceIsThumb ? context.Pc | 1u : context.Pc - 4;
|
||||
uint targetAddress = targetIsThumb ? context.Pc + (uint)imm : (context.Pc & ~3u) + (uint)imm;
|
||||
|
||||
if (sourceIsThumb != targetIsThumb)
|
||||
{
|
||||
if (targetIsThumb)
|
||||
{
|
||||
InstEmitCommon.SetThumbFlag(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
InstEmitCommon.ClearThumbFlag(context);
|
||||
}
|
||||
}
|
||||
|
||||
context.AddPendingCall(targetAddress, nextAddress);
|
||||
|
||||
context.Arm64Assembler.B(0);
|
||||
}
|
||||
|
||||
public static void Blx(CodeGenContext context, uint rm, bool sourceIsThumb)
|
||||
{
|
||||
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
||||
|
||||
InstEmitCommon.SetThumbFlag(context, rmOperand);
|
||||
|
||||
uint nextAddress = sourceIsThumb ? (context.Pc - 2) | 1u : context.Pc - 4;
|
||||
|
||||
context.AddPendingIndirectCall(rm, nextAddress);
|
||||
|
||||
context.Arm64Assembler.B(0);
|
||||
}
|
||||
|
||||
public static void Bx(CodeGenContext context, uint rm)
|
||||
{
|
||||
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
||||
|
||||
InstEmitCommon.SetThumbFlag(context, rmOperand);
|
||||
|
||||
context.AddPendingIndirectBranch(InstName.Bx, rm);
|
||||
|
||||
context.Arm64Assembler.B(0);
|
||||
}
|
||||
|
||||
public static void Cbnz(CodeGenContext context, uint rn, int imm, bool op)
|
||||
{
|
||||
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
||||
|
||||
context.AddPendingBranch(InstName.Cbnz, imm);
|
||||
|
||||
if (op)
|
||||
{
|
||||
context.Arm64Assembler.Cbnz(rnOperand, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Arm64Assembler.Cbz(rnOperand, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static void It(CodeGenContext context, uint firstCond, uint mask)
|
||||
{
|
||||
Debug.Assert(mask != 0);
|
||||
|
||||
int instCount = 4 - BitOperations.TrailingZeroCount(mask);
|
||||
|
||||
Span<ArmCondition> conditions = stackalloc ArmCondition[instCount];
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (int index = 5 - instCount; index < 4; index++)
|
||||
{
|
||||
bool invert = (mask & (1u << index)) != 0;
|
||||
|
||||
if (invert)
|
||||
{
|
||||
conditions[i++] = ((ArmCondition)firstCond).Invert();
|
||||
}
|
||||
else
|
||||
{
|
||||
conditions[i++] = (ArmCondition)firstCond;
|
||||
}
|
||||
}
|
||||
|
||||
conditions[i] = (ArmCondition)firstCond;
|
||||
|
||||
context.SetItBlockStart(conditions);
|
||||
}
|
||||
|
||||
public static void Tbb(CodeGenContext context, uint rn, uint rm, bool h)
|
||||
{
|
||||
context.Arm64Assembler.Mov(context.RegisterAllocator.RemapGprRegister(RegisterUtils.PcRegister), context.Pc);
|
||||
|
||||
context.AddPendingTableBranch(rn, rm, h);
|
||||
|
||||
context.Arm64Assembler.B(0);
|
||||
}
|
||||
|
||||
public unsafe static void WriteCallWithGuestAddress(
|
||||
CodeWriter writer,
|
||||
ref Assembler asm,
|
||||
RegisterAllocator regAlloc,
|
||||
TailMerger tailMerger,
|
||||
Action writeEpilogue,
|
||||
AddressTable<ulong> funcTable,
|
||||
IntPtr funcPtr,
|
||||
int spillBaseOffset,
|
||||
uint nextAddress,
|
||||
Operand guestAddress,
|
||||
bool isTail = false)
|
||||
{
|
||||
int tempRegister;
|
||||
|
||||
if (guestAddress.Kind == OperandKind.Constant)
|
||||
{
|
||||
tempRegister = regAlloc.AllocateTempGprRegister();
|
||||
|
||||
asm.Mov(Register(tempRegister), guestAddress.Value);
|
||||
asm.StrRiUn(Register(tempRegister), Register(regAlloc.FixedContextRegister), NativeContextOffsets.DispatchAddressOffset);
|
||||
|
||||
regAlloc.FreeTempGprRegister(tempRegister);
|
||||
}
|
||||
else
|
||||
{
|
||||
asm.StrRiUn(guestAddress, Register(regAlloc.FixedContextRegister), NativeContextOffsets.DispatchAddressOffset);
|
||||
}
|
||||
|
||||
tempRegister = regAlloc.FixedContextRegister == 1 ? 2 : 1;
|
||||
|
||||
if (!isTail)
|
||||
{
|
||||
WriteSpillSkipContext(ref asm, regAlloc, spillBaseOffset);
|
||||
}
|
||||
|
||||
Operand rn = Register(tempRegister);
|
||||
|
||||
if (regAlloc.FixedContextRegister != 0)
|
||||
{
|
||||
asm.Mov(Register(0), Register(regAlloc.FixedContextRegister));
|
||||
}
|
||||
|
||||
if (guestAddress.Kind == OperandKind.Constant && funcTable != null)
|
||||
{
|
||||
ulong funcPtrLoc = (ulong)Unsafe.AsPointer(ref funcTable.GetValue(guestAddress.Value));
|
||||
|
||||
asm.Mov(rn, funcPtrLoc & ~0xfffUL);
|
||||
asm.LdrRiUn(rn, rn, (int)(funcPtrLoc & 0xfffUL));
|
||||
}
|
||||
else
|
||||
{
|
||||
asm.Mov(rn, (ulong)funcPtr);
|
||||
}
|
||||
|
||||
if (isTail)
|
||||
{
|
||||
writeEpilogue();
|
||||
asm.Br(rn);
|
||||
}
|
||||
else
|
||||
{
|
||||
asm.Blr(rn);
|
||||
|
||||
asm.Mov(rn, nextAddress);
|
||||
asm.Cmp(Register(0), rn);
|
||||
|
||||
tailMerger.AddConditionalReturn(writer, asm, ArmCondition.Ne);
|
||||
|
||||
WriteFillSkipContext(ref asm, regAlloc, spillBaseOffset);
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteSpillSkipContext(ref Assembler asm, RegisterAllocator regAlloc, int spillOffset)
|
||||
{
|
||||
WriteSpillOrFillSkipContext(ref asm, regAlloc, spillOffset, spill: true);
|
||||
}
|
||||
|
||||
private static void WriteFillSkipContext(ref Assembler asm, RegisterAllocator regAlloc, int spillOffset)
|
||||
{
|
||||
WriteSpillOrFillSkipContext(ref asm, regAlloc, spillOffset, spill: false);
|
||||
}
|
||||
|
||||
private static void WriteSpillOrFillSkipContext(ref Assembler asm, RegisterAllocator regAlloc, int spillOffset, bool spill)
|
||||
{
|
||||
uint gprMask = regAlloc.UsedGprsMask & ((1u << regAlloc.FixedContextRegister) | (1u << regAlloc.FixedPageTableRegister));
|
||||
|
||||
while (gprMask != 0)
|
||||
{
|
||||
int reg = BitOperations.TrailingZeroCount(gprMask);
|
||||
|
||||
if (reg < 31 && (gprMask & (2u << reg)) != 0 && spillOffset < RegisterSaveRestore.Encodable9BitsOffsetLimit)
|
||||
{
|
||||
if (spill)
|
||||
{
|
||||
asm.StpRiUn(Register(reg), Register(reg + 1), Register(SpIndex), spillOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
asm.LdpRiUn(Register(reg), Register(reg + 1), Register(SpIndex), spillOffset);
|
||||
}
|
||||
|
||||
gprMask &= ~(3u << reg);
|
||||
spillOffset += 16;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (spill)
|
||||
{
|
||||
asm.StrRiUn(Register(reg), Register(SpIndex), spillOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
asm.LdrRiUn(Register(reg), Register(SpIndex), spillOffset);
|
||||
}
|
||||
|
||||
gprMask &= ~(1u << reg);
|
||||
spillOffset += 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Operand Register(int register, OperandType type = OperandType.I64)
|
||||
{
|
||||
return new Operand(register, RegisterType.Integer, type);
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user