mirror of
https://github.com/Ryujinx/Ryujinx.git
synced 2024-12-27 09:59:33 +01:00
aloha
This commit is contained in:
commit
b7e1d9930d
230 changed files with 17548 additions and 0 deletions
63
.gitattributes
vendored
Normal file
63
.gitattributes
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
###############################################################################
|
||||
# Set default behavior to automatically normalize line endings.
|
||||
###############################################################################
|
||||
* text=auto
|
||||
|
||||
###############################################################################
|
||||
# Set default behavior for command prompt diff.
|
||||
#
|
||||
# This is need for earlier builds of msysgit that does not have it on by
|
||||
# default for csharp files.
|
||||
# Note: This is only used by command line
|
||||
###############################################################################
|
||||
#*.cs diff=csharp
|
||||
|
||||
###############################################################################
|
||||
# Set the merge driver for project and solution files
|
||||
#
|
||||
# Merging from the command prompt will add diff markers to the files if there
|
||||
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
||||
# the diff markers are never inserted). Diff markers may cause the following
|
||||
# file extensions to fail to load in VS. An alternative would be to treat
|
||||
# these files as binary and thus will always conflict and require user
|
||||
# intervention with every merge. To do so, just uncomment the entries below
|
||||
###############################################################################
|
||||
#*.sln merge=binary
|
||||
#*.csproj merge=binary
|
||||
#*.vbproj merge=binary
|
||||
#*.vcxproj merge=binary
|
||||
#*.vcproj merge=binary
|
||||
#*.dbproj merge=binary
|
||||
#*.fsproj merge=binary
|
||||
#*.lsproj merge=binary
|
||||
#*.wixproj merge=binary
|
||||
#*.modelproj merge=binary
|
||||
#*.sqlproj merge=binary
|
||||
#*.wwaproj merge=binary
|
||||
|
||||
###############################################################################
|
||||
# behavior for image files
|
||||
#
|
||||
# image files are treated as binary by default.
|
||||
###############################################################################
|
||||
#*.jpg binary
|
||||
#*.png binary
|
||||
#*.gif binary
|
||||
|
||||
###############################################################################
|
||||
# diff behavior for common document formats
|
||||
#
|
||||
# Convert binary document formats to text before diffing them. This feature
|
||||
# is only available from the command line. Turn it on by uncommenting the
|
||||
# entries below.
|
||||
###############################################################################
|
||||
#*.doc diff=astextplain
|
||||
#*.DOC diff=astextplain
|
||||
#*.docx diff=astextplain
|
||||
#*.DOCX diff=astextplain
|
||||
#*.dot diff=astextplain
|
||||
#*.DOT diff=astextplain
|
||||
#*.pdf diff=astextplain
|
||||
#*.PDF diff=astextplain
|
||||
#*.rtf diff=astextplain
|
||||
#*.RTF diff=astextplain
|
160
.gitignore
vendored
Normal file
160
.gitignore
vendored
Normal file
|
@ -0,0 +1,160 @@
|
|||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.sln.docstates
|
||||
.vs
|
||||
.vscode
|
||||
|
||||
# Build results
|
||||
|
||||
[Dd]ebug/
|
||||
[Rr]elease/
|
||||
x64/
|
||||
build/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
|
||||
# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
|
||||
!packages/*/build/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.log
|
||||
*.scc
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# NCrunch
|
||||
*.ncrunch*
|
||||
.*crunch*.local.xml
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.Publish.xml
|
||||
|
||||
# NuGet Packages Directory
|
||||
## TODO: If you have NuGet Package Restore enabled, uncomment the next line
|
||||
#packages/
|
||||
|
||||
# Windows Azure Build Output
|
||||
csx
|
||||
*.build.csdef
|
||||
|
||||
# Windows Store app package directory
|
||||
AppPackages/
|
||||
|
||||
# Others
|
||||
sql/
|
||||
*.Cache
|
||||
ClientBin/
|
||||
[Ss]tyle[Cc]op.*
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.[Pp]ublish.xml
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
packages/*
|
||||
*.config
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file to a newer
|
||||
# Visual Studio version. Backup files are not needed, because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
# SQL Server files
|
||||
App_Data/*.mdf
|
||||
App_Data/*.ldf
|
||||
|
||||
|
||||
#LightSwitch generated files
|
||||
GeneratedArtifacts/
|
||||
_Pvt_Extensions/
|
||||
ModelManifest.xml
|
||||
|
||||
# =========================
|
||||
# Windows detritus
|
||||
# =========================
|
||||
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Mac desktop service store files
|
||||
.DS_Store
|
366
GLScreen.cs
Normal file
366
GLScreen.cs
Normal file
|
@ -0,0 +1,366 @@
|
|||
// This code was written for the OpenTK library and has been released
|
||||
// to the Public Domain.
|
||||
// It is provided "as is" without express or implied warranty of any kind.
|
||||
|
||||
using Gal;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx
|
||||
{
|
||||
public class GLScreen : GameWindow
|
||||
{
|
||||
class ScreenTexture : IDisposable
|
||||
{
|
||||
private Switch Ns;
|
||||
private IGalRenderer Renderer;
|
||||
|
||||
private int Width;
|
||||
private int Height;
|
||||
private int TexHandle;
|
||||
|
||||
private int[] Pixels;
|
||||
|
||||
public ScreenTexture(Switch Ns, IGalRenderer Renderer, int Width, int Height)
|
||||
{
|
||||
this.Ns = Ns;
|
||||
this.Renderer = Renderer;
|
||||
this.Width = Width;
|
||||
this.Height = Height;
|
||||
|
||||
Pixels = new int[Width * Height];
|
||||
|
||||
TexHandle = GL.GenTexture();
|
||||
|
||||
GL.BindTexture(TextureTarget.Texture2D, TexHandle);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
|
||||
GL.TexImage2D(TextureTarget.Texture2D,
|
||||
0,
|
||||
PixelInternalFormat.Rgba,
|
||||
Width,
|
||||
Height,
|
||||
0,
|
||||
PixelFormat.Rgba,
|
||||
PixelType.UnsignedByte,
|
||||
IntPtr.Zero);
|
||||
}
|
||||
|
||||
public int Texture
|
||||
{
|
||||
get
|
||||
{
|
||||
UploadBitmap();
|
||||
|
||||
return TexHandle;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe void UploadBitmap()
|
||||
{
|
||||
if (Renderer.FrameBufferPtr == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
byte* SrcPtr = (byte*)IntPtr.Add(Ns.Ram, (int)Renderer.FrameBufferPtr);
|
||||
|
||||
for (int Y = 0; Y < Height; Y++)
|
||||
{
|
||||
for (int X = 0; X < Width; X++)
|
||||
{
|
||||
int SrcOffs = GetSwizzleOffset(X, Y, 4);
|
||||
|
||||
Pixels[X + Y * Width] = *((int*)(SrcPtr + SrcOffs));
|
||||
}
|
||||
}
|
||||
|
||||
GL.BindTexture(TextureTarget.Texture2D, TexHandle);
|
||||
GL.TexSubImage2D(TextureTarget.Texture2D,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
Width,
|
||||
Height,
|
||||
PixelFormat.Rgba,
|
||||
PixelType.UnsignedByte,
|
||||
Pixels);
|
||||
}
|
||||
|
||||
private int GetSwizzleOffset(int X, int Y, int Bpp)
|
||||
{
|
||||
int Pos;
|
||||
|
||||
Pos = (Y & 0x7f) >> 4;
|
||||
Pos += (X >> 4) << 3;
|
||||
Pos += (Y >> 7) * ((Width >> 4) << 3);
|
||||
Pos *= 1024;
|
||||
Pos += ((Y & 0xf) >> 3) << 9;
|
||||
Pos += ((X & 0xf) >> 3) << 8;
|
||||
Pos += ((Y & 0x7) >> 1) << 6;
|
||||
Pos += ((X & 0x7) >> 2) << 5;
|
||||
Pos += ((Y & 0x1) >> 0) << 4;
|
||||
Pos += ((X & 0x3) >> 0) << 2;
|
||||
|
||||
return Pos;
|
||||
}
|
||||
|
||||
private bool disposed;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
GL.DeleteTexture(TexHandle);
|
||||
}
|
||||
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string VtxShaderSource = @"
|
||||
#version 330 core
|
||||
|
||||
precision highp float;
|
||||
|
||||
layout(location = 0) in vec3 in_position;
|
||||
layout(location = 1) in vec4 in_color;
|
||||
layout(location = 2) in vec2 in_tex_coord;
|
||||
|
||||
out vec4 color;
|
||||
out vec2 tex_coord;
|
||||
|
||||
void main(void) {
|
||||
color = in_color;
|
||||
tex_coord = in_tex_coord;
|
||||
gl_Position = vec4((in_position + vec3(-960, 270, 0)) / vec3(1920, 270, 1), 1);
|
||||
}";
|
||||
|
||||
private string FragShaderSource = @"
|
||||
#version 330 core
|
||||
|
||||
precision highp float;
|
||||
|
||||
uniform sampler2D tex;
|
||||
|
||||
in vec4 color;
|
||||
in vec2 tex_coord;
|
||||
out vec4 out_frag_color;
|
||||
|
||||
void main(void) {
|
||||
out_frag_color = vec4(texture(tex, tex_coord).rgb, color.a);
|
||||
}";
|
||||
|
||||
private int VtxShaderHandle,
|
||||
FragShaderHandle,
|
||||
PrgShaderHandle;
|
||||
|
||||
private int VaoHandle;
|
||||
private int VboHandle;
|
||||
|
||||
private Switch Ns;
|
||||
|
||||
private IGalRenderer Renderer;
|
||||
|
||||
private ScreenTexture ScreenTex;
|
||||
|
||||
public GLScreen(Switch Ns, IGalRenderer Renderer)
|
||||
: base(1280, 720,
|
||||
new GraphicsMode(), "Ryujinx", 0,
|
||||
DisplayDevice.Default, 3, 3,
|
||||
GraphicsContextFlags.ForwardCompatible)
|
||||
{
|
||||
this.Ns = Ns;
|
||||
this.Renderer = Renderer;
|
||||
|
||||
ScreenTex = new ScreenTexture(Ns, Renderer, 1280, 720);
|
||||
}
|
||||
|
||||
protected override void OnLoad (EventArgs e)
|
||||
{
|
||||
VSync = VSyncMode.On;
|
||||
|
||||
CreateShaders();
|
||||
CreateVbo();
|
||||
|
||||
GL.Enable(EnableCap.Blend);
|
||||
GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
|
||||
}
|
||||
|
||||
protected override void OnUnload(EventArgs e)
|
||||
{
|
||||
ScreenTex.Dispose();
|
||||
|
||||
GL.DeleteVertexArray(VaoHandle);
|
||||
GL.DeleteBuffer(VboHandle);
|
||||
}
|
||||
|
||||
private void CreateVbo()
|
||||
{
|
||||
VaoHandle = GL.GenVertexArray();
|
||||
VboHandle = GL.GenBuffer();
|
||||
|
||||
uint[] Buffer = new uint[]
|
||||
{
|
||||
0xc4700000, 0x80000000, 0x00000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x45340000, 0x80000000, 0x00000000, 0xffffffff, 0x00000000, 0x3f800000, 0x00000000,
|
||||
0xc4700000, 0xc4070000, 0x00000000, 0xffffffff, 0x00000000, 0x00000000, 0x3f800000,
|
||||
0x45340000, 0xc4070000, 0x00000000, 0xffffffff, 0x00000000, 0x3f800000, 0x3f800000
|
||||
};
|
||||
|
||||
IntPtr Length = new IntPtr(Buffer.Length * 4);
|
||||
|
||||
GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
|
||||
GL.BufferData(BufferTarget.ArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
|
||||
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
|
||||
|
||||
GL.BindVertexArray(VaoHandle);
|
||||
|
||||
GL.EnableVertexAttribArray(0);
|
||||
|
||||
GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
|
||||
|
||||
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 28, 0);
|
||||
|
||||
GL.EnableVertexAttribArray(1);
|
||||
|
||||
GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
|
||||
|
||||
GL.VertexAttribPointer(1, 4, VertexAttribPointerType.UnsignedByte, false, 28, 12);
|
||||
|
||||
GL.EnableVertexAttribArray(2);
|
||||
|
||||
GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
|
||||
|
||||
GL.VertexAttribPointer(2, 2, VertexAttribPointerType.Float, false, 28, 20);
|
||||
|
||||
GL.BindVertexArray(0);
|
||||
}
|
||||
|
||||
private void CreateShaders()
|
||||
{
|
||||
VtxShaderHandle = GL.CreateShader(ShaderType.VertexShader);
|
||||
FragShaderHandle = GL.CreateShader(ShaderType.FragmentShader);
|
||||
|
||||
GL.ShaderSource(VtxShaderHandle, VtxShaderSource);
|
||||
GL.ShaderSource(FragShaderHandle, FragShaderSource);
|
||||
GL.CompileShader(VtxShaderHandle);
|
||||
GL.CompileShader(FragShaderHandle);
|
||||
|
||||
PrgShaderHandle = GL.CreateProgram();
|
||||
|
||||
GL.AttachShader(PrgShaderHandle, VtxShaderHandle);
|
||||
GL.AttachShader(PrgShaderHandle, FragShaderHandle);
|
||||
GL.LinkProgram(PrgShaderHandle);
|
||||
GL.UseProgram(PrgShaderHandle);
|
||||
|
||||
int TexLocation = GL.GetUniformLocation(PrgShaderHandle, "tex");
|
||||
|
||||
GL.Uniform1(TexLocation, 0);
|
||||
}
|
||||
|
||||
protected override void OnUpdateFrame(FrameEventArgs e)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
byte* Ptr = (byte*)IntPtr.Add(Ns.Ram, (int)Ns.Os.HidOffset);
|
||||
|
||||
int State = 0;
|
||||
|
||||
if (Keyboard[OpenTK.Input.Key.Up])
|
||||
{
|
||||
State |= 0x2000;
|
||||
}
|
||||
|
||||
if (Keyboard[OpenTK.Input.Key.Down])
|
||||
{
|
||||
State |= 0x8000;
|
||||
}
|
||||
|
||||
if (Keyboard[OpenTK.Input.Key.Left])
|
||||
{
|
||||
State |= 0x1000;
|
||||
}
|
||||
|
||||
if (Keyboard[OpenTK.Input.Key.Right])
|
||||
{
|
||||
State |= 0x4000;
|
||||
}
|
||||
|
||||
if (Keyboard[OpenTK.Input.Key.A])
|
||||
{
|
||||
State |= 0x1;
|
||||
}
|
||||
|
||||
if (Keyboard[OpenTK.Input.Key.S])
|
||||
{
|
||||
State |= 0x2;
|
||||
}
|
||||
|
||||
if (Keyboard[OpenTK.Input.Key.Z])
|
||||
{
|
||||
State |= 0x4;
|
||||
}
|
||||
|
||||
if (Keyboard[OpenTK.Input.Key.X])
|
||||
{
|
||||
State |= 0x8;
|
||||
}
|
||||
|
||||
if (Keyboard[OpenTK.Input.Key.Enter])
|
||||
{
|
||||
State |= 0x400;
|
||||
}
|
||||
|
||||
if (Keyboard[OpenTK.Input.Key.Tab])
|
||||
{
|
||||
State |= 0x800;
|
||||
}
|
||||
|
||||
*((int*)(Ptr + 0xae38)) = (int)State;
|
||||
}
|
||||
|
||||
if (Keyboard[OpenTK.Input.Key.Escape])
|
||||
{
|
||||
this.Exit();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnRenderFrame(FrameEventArgs e)
|
||||
{
|
||||
GL.Viewport(0, 0, 1280, 720);
|
||||
|
||||
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
|
||||
|
||||
RenderFb();
|
||||
|
||||
GL.UseProgram(PrgShaderHandle);
|
||||
|
||||
Renderer.RunActions();
|
||||
Renderer.BindTexture(0);
|
||||
Renderer.Render();
|
||||
|
||||
SwapBuffers();
|
||||
}
|
||||
|
||||
void RenderFb()
|
||||
{
|
||||
GL.ActiveTexture(TextureUnit.Texture0);
|
||||
GL.BindTexture(TextureTarget.Texture2D, ScreenTex.Texture);
|
||||
GL.BindVertexArray(VaoHandle);
|
||||
GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4);
|
||||
}
|
||||
}
|
||||
}
|
24
LICENSE.txt
Normal file
24
LICENSE.txt
Normal file
|
@ -0,0 +1,24 @@
|
|||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
57
Program.cs
Normal file
57
Program.cs
Normal file
|
@ -0,0 +1,57 @@
|
|||
using Gal;
|
||||
using Gal.OpenGL;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
IGalRenderer Renderer = new OpenGLRenderer();
|
||||
|
||||
Switch Ns = new Switch(Renderer);
|
||||
|
||||
if (args.Length == 1)
|
||||
{
|
||||
if (Directory.Exists(args[0]))
|
||||
{
|
||||
string[] RomFsFiles = Directory.GetFiles(args[0], "*.istorage");
|
||||
|
||||
if (RomFsFiles.Length > 0)
|
||||
{
|
||||
Console.WriteLine("Loading as cart with RomFS.");
|
||||
|
||||
Ns.Os.LoadCart(args[0], RomFsFiles[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Loading as cart WITHOUT RomFS.");
|
||||
|
||||
Ns.Os.LoadCart(args[0]);
|
||||
}
|
||||
}
|
||||
else if (File.Exists(args[0]))
|
||||
{
|
||||
Console.WriteLine("Loading as homebrew.");
|
||||
|
||||
Ns.Os.LoadProgram(args[0]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Please specify the folder with the NSOs/IStorage or a NSO/NRO.");
|
||||
}
|
||||
|
||||
using (GLScreen Screen = new GLScreen(Ns, Renderer))
|
||||
{
|
||||
Screen.Run(60.0);
|
||||
}
|
||||
|
||||
Ns.Os.StopAllProcesses();
|
||||
|
||||
Ns.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
12
Ryujinx.csproj
Normal file
12
Ryujinx.csproj
Normal file
|
@ -0,0 +1,12 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="OpenTK.NETCore" Version="1.1.2749.6433" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.4.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
25
Ryujinx.sln
Normal file
25
Ryujinx.sln
Normal file
|
@ -0,0 +1,25 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26730.8
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx", "Ryujinx.csproj", "{074045D4-3ED2-4711-9169-E385F2BFB5A0}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{074045D4-3ED2-4711-9169-E385F2BFB5A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{074045D4-3ED2-4711-9169-E385F2BFB5A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{074045D4-3ED2-4711-9169-E385F2BFB5A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{074045D4-3ED2-4711-9169-E385F2BFB5A0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {110169B3-3328-4730-8AB0-BA05BEF75C1A}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
57
Ryujinx/Cpu/ABitUtils.cs
Normal file
57
Ryujinx/Cpu/ABitUtils.cs
Normal file
|
@ -0,0 +1,57 @@
|
|||
namespace ChocolArm64
|
||||
{
|
||||
static class ABitUtils
|
||||
{
|
||||
public static int CountBitsSet(long Value)
|
||||
{
|
||||
int Count = 0;
|
||||
|
||||
for (int Bit = 0; Bit < 64; Bit++)
|
||||
{
|
||||
Count += (int)(Value >> Bit) & 1;
|
||||
}
|
||||
|
||||
return Count;
|
||||
}
|
||||
|
||||
public static int HighestBitSet32(int Value)
|
||||
{
|
||||
for (int Bit = 31; Bit >= 0; Bit--)
|
||||
{
|
||||
if (((Value >> Bit) & 1) != 0)
|
||||
{
|
||||
return Bit;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static long Replicate(long Bits, int Size)
|
||||
{
|
||||
long Output = 0;
|
||||
|
||||
for (int Bit = 0; Bit < 64; Bit += Size)
|
||||
{
|
||||
Output |= Bits << Bit;
|
||||
}
|
||||
|
||||
return Output;
|
||||
}
|
||||
|
||||
public static long FillWithOnes(int Bits)
|
||||
{
|
||||
return Bits == 64 ? -1L : (1L << Bits) - 1;
|
||||
}
|
||||
|
||||
public static long RotateRight(long Bits, int Shift, int Size)
|
||||
{
|
||||
return (Bits >> Shift) | (Bits << (Size - Shift));
|
||||
}
|
||||
|
||||
public static bool IsPow2(int Value)
|
||||
{
|
||||
return Value != 0 && (Value & (Value - 1)) == 0;
|
||||
}
|
||||
}
|
||||
}
|
374
Ryujinx/Cpu/AOpCodeTable.cs
Normal file
374
Ryujinx/Cpu/AOpCodeTable.cs
Normal file
|
@ -0,0 +1,374 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.Instruction;
|
||||
using System;
|
||||
|
||||
namespace ChocolArm64
|
||||
{
|
||||
static class AOpCodeTable
|
||||
{
|
||||
static AOpCodeTable()
|
||||
{
|
||||
#region "OpCode Table"
|
||||
//Integer
|
||||
Set("x0010001xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Add, typeof(AOpCodeAluImm));
|
||||
Set("x0001011xx0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Add, typeof(AOpCodeAluRs));
|
||||
Set("x0001011001xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Add, typeof(AOpCodeAluRx));
|
||||
Set("x0110001xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Adds, typeof(AOpCodeAluImm));
|
||||
Set("x0101011xx0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Adds, typeof(AOpCodeAluRs));
|
||||
Set("x0101011001xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Adds, typeof(AOpCodeAluRx));
|
||||
Set("0xx10000xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Adr, typeof(AOpCodeAdr));
|
||||
Set("1xx10000xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Adrp, typeof(AOpCodeAdr));
|
||||
Set("x00100100xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.And, typeof(AOpCodeAluImm));
|
||||
Set("x0001010xx0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.And, typeof(AOpCodeAluRs));
|
||||
Set("x11100100xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ands, typeof(AOpCodeAluImm));
|
||||
Set("x1101010xx0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ands, typeof(AOpCodeAluRs));
|
||||
Set("x0011010110xxxxx001010xxxxxxxxxx", AInstEmit.Asrv, typeof(AOpCodeAluRs));
|
||||
Set("000101xxxxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.B, typeof(AOpCodeBImmAl));
|
||||
Set("01010100xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.B_Cond, typeof(AOpCodeBImmCond));
|
||||
Set("x01100110xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Bfm, typeof(AOpCodeBfm));
|
||||
Set("x0001010xx1xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Bic, typeof(AOpCodeAluRs));
|
||||
Set("x1101010xx1xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Bics, typeof(AOpCodeAluRs));
|
||||
Set("100101xxxxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Bl, typeof(AOpCodeBImmAl));
|
||||
Set("11010110001xxxxx000000xxxxxxxxxx", AInstEmit.Blr, typeof(AOpCodeBReg));
|
||||
Set("11010110000xxxxx000000xxxxxxxxxx", AInstEmit.Br, typeof(AOpCodeBReg));
|
||||
Set("x0110101xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Cbnz, typeof(AOpCodeBImmCmp));
|
||||
Set("x0110100xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Cbz, typeof(AOpCodeBImmCmp));
|
||||
Set("x0111010010xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ccmn, typeof(AOpCodeCcmpImm));
|
||||
Set("x0111010010xxxxxxxxx00xxxxxxxxxx", AInstEmit.Ccmn, typeof(AOpCodeCcmpReg));
|
||||
Set("x1111010010xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ccmp, typeof(AOpCodeCcmpImm));
|
||||
Set("x1111010010xxxxxxxxx00xxxxxxxxxx", AInstEmit.Ccmp, typeof(AOpCodeCcmpReg));
|
||||
Set("11010101000000110011xxxx01011111", AInstEmit.Clrex, typeof(AOpCodeSystem));
|
||||
Set("x101101011000000000100xxxxxxxxxx", AInstEmit.Clz, typeof(AOpCodeAlu));
|
||||
Set("x0011010100xxxxxxxxx00xxxxxxxxxx", AInstEmit.Csel, typeof(AOpCodeCsel));
|
||||
Set("x0011010100xxxxxxxxx01xxxxxxxxxx", AInstEmit.Csinc, typeof(AOpCodeCsel));
|
||||
Set("x1011010100xxxxxxxxx00xxxxxxxxxx", AInstEmit.Csinv, typeof(AOpCodeCsel));
|
||||
Set("x1011010100xxxxxxxxx01xxxxxxxxxx", AInstEmit.Csneg, typeof(AOpCodeCsel));
|
||||
Set("11010101000000110011xxxx10111111", AInstEmit.Dmb, typeof(AOpCodeSystem));
|
||||
Set("11010101000000110011xxxx10011111", AInstEmit.Dsb, typeof(AOpCodeSystem));
|
||||
Set("x10100100xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Eor, typeof(AOpCodeAluImm));
|
||||
Set("x1001010xx0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Eor, typeof(AOpCodeAluRs));
|
||||
Set("x00100111x0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Extr, typeof(AOpCodeAluRs));
|
||||
Set("xx001000110xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Ldar, typeof(AOpCodeMemEx));
|
||||
Set("1x001000011xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Ldaxp, typeof(AOpCodeMemEx));
|
||||
Set("xx001000010xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Ldaxr, typeof(AOpCodeMemEx));
|
||||
Set("<<10100xx1xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldp, typeof(AOpCodeMemPair));
|
||||
Set("xx111000010xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeMemImm));
|
||||
Set("xx11100101xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeMemImm));
|
||||
Set("xx111000011xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeMemReg));
|
||||
Set("xx011000xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.LdrLit, typeof(AOpCodeMemLit));
|
||||
Set("0x1110001x0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemImm));
|
||||
Set("0x1110011xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemImm));
|
||||
Set("10111000100xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemImm));
|
||||
Set("1011100110xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemImm));
|
||||
Set("0x1110001x1xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemReg));
|
||||
Set("10111000101xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemReg));
|
||||
Set("xx001000010xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Ldxr, typeof(AOpCodeMemEx));
|
||||
Set("1x001000011xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Ldxp, typeof(AOpCodeMemEx));
|
||||
Set("x0011010110xxxxx001000xxxxxxxxxx", AInstEmit.Lslv, typeof(AOpCodeAluRs));
|
||||
Set("x0011010110xxxxx001001xxxxxxxxxx", AInstEmit.Lsrv, typeof(AOpCodeAluRs));
|
||||
Set("x0011011000xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Madd, typeof(AOpCodeMul));
|
||||
Set("x11100101xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Movk, typeof(AOpCodeMov));
|
||||
Set("x00100101xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Movn, typeof(AOpCodeMov));
|
||||
Set("x10100101xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Movz, typeof(AOpCodeMov));
|
||||
Set("110101010011xxxxxxxxxxxxxxxxxxxx", AInstEmit.Mrs, typeof(AOpCodeSystem));
|
||||
Set("110101010001xxxxxxxxxxxxxxxxxxxx", AInstEmit.Msr, typeof(AOpCodeSystem));
|
||||
Set("x0011011000xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Msub, typeof(AOpCodeMul));
|
||||
Set("11010101000000110010000000011111", AInstEmit.Nop, typeof(AOpCodeSystem));
|
||||
Set("x0101010xx1xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Orn, typeof(AOpCodeAluRs));
|
||||
Set("x01100100xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Orr, typeof(AOpCodeAluImm));
|
||||
Set("x0101010xx0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Orr, typeof(AOpCodeAluRs));
|
||||
Set("1111100110xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Pfrm, typeof(AOpCodeMemImm));
|
||||
Set("11011000xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Pfrm, typeof(AOpCodeMemLit));
|
||||
Set("x101101011000000000000xxxxxxxxxx", AInstEmit.Rbit, typeof(AOpCodeAlu));
|
||||
Set("11010110010xxxxx000000xxxxxxxxxx", AInstEmit.Ret, typeof(AOpCodeBReg));
|
||||
Set("x101101011000000000001xxxxxxxxxx", AInstEmit.Rev16, typeof(AOpCodeAlu));
|
||||
Set("x101101011000000000010xxxxxxxxxx", AInstEmit.Rev32, typeof(AOpCodeAlu));
|
||||
Set("1101101011000000000011xxxxxxxxxx", AInstEmit.Rev64, typeof(AOpCodeAlu));
|
||||
Set("x0011010110xxxxx001011xxxxxxxxxx", AInstEmit.Rorv, typeof(AOpCodeAluRs));
|
||||
Set("x00100110xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Sbfm, typeof(AOpCodeBfm));
|
||||
Set("x0011010110xxxxx000011xxxxxxxxxx", AInstEmit.Sdiv, typeof(AOpCodeAluRs));
|
||||
Set("10011011001xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Smaddl, typeof(AOpCodeMul));
|
||||
Set("10011011001xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Smsubl, typeof(AOpCodeMul));
|
||||
Set("10011011010xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Smulh, typeof(AOpCodeMul));
|
||||
Set("xx001000100xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Stlr, typeof(AOpCodeMemEx));
|
||||
Set("1x001000001xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Stlxp, typeof(AOpCodeMemEx));
|
||||
Set("xx001000000xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Stlxr, typeof(AOpCodeMemEx));
|
||||
Set("x010100xx0xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Stp, typeof(AOpCodeMemPair));
|
||||
Set("xx111000000xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeMemImm));
|
||||
Set("xx11100100xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeMemImm));
|
||||
Set("xx111000001xxxxxxxxx10xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeMemReg));
|
||||
Set("1x001000001xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Stxp, typeof(AOpCodeMemEx));
|
||||
Set("xx001000000xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Stxr, typeof(AOpCodeMemEx));
|
||||
Set("x1010001xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Sub, typeof(AOpCodeAluImm));
|
||||
Set("x1001011xx0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Sub, typeof(AOpCodeAluRs));
|
||||
Set("x1001011001xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Sub, typeof(AOpCodeAluRx));
|
||||
Set("x1110001xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Subs, typeof(AOpCodeAluImm));
|
||||
Set("x1101011xx0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Subs, typeof(AOpCodeAluRs));
|
||||
Set("x1101011001xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Subs, typeof(AOpCodeAluRx));
|
||||
Set("11010100000xxxxxxxxxxxxxxxx00001", AInstEmit.Svc, typeof(AOpCodeException));
|
||||
Set("1101010100001xxxxxxxxxxxxxxxxxxx", AInstEmit.Sys, typeof(AOpCodeSystem));
|
||||
Set("x0110111xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Tbnz, typeof(AOpCodeBImmTest));
|
||||
Set("x0110110xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Tbz, typeof(AOpCodeBImmTest));
|
||||
Set("x10100110xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ubfm, typeof(AOpCodeBfm));
|
||||
Set("x0011010110xxxxx000010xxxxxxxxxx", AInstEmit.Udiv, typeof(AOpCodeAluRs));
|
||||
Set("10011011101xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Umaddl, typeof(AOpCodeMul));
|
||||
Set("10011011101xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Umsubl, typeof(AOpCodeMul));
|
||||
Set("10011011110xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Umulh, typeof(AOpCodeMul));
|
||||
|
||||
//Vector
|
||||
Set("0x001110xx1xxxxx100001xxxxxxxxxx", AInstEmit.Add_V, typeof(AOpCodeSimdReg));
|
||||
Set("01011110xx110001101110xxxxxxxxxx", AInstEmit.Addp_S, typeof(AOpCodeSimd));
|
||||
Set("0x001110xx1xxxxx101111xxxxxxxxxx", AInstEmit.Addp_V, typeof(AOpCodeSimdReg));
|
||||
Set("0x001110<<110001101110xxxxxxxxxx", AInstEmit.Addv_V, typeof(AOpCodeSimd));
|
||||
Set("0x001110001xxxxx000111xxxxxxxxxx", AInstEmit.And_V, typeof(AOpCodeSimdReg));
|
||||
Set("0x001110011xxxxx000111xxxxxxxxxx", AInstEmit.Bic_V, typeof(AOpCodeSimdReg));
|
||||
Set("0x10111100000xxx<<x101xxxxxxxxxx", AInstEmit.Bic_Vi, typeof(AOpCodeSimdImm));
|
||||
Set("0x101110011xxxxx000111xxxxxxxxxx", AInstEmit.Bsl_V, typeof(AOpCodeSimdReg));
|
||||
Set("0x101110xx1xxxxx100011xxxxxxxxxx", AInstEmit.Cmeq_V, typeof(AOpCodeSimdReg));
|
||||
Set("0x001110xx100000100110xxxxxxxxxx", AInstEmit.Cmeq_V, typeof(AOpCodeSimd));
|
||||
Set("0x001110xx1xxxxx001111xxxxxxxxxx", AInstEmit.Cmge_V, typeof(AOpCodeSimdReg));
|
||||
Set("0x101110xx100000100010xxxxxxxxxx", AInstEmit.Cmge_V, typeof(AOpCodeSimd));
|
||||
Set("0x001110xx1xxxxx001101xxxxxxxxxx", AInstEmit.Cmgt_V, typeof(AOpCodeSimdReg));
|
||||
Set("0x001110xx100000100010xxxxxxxxxx", AInstEmit.Cmgt_V, typeof(AOpCodeSimd));
|
||||
Set("0x101110xx1xxxxx001101xxxxxxxxxx", AInstEmit.Cmhi_V, typeof(AOpCodeSimdReg));
|
||||
Set("0x101110xx1xxxxx001111xxxxxxxxxx", AInstEmit.Cmhs_V, typeof(AOpCodeSimdReg));
|
||||
Set("0x101110xx100000100110xxxxxxxxxx", AInstEmit.Cmle_V, typeof(AOpCodeSimd));
|
||||
Set("0x001110xx100000101010xxxxxxxxxx", AInstEmit.Cmlt_V, typeof(AOpCodeSimd));
|
||||
Set("0x00111000100000010110xxxxxxxxxx", AInstEmit.Cnt_V, typeof(AOpCodeSimd));
|
||||
Set("01011110000xxxxx000001xxxxxxxxxx", AInstEmit.Dup_S, typeof(AOpCodeSimdIns));
|
||||
Set("0x001110000xxxxx000011xxxxxxxxxx", AInstEmit.Dup_Gp, typeof(AOpCodeSimdIns));
|
||||
Set("0x001110000xxxxx000001xxxxxxxxxx", AInstEmit.Dup_V, typeof(AOpCodeSimdIns));
|
||||
Set("0x101110001xxxxx000111xxxxxxxxxx", AInstEmit.Eor_V, typeof(AOpCodeSimdReg));
|
||||
Set("00011110xx100000110000xxxxxxxxxx", AInstEmit.Fabs_S, typeof(AOpCodeSimd));
|
||||
Set("00011110xx1xxxxx001010xxxxxxxxxx", AInstEmit.Fadd_S, typeof(AOpCodeSimdReg));
|
||||
Set("0x0011100x1xxxxx110101xxxxxxxxxx", AInstEmit.Fadd_V, typeof(AOpCodeSimdReg));
|
||||
Set("00011110xx1xxxxxxxxx01xxxxx0xxxx", AInstEmit.Fccmp_S, typeof(AOpCodeSimdFcond));
|
||||
Set("00011110xx1xxxxx001000xxxxx0x000", AInstEmit.Fcmp_S, typeof(AOpCodeSimdReg));
|
||||
Set("00011110xx1xxxxxxxxx11xxxxxxxxxx", AInstEmit.Fcsel_S, typeof(AOpCodeSimdFcond));
|
||||
Set("00011110xx10001xx10000xxxxxxxxxx", AInstEmit.Fcvt_S, typeof(AOpCodeSimd));
|
||||
Set("x0011110xx110000000000xxxxxxxxxx", AInstEmit.Fcvtms_S, typeof(AOpCodeSimdCvt));
|
||||
Set("x0011110xx101000000000xxxxxxxxxx", AInstEmit.Fcvtps_S, typeof(AOpCodeSimdCvt));
|
||||
Set("x0011110xx111000000000xxxxxxxxxx", AInstEmit.Fcvtzs_S, typeof(AOpCodeSimdCvt));
|
||||
Set("0x0011101x100001101110xxxxxxxxxx", AInstEmit.Fcvtzs_V, typeof(AOpCodeSimd));
|
||||
Set("x0011110xx111001000000xxxxxxxxxx", AInstEmit.Fcvtzu_S, typeof(AOpCodeSimdCvt));
|
||||
Set("0x1011101x100001101110xxxxxxxxxx", AInstEmit.Fcvtzu_V, typeof(AOpCodeSimd));
|
||||
Set("0x1011110>>xxxxx111111xxxxxxxxxx", AInstEmit.Fcvtzu_V_Fix, typeof(AOpCodeSimdShImm));
|
||||
Set("x0011110xx011000xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzs_Fix, typeof(AOpCodeSimdCvt));
|
||||
Set("x0011110xx011001xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzu_Fix, typeof(AOpCodeSimdCvt));
|
||||
Set("00011110xx1xxxxx000110xxxxxxxxxx", AInstEmit.Fdiv_S, typeof(AOpCodeSimdReg));
|
||||
Set("00011110xx1xxxxx010010xxxxxxxxxx", AInstEmit.Fmax_S, typeof(AOpCodeSimdReg));
|
||||
Set("00011110xx1xxxxx011010xxxxxxxxxx", AInstEmit.Fmaxnm_S, typeof(AOpCodeSimdReg));
|
||||
Set("00011110xx1xxxxx010110xxxxxxxxxx", AInstEmit.Fmin_S, typeof(AOpCodeSimdReg));
|
||||
Set("00011110xx1xxxxx011110xxxxxxxxxx", AInstEmit.Fminnm_S, typeof(AOpCodeSimdReg));
|
||||
Set("0x0011100x1xxxxx110011xxxxxxxxxx", AInstEmit.Fmla_V, typeof(AOpCodeSimdReg));
|
||||
Set("0x0011111<<xxxxx0001x0xxxxxxxxxx", AInstEmit.Fmla_Vs, typeof(AOpCodeSimdRegElem));
|
||||
Set("00011110xx1xxxxxxxx100xxxxxxxxxx", AInstEmit.Fmov_S, typeof(AOpCodeSimdFmov));
|
||||
Set("0xx0111100000xxx111101xxxxxxxxxx", AInstEmit.Fmov_V, typeof(AOpCodeSimdImm));
|
||||
Set("x0011110xx100110000000xxxxxxxxxx", AInstEmit.Fmov_Ftoi, typeof(AOpCodeSimdCvt));
|
||||
Set("x0011110xx100111000000xxxxxxxxxx", AInstEmit.Fmov_Itof, typeof(AOpCodeSimdCvt));
|
||||
Set("x0011110xx101110000000xxxxxxxxxx", AInstEmit.Fmov_Ftoi1, typeof(AOpCodeSimdCvt));
|
||||
Set("x0011110xx101111000000xxxxxxxxxx", AInstEmit.Fmov_Itof1, typeof(AOpCodeSimdCvt));
|
||||
Set("00011110xx1xxxxx000010xxxxxxxxxx", AInstEmit.Fmul_S, typeof(AOpCodeSimdReg));
|
||||
Set("0x1011100x1xxxxx110111xxxxxxxxxx", AInstEmit.Fmul_V, typeof(AOpCodeSimdReg));
|
||||
Set("0x0011111<<xxxxx1001x0xxxxxxxxxx", AInstEmit.Fmul_Vs, typeof(AOpCodeSimdRegElem));
|
||||
Set("00011110xx100001010000xxxxxxxxxx", AInstEmit.Fneg_S, typeof(AOpCodeSimdReg));
|
||||
Set("00011110xx1xxxxx100010xxxxxxxxxx", AInstEmit.Fnmul_S, typeof(AOpCodeSimdReg));
|
||||
Set("00011110xx100110010000xxxxxxxxxx", AInstEmit.Frinta_S, typeof(AOpCodeSimd));
|
||||
Set("00011110xx100101010000xxxxxxxxxx", AInstEmit.Frintm_S, typeof(AOpCodeSimd));
|
||||
Set("00011110xx100001110000xxxxxxxxxx", AInstEmit.Fsqrt_S, typeof(AOpCodeSimd));
|
||||
Set("00011110xx1xxxxx001110xxxxxxxxxx", AInstEmit.Fsub_S, typeof(AOpCodeSimdReg));
|
||||
Set("0x0011101x1xxxxx110101xxxxxxxxxx", AInstEmit.Fsub_V, typeof(AOpCodeSimdReg));
|
||||
Set("01001110000xxxxx000111xxxxxxxxxx", AInstEmit.Ins_Gp, typeof(AOpCodeSimdIns));
|
||||
Set("01101110000xxxxx0xxxx1xxxxxxxxxx", AInstEmit.Ins_V, typeof(AOpCodeSimdIns));
|
||||
Set("0x00110001000000xxxxxxxxxxxxxxxx", AInstEmit.Ld__V, typeof(AOpCodeSimdMemMult));
|
||||
Set("0x001100110xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ld__V, typeof(AOpCodeSimdMemMult));
|
||||
Set("xx10110xx1xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldp, typeof(AOpCodeSimdMemPair));
|
||||
Set("xx111100x10xxxxxxxxx00xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemImm));
|
||||
Set("xx111100x10xxxxxxxxx01xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemImm));
|
||||
Set("xx111100x10xxxxxxxxx11xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemImm));
|
||||
Set("xx111101x1xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemImm));
|
||||
Set("xx111100x11xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemReg));
|
||||
Set("xx011100xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.LdrLit, typeof(AOpCodeSimdMemLit));
|
||||
Set("0x001110xx1xxxxx100101xxxxxxxxxx", AInstEmit.Mla_V, typeof(AOpCodeSimdReg));
|
||||
Set("0x00111100000xxx0xx001xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm));
|
||||
Set("0x00111100000xxx10x001xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm));
|
||||
Set("0x00111100000xxx110x01xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm));
|
||||
Set("0xx0111100000xxx111001xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm));
|
||||
Set("0x001110xx1xxxxx100111xxxxxxxxxx", AInstEmit.Mul_V, typeof(AOpCodeSimdReg));
|
||||
Set("0x10111100000xxx0xx001xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm));
|
||||
Set("0x10111100000xxx10x001xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm));
|
||||
Set("0x10111100000xxx110x01xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm));
|
||||
Set("0x101110xx100000101110xxxxxxxxxx", AInstEmit.Neg_V, typeof(AOpCodeSimdReg));
|
||||
Set("0x10111000100000010110xxxxxxxxxx", AInstEmit.Not_V, typeof(AOpCodeSimd));
|
||||
Set("0x001110101xxxxx000111xxxxxxxxxx", AInstEmit.Orr_V, typeof(AOpCodeSimdReg));
|
||||
Set("0x00111100000xxx<<x101xxxxxxxxxx", AInstEmit.Orr_Vi, typeof(AOpCodeSimdImm));
|
||||
Set("0x001110<<1xxxxx000100xxxxxxxxxx", AInstEmit.Saddw_V, typeof(AOpCodeSimdReg));
|
||||
Set("0x0011100x100001110110xxxxxxxxxx", AInstEmit.Scvtf_V, typeof(AOpCodeSimd));
|
||||
Set("010111110>>>>xxx010101xxxxxxxxxx", AInstEmit.Shl_S, typeof(AOpCodeSimdShImm));
|
||||
Set("0x0011110>>>>xxx010101xxxxxxxxxx", AInstEmit.Shl_V, typeof(AOpCodeSimdShImm));
|
||||
Set("0x001110<<1xxxxx011001xxxxxxxxxx", AInstEmit.Smax_V, typeof(AOpCodeSimdReg));
|
||||
Set("0x001110<<1xxxxx011011xxxxxxxxxx", AInstEmit.Smin_V, typeof(AOpCodeSimdReg));
|
||||
Set("0x00111100>>>xxx101001xxxxxxxxxx", AInstEmit.Sshll_V, typeof(AOpCodeSimdShImm));
|
||||
Set("010111110>>>>xxx000001xxxxxxxxxx", AInstEmit.Sshr_S, typeof(AOpCodeSimdShImm));
|
||||
Set("0x0011110>>>>xxx000001xxxxxxxxxx", AInstEmit.Sshr_V, typeof(AOpCodeSimdShImm));
|
||||
Set("0x00110000000000xxxxxxxxxxxxxxxx", AInstEmit.St__V, typeof(AOpCodeSimdMemMult));
|
||||
Set("0x001100100xxxxxxxxxxxxxxxxxxxxx", AInstEmit.St__V, typeof(AOpCodeSimdMemMult));
|
||||
Set("xx10110xx0xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Stp, typeof(AOpCodeSimdMemPair));
|
||||
Set("xx111100x00xxxxxxxxx00xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm));
|
||||
Set("xx111100x00xxxxxxxxx01xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm));
|
||||
Set("xx111100x00xxxxxxxxx11xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm));
|
||||
Set("xx111101x0xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm));
|
||||
Set("xx111100x01xxxxxxxxx10xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemReg));
|
||||
Set("01111110xx1xxxxx100001xxxxxxxxxx", AInstEmit.Sub_S, typeof(AOpCodeSimdReg));
|
||||
Set("0x101110xx1xxxxx100001xxxxxxxxxx", AInstEmit.Sub_V, typeof(AOpCodeSimdReg));
|
||||
Set("x0011110xx100010000000xxxxxxxxxx", AInstEmit.Scvtf_Gp, typeof(AOpCodeSimdCvt));
|
||||
Set("010111100x100001110110xxxxxxxxxx", AInstEmit.Scvtf_S, typeof(AOpCodeSimd));
|
||||
Set("0x001110000xxxxx0xx000xxxxxxxxxx", AInstEmit.Tbl_V, typeof(AOpCodeSimdTbl));
|
||||
Set("001011100x110000001110xxxxxxxxxx", AInstEmit.Uaddlv_V, typeof(AOpCodeSimd));
|
||||
Set("01101110<<110000001110xxxxxxxxxx", AInstEmit.Uaddlv_V, typeof(AOpCodeSimd));
|
||||
Set("0x101110<<1xxxxx000100xxxxxxxxxx", AInstEmit.Uaddw_V, typeof(AOpCodeSimdReg));
|
||||
Set("x0011110xx100011000000xxxxxxxxxx", AInstEmit.Ucvtf_Gp, typeof(AOpCodeSimdCvt));
|
||||
Set("011111100x100001110110xxxxxxxxxx", AInstEmit.Ucvtf_V, typeof(AOpCodeSimdReg));
|
||||
Set("0x001110000xxxxx001111xxxxxxxxxx", AInstEmit.Umov_S, typeof(AOpCodeSimdIns));
|
||||
Set("0x101110xx1xxxxx010001xxxxxxxxxx", AInstEmit.Ushl_V, typeof(AOpCodeSimdReg));
|
||||
Set("0x10111100>>>xxx101001xxxxxxxxxx", AInstEmit.Ushll_V, typeof(AOpCodeSimdShImm));
|
||||
Set("0x1011110>>>>xxx000001xxxxxxxxxx", AInstEmit.Ushr_V, typeof(AOpCodeSimdShImm));
|
||||
Set("0x1011110>>>>xxx000101xxxxxxxxxx", AInstEmit.Usra_V, typeof(AOpCodeSimdShImm));
|
||||
Set("0x001110xx0xxxxx000110xxxxxxxxxx", AInstEmit.Uzp1_V, typeof(AOpCodeSimdReg));
|
||||
Set("0x001110<<100001001010xxxxxxxxxx", AInstEmit.Xtn_V, typeof(AOpCodeSimd));
|
||||
#endregion
|
||||
}
|
||||
|
||||
private class TreeNode
|
||||
{
|
||||
public int Mask;
|
||||
public int Value;
|
||||
|
||||
public TreeNode Next;
|
||||
public TreeNode Child;
|
||||
|
||||
public AInst Inst;
|
||||
|
||||
public TreeNode(int Mask, int Value, AInst Inst)
|
||||
{
|
||||
this.Mask = Mask;
|
||||
this.Value = Value;
|
||||
this.Inst = Inst;
|
||||
}
|
||||
}
|
||||
|
||||
private static TreeNode Root;
|
||||
|
||||
private static void Set(string Encoding, AInstEmitter Emitter, Type Type)
|
||||
{
|
||||
Set(Encoding, new AInst(Emitter, Type));
|
||||
}
|
||||
|
||||
private static void Set(string Encoding, AInst Inst)
|
||||
{
|
||||
int Bit = Encoding.Length - 1;
|
||||
int Value = 0;
|
||||
int XMask = 0;
|
||||
int ZCount = 0;
|
||||
int OCount = 0;
|
||||
|
||||
int[] ZPos = new int[Encoding.Length];
|
||||
int[] OPos = new int[Encoding.Length];
|
||||
|
||||
for (int Index = 0; Index < Encoding.Length; Index++, Bit--)
|
||||
{
|
||||
//Note: < and > are used on special encodings.
|
||||
//The < means that we should never have ALL bits with the '<' set.
|
||||
//So, when the encoding has <<, it means that 00, 01, and 10 are valid,
|
||||
//but not 11. <<< is 000, 001, ..., 110 but NOT 111, and so on...
|
||||
//For >, the invalid value is zero. So, for << 01, 10 and 11 are valid,
|
||||
//but 00 isn't.
|
||||
switch (Encoding[Index])
|
||||
{
|
||||
case '0': /* Do nothing. */ break;
|
||||
case '1': Value |= 1 << Bit; break;
|
||||
case 'x': XMask |= 1 << Bit; break;
|
||||
|
||||
case '<': OPos[OCount++] = Bit; break;
|
||||
case '>': ZPos[ZCount++] = Bit; break;
|
||||
|
||||
default: throw new ArgumentException(nameof(Encoding));
|
||||
}
|
||||
}
|
||||
|
||||
if (ZCount + OCount == 0)
|
||||
{
|
||||
InsertTop(XMask, Value, Inst);
|
||||
}
|
||||
else if ((ZCount & OCount) != 0)
|
||||
{
|
||||
for (int OCtr = 0; (uint)OCtr < (1 << OCount) - 1; OCtr++)
|
||||
{
|
||||
int OVal = Value;
|
||||
|
||||
for (int O = 0; O < OCount; O++)
|
||||
{
|
||||
OVal |= ((OCtr >> O) & 1) << OPos[O];
|
||||
}
|
||||
|
||||
InsertWithCtr(1, 1 << ZCount, ZCount, ZPos, XMask, OVal, Inst);
|
||||
}
|
||||
}
|
||||
else if (ZCount != 0)
|
||||
{
|
||||
InsertWithCtr(1, 1 << ZCount, ZCount, ZPos, XMask, Value, Inst);
|
||||
}
|
||||
else if (OCount != 0)
|
||||
{
|
||||
InsertWithCtr(0, (1 << OCount) - 1, OCount, OPos, XMask, Value, Inst);
|
||||
}
|
||||
}
|
||||
|
||||
private static void InsertWithCtr(
|
||||
int Start,
|
||||
int End,
|
||||
int Cnt,
|
||||
int[] Pos,
|
||||
int XMask,
|
||||
int Value,
|
||||
AInst Inst)
|
||||
{
|
||||
for (int Ctr = Start; (uint)Ctr < End; Ctr++)
|
||||
{
|
||||
int Val = Value;
|
||||
|
||||
for (int Index = 0; Index < Cnt; Index++)
|
||||
{
|
||||
Val |= ((Ctr >> Index) & 1) << Pos[Index];
|
||||
}
|
||||
|
||||
InsertTop(XMask, Val, Inst);
|
||||
}
|
||||
}
|
||||
|
||||
private static void InsertTop(int XMask, int Value, AInst Inst)
|
||||
{
|
||||
TreeNode Next = Root;
|
||||
|
||||
Root = new TreeNode(~XMask, Value, Inst);
|
||||
|
||||
Root.Next = Next;
|
||||
}
|
||||
|
||||
public static AInst GetInst(int OpCode)
|
||||
{
|
||||
TreeNode Node = Root;
|
||||
|
||||
do
|
||||
{
|
||||
if ((OpCode & Node.Mask) == Node.Value)
|
||||
{
|
||||
return Node.Inst;
|
||||
}
|
||||
}
|
||||
while ((Node = Node.Next) != null);
|
||||
|
||||
return AInst.Undefined;
|
||||
}
|
||||
}
|
||||
}
|
74
Ryujinx/Cpu/AThread.cs
Normal file
74
Ryujinx/Cpu/AThread.cs
Normal file
|
@ -0,0 +1,74 @@
|
|||
using ChocolArm64.Memory;
|
||||
using ChocolArm64.State;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace ChocolArm64
|
||||
{
|
||||
class AThread
|
||||
{
|
||||
public ARegisters Registers { get; private set; }
|
||||
public AMemory Memory { get; private set; }
|
||||
|
||||
private ATranslator Translator;
|
||||
private Thread Work;
|
||||
|
||||
public event EventHandler WorkFinished;
|
||||
|
||||
public int ThreadId => Registers.ThreadId;
|
||||
|
||||
public bool IsAlive => Work.IsAlive;
|
||||
|
||||
public long EntryPoint { get; private set; }
|
||||
public int Priority { get; private set; }
|
||||
|
||||
public AThread(AMemory Memory, long EntryPoint = 0, int Priority = 0)
|
||||
{
|
||||
this.Memory = Memory;
|
||||
this.EntryPoint = EntryPoint;
|
||||
this.Priority = Priority;
|
||||
|
||||
Registers = new ARegisters();
|
||||
Translator = new ATranslator(this);
|
||||
}
|
||||
|
||||
public void StopExecution() => Translator.StopExecution();
|
||||
|
||||
public void Execute() => Execute(EntryPoint);
|
||||
|
||||
public void Execute(long EntryPoint)
|
||||
{
|
||||
Work = new Thread(delegate()
|
||||
{
|
||||
Translator.ExecuteSubroutine(EntryPoint);
|
||||
|
||||
Memory.RemoveMonitor(ThreadId);
|
||||
|
||||
WorkFinished?.Invoke(this, EventArgs.Empty);
|
||||
});
|
||||
|
||||
if (Priority < 12)
|
||||
{
|
||||
Work.Priority = ThreadPriority.Highest;
|
||||
}
|
||||
else if (Priority < 24)
|
||||
{
|
||||
Work.Priority = ThreadPriority.AboveNormal;
|
||||
}
|
||||
else if (Priority < 36)
|
||||
{
|
||||
Work.Priority = ThreadPriority.Normal;
|
||||
}
|
||||
else if (Priority < 48)
|
||||
{
|
||||
Work.Priority = ThreadPriority.BelowNormal;
|
||||
}
|
||||
else
|
||||
{
|
||||
Work.Priority = ThreadPriority.Lowest;
|
||||
}
|
||||
|
||||
Work.Start();
|
||||
}
|
||||
}
|
||||
}
|
104
Ryujinx/Cpu/ATranslatedSub.cs
Normal file
104
Ryujinx/Cpu/ATranslatedSub.cs
Normal file
|
@ -0,0 +1,104 @@
|
|||
using ChocolArm64.Memory;
|
||||
using ChocolArm64.State;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64
|
||||
{
|
||||
class ATranslatedSub
|
||||
{
|
||||
private delegate long AA64Subroutine(ARegisters Register, AMemory Memory);
|
||||
|
||||
private AA64Subroutine ExecDelegate;
|
||||
|
||||
private bool HasDelegate;
|
||||
|
||||
public static Type[] FixedArgTypes { get; private set; }
|
||||
|
||||
public static int RegistersArgIdx { get; private set; }
|
||||
public static int MemoryArgIdx { get; private set; }
|
||||
|
||||
public DynamicMethod Method { get; private set; }
|
||||
|
||||
public HashSet<long> SubCalls { get; private set; }
|
||||
|
||||
public List<ARegister> Params { get; private set; }
|
||||
|
||||
public bool NeedsReJit { get; private set; }
|
||||
|
||||
public ATranslatedSub()
|
||||
{
|
||||
SubCalls = new HashSet<long>();
|
||||
}
|
||||
|
||||
public ATranslatedSub(DynamicMethod Method, List<ARegister> Params) : this()
|
||||
{
|
||||
if (Params == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(Params));
|
||||
}
|
||||
|
||||
this.Method = Method;
|
||||
this.Params = Params;
|
||||
}
|
||||
|
||||
static ATranslatedSub()
|
||||
{
|
||||
MethodInfo MthdInfo = typeof(AA64Subroutine).GetMethod("Invoke");
|
||||
|
||||
ParameterInfo[] Params = MthdInfo.GetParameters();
|
||||
|
||||
FixedArgTypes = new Type[Params.Length];
|
||||
|
||||
for (int Index = 0; Index < Params.Length; Index++)
|
||||
{
|
||||
Type ParamType = Params[Index].ParameterType;
|
||||
|
||||
FixedArgTypes[Index] = ParamType;
|
||||
|
||||
if (ParamType == typeof(ARegisters))
|
||||
{
|
||||
RegistersArgIdx = Index;
|
||||
}
|
||||
else if (ParamType == typeof(AMemory))
|
||||
{
|
||||
MemoryArgIdx = Index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public long Execute(ARegisters Registers, AMemory Memory)
|
||||
{
|
||||
if (!HasDelegate)
|
||||
{
|
||||
string Name = $"{Method.Name}_Dispatch";
|
||||
|
||||
DynamicMethod Mthd = new DynamicMethod(Name, typeof(long), FixedArgTypes);
|
||||
|
||||
ILGenerator Generator = Mthd.GetILGenerator();
|
||||
|
||||
Generator.EmitLdargSeq(FixedArgTypes.Length);
|
||||
|
||||
foreach (ARegister Reg in Params)
|
||||
{
|
||||
Generator.EmitLdarg(RegistersArgIdx);
|
||||
|
||||
Generator.Emit(OpCodes.Ldfld, Reg.GetField());
|
||||
}
|
||||
|
||||
Generator.Emit(OpCodes.Call, Method);
|
||||
Generator.Emit(OpCodes.Ret);
|
||||
|
||||
ExecDelegate = (AA64Subroutine)Mthd.CreateDelegate(typeof(AA64Subroutine));
|
||||
|
||||
HasDelegate = true;
|
||||
}
|
||||
|
||||
return ExecDelegate(Registers, Memory);
|
||||
}
|
||||
|
||||
public void MarkForReJit() => NeedsReJit = true;
|
||||
}
|
||||
}
|
106
Ryujinx/Cpu/ATranslator.cs
Normal file
106
Ryujinx/Cpu/ATranslator.cs
Normal file
|
@ -0,0 +1,106 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.Instruction;
|
||||
using ChocolArm64.Translation;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64
|
||||
{
|
||||
class ATranslator
|
||||
{
|
||||
private Dictionary<long, ATranslatedSub> CachedSubs;
|
||||
|
||||
public AThread Thread { get; private set; }
|
||||
|
||||
private bool KeepRunning;
|
||||
|
||||
public ATranslator(AThread Parent)
|
||||
{
|
||||
this.Thread = Parent;
|
||||
|
||||
CachedSubs = new Dictionary<long, ATranslatedSub>();
|
||||
|
||||
KeepRunning = true;
|
||||
}
|
||||
|
||||
public void StopExecution() => KeepRunning = false;
|
||||
|
||||
public void ExecuteSubroutine(long Position)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (CachedSubs.TryGetValue(Position, out ATranslatedSub Sub) && !Sub.NeedsReJit)
|
||||
{
|
||||
Position = Sub.Execute(Thread.Registers, Thread.Memory);
|
||||
}
|
||||
else
|
||||
{
|
||||
Position = TranslateSubroutine(Position).Execute(Thread.Registers, Thread.Memory);
|
||||
}
|
||||
}
|
||||
while (Position != 0 && KeepRunning);
|
||||
}
|
||||
|
||||
public bool TryGetCachedSub(AOpCode OpCode, out ATranslatedSub Sub)
|
||||
{
|
||||
if (OpCode.Emitter != AInstEmit.Bl)
|
||||
{
|
||||
Sub = null;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return TryGetCachedSub(((AOpCodeBImmAl)OpCode).Imm, out Sub);
|
||||
}
|
||||
|
||||
public bool TryGetCachedSub(long Position, out ATranslatedSub Sub)
|
||||
{
|
||||
return CachedSubs.TryGetValue(Position, out Sub);
|
||||
}
|
||||
|
||||
public bool HasCachedSub(long Position)
|
||||
{
|
||||
return CachedSubs.ContainsKey(Position);
|
||||
}
|
||||
|
||||
private ATranslatedSub TranslateSubroutine(long Position)
|
||||
{
|
||||
(ABlock[] Graph, ABlock Root) Cfg = ADecoder.DecodeSubroutine(this, Position);
|
||||
|
||||
AILEmitterCtx Context = new AILEmitterCtx(
|
||||
this,
|
||||
Cfg.Graph,
|
||||
Cfg.Root);
|
||||
|
||||
if (Context.CurrBlock.Position != Position)
|
||||
{
|
||||
Context.Emit(OpCodes.Br, Context.GetLabel(Position));
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
Context.EmitOpCode();
|
||||
}
|
||||
while (Context.AdvanceOpCode());
|
||||
|
||||
//Mark all methods that calls this method for ReJiting,
|
||||
//since we can now call it directly which is faster.
|
||||
foreach (ATranslatedSub TS in CachedSubs.Values)
|
||||
{
|
||||
if (TS.SubCalls.Contains(Position))
|
||||
{
|
||||
TS.MarkForReJit();
|
||||
}
|
||||
}
|
||||
|
||||
ATranslatedSub Subroutine = Context.GetSubroutine();
|
||||
|
||||
if (!CachedSubs.TryAdd(Position, Subroutine))
|
||||
{
|
||||
CachedSubs[Position] = Subroutine;
|
||||
}
|
||||
|
||||
return Subroutine;
|
||||
}
|
||||
}
|
||||
}
|
35
Ryujinx/Cpu/Decoder/ABlock.cs
Normal file
35
Ryujinx/Cpu/Decoder/ABlock.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class ABlock
|
||||
{
|
||||
public long Position { get; set; }
|
||||
public long EndPosition { get; set; }
|
||||
|
||||
public ABlock Next { get; set; }
|
||||
public ABlock Branch { get; set; }
|
||||
|
||||
public List<AOpCode> OpCodes { get; private set; }
|
||||
|
||||
public ABlock()
|
||||
{
|
||||
OpCodes = new List<AOpCode>();
|
||||
}
|
||||
|
||||
public ABlock(long Position) : this()
|
||||
{
|
||||
this.Position = Position;
|
||||
}
|
||||
|
||||
public AOpCode GetLastOp()
|
||||
{
|
||||
if (OpCodes.Count > 0)
|
||||
{
|
||||
return OpCodes[OpCodes.Count - 1];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
22
Ryujinx/Cpu/Decoder/ACond.cs
Normal file
22
Ryujinx/Cpu/Decoder/ACond.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
enum ACond
|
||||
{
|
||||
Eq = 0,
|
||||
Ne = 1,
|
||||
Ge_Un = 2,
|
||||
Lt_Un = 3,
|
||||
Mi = 4,
|
||||
Pl = 5,
|
||||
Vs = 6,
|
||||
Vc = 7,
|
||||
Gt_Un = 8,
|
||||
Le_Un = 9,
|
||||
Ge = 10,
|
||||
Lt = 11,
|
||||
Gt = 12,
|
||||
Le = 13,
|
||||
Al = 14,
|
||||
Nv = 15
|
||||
}
|
||||
}
|
10
Ryujinx/Cpu/Decoder/ADataOp.cs
Normal file
10
Ryujinx/Cpu/Decoder/ADataOp.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
enum ADataOp
|
||||
{
|
||||
Adr = 0,
|
||||
Arithmetic = 1,
|
||||
Logical = 2,
|
||||
BitField = 3
|
||||
}
|
||||
}
|
206
Ryujinx/Cpu/Decoder/ADecoder.cs
Normal file
206
Ryujinx/Cpu/Decoder/ADecoder.cs
Normal file
|
@ -0,0 +1,206 @@
|
|||
using ChocolArm64.Instruction;
|
||||
using ChocolArm64.Memory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
static class ADecoder
|
||||
{
|
||||
public static (ABlock[] Graph, ABlock Root) DecodeSubroutine(ATranslator Translator, long Start)
|
||||
{
|
||||
Dictionary<long, ABlock> Visited = new Dictionary<long, ABlock>();
|
||||
Dictionary<long, ABlock> VisitedEnd = new Dictionary<long, ABlock>();
|
||||
|
||||
Queue<ABlock> Blocks = new Queue<ABlock>();
|
||||
|
||||
ABlock Enqueue(long Position)
|
||||
{
|
||||
if (!Visited.TryGetValue(Position, out ABlock Output))
|
||||
{
|
||||
Output = new ABlock(Position);
|
||||
|
||||
Blocks.Enqueue(Output);
|
||||
|
||||
Visited.Add(Position, Output);
|
||||
}
|
||||
|
||||
return Output;
|
||||
}
|
||||
|
||||
ABlock Root = Enqueue(Start);
|
||||
|
||||
while (Blocks.Count > 0)
|
||||
{
|
||||
ABlock Current = Blocks.Dequeue();
|
||||
|
||||
FillBlock(Translator.Thread.Memory, Current);
|
||||
|
||||
//Set child blocks. "Branch" is the block the branch instruction
|
||||
//points to (when taken), "Next" is the block at the next address,
|
||||
//executed when the branch is not taken. For Unconditional Branches
|
||||
//(except BL/BLR that are sub calls) or end of executable, Next is null.
|
||||
if (Current.OpCodes.Count > 0)
|
||||
{
|
||||
bool HasCachedSub = false;
|
||||
|
||||
AOpCode LastOp = Current.GetLastOp();
|
||||
|
||||
if (LastOp is AOpCodeBImm Op)
|
||||
{
|
||||
if (Op.Emitter == AInstEmit.Bl)
|
||||
{
|
||||
HasCachedSub = Translator.HasCachedSub(Op.Imm);
|
||||
}
|
||||
else
|
||||
{
|
||||
Current.Branch = Enqueue(Op.Imm);
|
||||
}
|
||||
}
|
||||
|
||||
if ((!(LastOp is AOpCodeBImmAl) &&
|
||||
!(LastOp is AOpCodeBReg)) || HasCachedSub)
|
||||
{
|
||||
Current.Next = Enqueue(Current.EndPosition);
|
||||
}
|
||||
}
|
||||
|
||||
//If we have on the tree two blocks with the same end position,
|
||||
//then we need to split the bigger block and have two small blocks,
|
||||
//the end position of the bigger "Current" block should then be == to
|
||||
//the position of the "Smaller" block.
|
||||
while (VisitedEnd.TryGetValue(Current.EndPosition, out ABlock Smaller))
|
||||
{
|
||||
if (Current.Position > Smaller.Position)
|
||||
{
|
||||
ABlock Temp = Smaller;
|
||||
|
||||
Smaller = Current;
|
||||
Current = Temp;
|
||||
}
|
||||
|
||||
Current.EndPosition = Smaller.Position;
|
||||
Current.Next = Smaller;
|
||||
Current.Branch = null;
|
||||
|
||||
Current.OpCodes.RemoveRange(
|
||||
Current.OpCodes.Count - Smaller.OpCodes.Count,
|
||||
Smaller.OpCodes.Count);
|
||||
|
||||
VisitedEnd[Smaller.EndPosition] = Smaller;
|
||||
}
|
||||
|
||||
VisitedEnd.Add(Current.EndPosition, Current);
|
||||
}
|
||||
|
||||
//Make and sort Graph blocks array by position.
|
||||
ABlock[] Graph = new ABlock[Visited.Count];
|
||||
|
||||
while (Visited.Count > 0)
|
||||
{
|
||||
ulong FirstPos = ulong.MaxValue;
|
||||
|
||||
foreach (ABlock Block in Visited.Values)
|
||||
{
|
||||
if (FirstPos > (ulong)Block.Position)
|
||||
FirstPos = (ulong)Block.Position;
|
||||
}
|
||||
|
||||
ABlock Current = Visited[(long)FirstPos];
|
||||
|
||||
do
|
||||
{
|
||||
Graph[Graph.Length - Visited.Count] = Current;
|
||||
|
||||
Visited.Remove(Current.Position);
|
||||
|
||||
Current = Current.Next;
|
||||
}
|
||||
while (Current != null);
|
||||
}
|
||||
|
||||
return (Graph, Root);
|
||||
}
|
||||
|
||||
private static void FillBlock(AMemory Memory, ABlock Block)
|
||||
{
|
||||
long Position = Block.Position;
|
||||
|
||||
AOpCode OpCode;
|
||||
|
||||
do
|
||||
{
|
||||
OpCode = DecodeOpCode(Memory, Position);
|
||||
|
||||
Block.OpCodes.Add(OpCode);
|
||||
|
||||
Position += 4;
|
||||
}
|
||||
while (!(IsBranch(OpCode) || IsException(OpCode)));
|
||||
|
||||
Block.EndPosition = Position;
|
||||
}
|
||||
|
||||
private static bool IsBranch(AOpCode OpCode)
|
||||
{
|
||||
return OpCode is AOpCodeBImm ||
|
||||
OpCode is AOpCodeBReg;
|
||||
}
|
||||
|
||||
private static bool IsException(AOpCode OpCode)
|
||||
{
|
||||
return OpCode.Emitter == AInstEmit.Svc ||
|
||||
OpCode.Emitter == AInstEmit.Und;
|
||||
}
|
||||
|
||||
public static AOpCode DecodeOpCode(AMemory Memory, long Position)
|
||||
{
|
||||
int OpCode = Memory.ReadInt32(Position);
|
||||
|
||||
AInst Inst = AOpCodeTable.GetInst(OpCode);
|
||||
|
||||
AOpCode DecodedOpCode = new AOpCode(AInst.Undefined, Position);
|
||||
|
||||
if (Inst.Type != null)
|
||||
{
|
||||
DecodedOpCode = CreateOpCode(Inst.Type, Inst, Position, OpCode);
|
||||
}
|
||||
|
||||
return DecodedOpCode;
|
||||
}
|
||||
|
||||
private delegate object OpActivator(AInst Inst, long Position, int OpCode);
|
||||
|
||||
private static Dictionary<Type, OpActivator> Activators = new Dictionary<Type, OpActivator>();
|
||||
|
||||
private static AOpCode CreateOpCode(Type Type, AInst Inst, long Position, int OpCode)
|
||||
{
|
||||
if (Type == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(Type));
|
||||
}
|
||||
|
||||
if (!Activators.TryGetValue(Type, out OpActivator CreateInstance))
|
||||
{
|
||||
Type[] ArgTypes = new Type[] { typeof(AInst), typeof(long), typeof(int) };
|
||||
|
||||
DynamicMethod Mthd = new DynamicMethod($"{Type.Name}_Create", Type, ArgTypes);
|
||||
|
||||
ILGenerator Generator = Mthd.GetILGenerator();
|
||||
|
||||
Generator.Emit(OpCodes.Ldarg_0);
|
||||
Generator.Emit(OpCodes.Ldarg_1);
|
||||
Generator.Emit(OpCodes.Ldarg_2);
|
||||
Generator.Emit(OpCodes.Newobj, Type.GetConstructor(ArgTypes));
|
||||
Generator.Emit(OpCodes.Ret);
|
||||
|
||||
CreateInstance = (OpActivator)Mthd.CreateDelegate(typeof(OpActivator));
|
||||
|
||||
Activators.Add(Type, CreateInstance);
|
||||
}
|
||||
|
||||
return (AOpCode)CreateInstance(Inst, Position, OpCode);
|
||||
}
|
||||
}
|
||||
}
|
107
Ryujinx/Cpu/Decoder/ADecoderHelper.cs
Normal file
107
Ryujinx/Cpu/Decoder/ADecoderHelper.cs
Normal file
|
@ -0,0 +1,107 @@
|
|||
using System;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
static class ADecoderHelper
|
||||
{
|
||||
public struct BitMask
|
||||
{
|
||||
public long WMask;
|
||||
public long TMask;
|
||||
public int Pos;
|
||||
public int Shift;
|
||||
public bool IsUndefined;
|
||||
|
||||
public static BitMask Invalid => new BitMask { IsUndefined = true };
|
||||
}
|
||||
|
||||
public static BitMask DecodeBitMask(int OpCode, bool Immediate)
|
||||
{
|
||||
int ImmS = (OpCode >> 10) & 0x3f;
|
||||
int ImmR = (OpCode >> 16) & 0x3f;
|
||||
|
||||
int N = (OpCode >> 22) & 1;
|
||||
int SF = (OpCode >> 31) & 1;
|
||||
|
||||
int Length = ABitUtils.HighestBitSet32((~ImmS & 0x3f) | (N << 6));
|
||||
|
||||
if (Length < 1 || (SF == 0 && N != 0))
|
||||
{
|
||||
return BitMask.Invalid;
|
||||
}
|
||||
|
||||
int Size = 1 << Length;
|
||||
|
||||
int Levels = Size - 1;
|
||||
|
||||
int S = ImmS & Levels;
|
||||
int R = ImmR & Levels;
|
||||
|
||||
if (Immediate && S == Levels)
|
||||
{
|
||||
return BitMask.Invalid;
|
||||
}
|
||||
|
||||
long WMask = ABitUtils.FillWithOnes(S + 1);
|
||||
long TMask = ABitUtils.FillWithOnes(((S - R) & Levels) + 1);
|
||||
|
||||
if (R > 0)
|
||||
{
|
||||
WMask = ABitUtils.RotateRight(WMask, R, Size);
|
||||
WMask &= ABitUtils.FillWithOnes(Size);
|
||||
}
|
||||
|
||||
return new BitMask()
|
||||
{
|
||||
WMask = ABitUtils.Replicate(WMask, Size),
|
||||
TMask = ABitUtils.Replicate(TMask, Size),
|
||||
|
||||
Pos = ImmS,
|
||||
Shift = ImmR
|
||||
};
|
||||
}
|
||||
|
||||
public static long DecodeImm8Float(long Imm, int Size)
|
||||
{
|
||||
int E = 0, F = 0;
|
||||
|
||||
switch (Size)
|
||||
{
|
||||
case 0: E = 8; F = 23; break;
|
||||
case 1: E = 11; F = 52; break;
|
||||
|
||||
default: throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
|
||||
long Value = (Imm & 0x3f) << F - 4;
|
||||
|
||||
long EBit = (Imm >> 6) & 1;
|
||||
long SBit = (Imm >> 7) & 1;
|
||||
|
||||
if (EBit != 0)
|
||||
{
|
||||
Value |= (1L << E - 3) - 1 << F + 2;
|
||||
}
|
||||
|
||||
Value |= (EBit ^ 1) << F + E - 1;
|
||||
Value |= SBit << F + E;
|
||||
|
||||
return Value;
|
||||
}
|
||||
|
||||
public static long DecodeImm26_2(int OpCode)
|
||||
{
|
||||
return ((long)OpCode << 38) >> 36;
|
||||
}
|
||||
|
||||
public static long DecodeImmS19_2(int OpCode)
|
||||
{
|
||||
return (((long)OpCode << 40) >> 43) & ~3;
|
||||
}
|
||||
|
||||
public static long DecodeImmS14_2(int OpCode)
|
||||
{
|
||||
return (((long)OpCode << 45) >> 48) & ~3;
|
||||
}
|
||||
}
|
||||
}
|
14
Ryujinx/Cpu/Decoder/AIntType.cs
Normal file
14
Ryujinx/Cpu/Decoder/AIntType.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
enum AIntType
|
||||
{
|
||||
UInt8 = 0,
|
||||
UInt16 = 1,
|
||||
UInt32 = 2,
|
||||
UInt64 = 3,
|
||||
Int8 = 4,
|
||||
Int16 = 5,
|
||||
Int32 = 6,
|
||||
Int64 = 7
|
||||
}
|
||||
}
|
36
Ryujinx/Cpu/Decoder/AOpCode.cs
Normal file
36
Ryujinx/Cpu/Decoder/AOpCode.cs
Normal file
|
@ -0,0 +1,36 @@
|
|||
using ChocolArm64.Instruction;
|
||||
using ChocolArm64.State;
|
||||
using System;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCode : IAOpCode
|
||||
{
|
||||
public long Position { get; private set; }
|
||||
|
||||
public AInstEmitter Emitter { get; protected set; }
|
||||
public ARegisterSize RegisterSize { get; protected set; }
|
||||
|
||||
public AOpCode(AInst Inst, long Position)
|
||||
{
|
||||
this.Position = Position;
|
||||
|
||||
RegisterSize = ARegisterSize.Int64;
|
||||
|
||||
Emitter = Inst.Emitter;
|
||||
}
|
||||
|
||||
public int GetBitsCount()
|
||||
{
|
||||
switch (RegisterSize)
|
||||
{
|
||||
case ARegisterSize.Int32: return 32;
|
||||
case ARegisterSize.Int64: return 64;
|
||||
case ARegisterSize.SIMD64: return 64;
|
||||
case ARegisterSize.SIMD128: return 128;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
18
Ryujinx/Cpu/Decoder/AOpCodeAdr.cs
Normal file
18
Ryujinx/Cpu/Decoder/AOpCodeAdr.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeAdr : AOpCode
|
||||
{
|
||||
public int Rd { get; private set; }
|
||||
public long Imm { get; private set; }
|
||||
|
||||
public AOpCodeAdr(AInst Inst, long Position, int OpCode) : base(Inst, Position)
|
||||
{
|
||||
Rd = OpCode & 0x1f;
|
||||
|
||||
Imm = ADecoderHelper.DecodeImmS19_2(OpCode);
|
||||
Imm |= ((long)OpCode >> 29) & 3;
|
||||
}
|
||||
}
|
||||
}
|
24
Ryujinx/Cpu/Decoder/AOpCodeAlu.cs
Normal file
24
Ryujinx/Cpu/Decoder/AOpCodeAlu.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
using ChocolArm64.Instruction;
|
||||
using ChocolArm64.State;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeAlu : AOpCode, IAOpCodeAlu
|
||||
{
|
||||
public int Rd { get; protected set; }
|
||||
public int Rn { get; private set; }
|
||||
|
||||
public ADataOp DataOp { get; private set; }
|
||||
|
||||
public AOpCodeAlu(AInst Inst, long Position, int OpCode) : base(Inst, Position)
|
||||
{
|
||||
Rd = (OpCode >> 0) & 0x1f;
|
||||
Rn = (OpCode >> 5) & 0x1f;
|
||||
DataOp = (ADataOp)((OpCode >> 24) & 0x3);
|
||||
|
||||
RegisterSize = (OpCode >> 31) != 0
|
||||
? ARegisterSize.Int64
|
||||
: ARegisterSize.Int32;
|
||||
}
|
||||
}
|
||||
}
|
41
Ryujinx/Cpu/Decoder/AOpCodeAluImm.cs
Normal file
41
Ryujinx/Cpu/Decoder/AOpCodeAluImm.cs
Normal file
|
@ -0,0 +1,41 @@
|
|||
using ChocolArm64.Instruction;
|
||||
using System;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeAluImm : AOpCodeAlu, IAOpCodeAluImm
|
||||
{
|
||||
public long Imm { get; private set; }
|
||||
|
||||
public AOpCodeAluImm(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
|
||||
{
|
||||
if (DataOp == ADataOp.Arithmetic)
|
||||
{
|
||||
Imm = (OpCode >> 10) & 0xfff;
|
||||
|
||||
int Shift = (OpCode >> 22) & 3;
|
||||
|
||||
//Assert Shift < 2
|
||||
|
||||
Imm <<= Shift * 12;
|
||||
}
|
||||
else if (DataOp == ADataOp.Logical)
|
||||
{
|
||||
var BM = ADecoderHelper.DecodeBitMask(OpCode, true);
|
||||
|
||||
if (BM.IsUndefined)
|
||||
{
|
||||
Emitter = AInstEmit.Und;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Imm = BM.WMask;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException(nameof(OpCode));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
21
Ryujinx/Cpu/Decoder/AOpCodeAluRs.cs
Normal file
21
Ryujinx/Cpu/Decoder/AOpCodeAluRs.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeAluRs : AOpCodeAlu, IAOpCodeAluRs
|
||||
{
|
||||
public int Shift { get; private set; }
|
||||
public int Rm { get; private set; }
|
||||
|
||||
public AShiftType ShiftType { get; private set; }
|
||||
|
||||
public AOpCodeAluRs(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
|
||||
{
|
||||
Shift = (OpCode >> 10) & 0x3f;
|
||||
Rm = (OpCode >> 16) & 0x1f;
|
||||
ShiftType = (AShiftType)((OpCode >> 22) & 0x3);
|
||||
|
||||
//Assert ShiftType != 3
|
||||
}
|
||||
}
|
||||
}
|
19
Ryujinx/Cpu/Decoder/AOpCodeAluRx.cs
Normal file
19
Ryujinx/Cpu/Decoder/AOpCodeAluRx.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeAluRx : AOpCodeAlu, IAOpCodeAluRx
|
||||
{
|
||||
public int Shift { get; private set; }
|
||||
public int Rm { get; private set; }
|
||||
|
||||
public AIntType IntType { get; private set; }
|
||||
|
||||
public AOpCodeAluRx(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
|
||||
{
|
||||
Shift = (OpCode >> 10) & 0x7;
|
||||
IntType = (AIntType)((OpCode >> 13) & 0x7);
|
||||
Rm = (OpCode >> 16) & 0x1f;
|
||||
}
|
||||
}
|
||||
}
|
11
Ryujinx/Cpu/Decoder/AOpCodeBImm.cs
Normal file
11
Ryujinx/Cpu/Decoder/AOpCodeBImm.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeBImm : AOpCode
|
||||
{
|
||||
public long Imm { get; protected set; }
|
||||
|
||||
public AOpCodeBImm(AInst Inst, long Position) : base(Inst, Position) { }
|
||||
}
|
||||
}
|
12
Ryujinx/Cpu/Decoder/AOpCodeBImmAl.cs
Normal file
12
Ryujinx/Cpu/Decoder/AOpCodeBImmAl.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeBImmAl : AOpCodeBImm
|
||||
{
|
||||
public AOpCodeBImmAl(AInst Inst, long Position, int OpCode) : base(Inst, Position)
|
||||
{
|
||||
Imm = Position + ADecoderHelper.DecodeImm26_2(OpCode);
|
||||
}
|
||||
}
|
||||
}
|
16
Ryujinx/Cpu/Decoder/AOpCodeBImmCmp.cs
Normal file
16
Ryujinx/Cpu/Decoder/AOpCodeBImmCmp.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeBImmCmp : AOpCodeBImm
|
||||
{
|
||||
public int Rt { get; private set; }
|
||||
|
||||
public AOpCodeBImmCmp(AInst Inst, long Position, int OpCode) : base(Inst, Position)
|
||||
{
|
||||
Rt = OpCode & 0x1f;
|
||||
|
||||
Imm = Position + ADecoderHelper.DecodeImmS19_2(OpCode);
|
||||
}
|
||||
}
|
||||
}
|
25
Ryujinx/Cpu/Decoder/AOpCodeBImmCond.cs
Normal file
25
Ryujinx/Cpu/Decoder/AOpCodeBImmCond.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeBImmCond : AOpCodeBImm, IAOpCodeCond
|
||||
{
|
||||
public ACond Cond { get; private set; }
|
||||
|
||||
public AOpCodeBImmCond(AInst Inst, long Position, int OpCode) : base(Inst, Position)
|
||||
{
|
||||
int O0 = (OpCode >> 4) & 1;
|
||||
|
||||
if (O0 != 0)
|
||||
{
|
||||
Emitter = AInstEmit.Und;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Cond = (ACond)(OpCode & 0xf);
|
||||
|
||||
Imm = Position + ADecoderHelper.DecodeImmS19_2(OpCode);
|
||||
}
|
||||
}
|
||||
}
|
20
Ryujinx/Cpu/Decoder/AOpCodeBImmTest.cs
Normal file
20
Ryujinx/Cpu/Decoder/AOpCodeBImmTest.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeBImmTest : AOpCodeBImm
|
||||
{
|
||||
public int Rt { get; private set; }
|
||||
public int Pos { get; private set; }
|
||||
|
||||
public AOpCodeBImmTest(AInst Inst, long Position, int OpCode) : base(Inst, Position)
|
||||
{
|
||||
Rt = OpCode & 0x1f;
|
||||
|
||||
Imm = Position + ADecoderHelper.DecodeImmS14_2(OpCode);
|
||||
|
||||
Pos = (OpCode >> 19) & 0x1f;
|
||||
Pos |= (OpCode >> 26) & 0x20;
|
||||
}
|
||||
}
|
||||
}
|
24
Ryujinx/Cpu/Decoder/AOpCodeBReg.cs
Normal file
24
Ryujinx/Cpu/Decoder/AOpCodeBReg.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeBReg : AOpCode
|
||||
{
|
||||
public int Rn { get; private set; }
|
||||
|
||||
public AOpCodeBReg(AInst Inst, long Position, int OpCode) : base(Inst, Position)
|
||||
{
|
||||
int Op4 = (OpCode >> 0) & 0x1f;
|
||||
int Op2 = (OpCode >> 16) & 0x1f;
|
||||
|
||||
if (Op2 != 0b11111 || Op4 != 0b00000)
|
||||
{
|
||||
Emitter = AInstEmit.Und;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Rn = (OpCode >> 5) & 0x1f;
|
||||
}
|
||||
}
|
||||
}
|
29
Ryujinx/Cpu/Decoder/AOpCodeBfm.cs
Normal file
29
Ryujinx/Cpu/Decoder/AOpCodeBfm.cs
Normal file
|
@ -0,0 +1,29 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeBfm : AOpCodeAlu
|
||||
{
|
||||
public long WMask { get; private set; }
|
||||
public long TMask { get; private set; }
|
||||
public int Pos { get; private set; }
|
||||
public int Shift { get; private set; }
|
||||
|
||||
public AOpCodeBfm(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
|
||||
{
|
||||
var BM = ADecoderHelper.DecodeBitMask(OpCode, false);
|
||||
|
||||
if (BM.IsUndefined)
|
||||
{
|
||||
Emitter = AInstEmit.Und;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
WMask = BM.WMask;
|
||||
TMask = BM.TMask;
|
||||
Pos = BM.Pos;
|
||||
Shift = BM.Shift;
|
||||
}
|
||||
}
|
||||
}
|
31
Ryujinx/Cpu/Decoder/AOpCodeCcmp.cs
Normal file
31
Ryujinx/Cpu/Decoder/AOpCodeCcmp.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using ChocolArm64.Instruction;
|
||||
using ChocolArm64.State;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeCcmp : AOpCodeAlu, IAOpCodeCond
|
||||
{
|
||||
public int NZCV { get; private set; }
|
||||
protected int RmImm;
|
||||
|
||||
public ACond Cond { get; private set; }
|
||||
|
||||
public AOpCodeCcmp(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
|
||||
{
|
||||
int O3 = (OpCode >> 4) & 1;
|
||||
|
||||
if (O3 != 0)
|
||||
{
|
||||
Emitter = AInstEmit.Und;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
NZCV = (OpCode >> 0) & 0xf;
|
||||
Cond = (ACond)((OpCode >> 12) & 0xf);
|
||||
RmImm = (OpCode >> 16) & 0x1f;
|
||||
|
||||
Rd = ARegisters.ZRIndex;
|
||||
}
|
||||
}
|
||||
}
|
11
Ryujinx/Cpu/Decoder/AOpCodeCcmpImm.cs
Normal file
11
Ryujinx/Cpu/Decoder/AOpCodeCcmpImm.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeCcmpImm : AOpCodeCcmp, IAOpCodeAluImm
|
||||
{
|
||||
public long Imm => RmImm;
|
||||
|
||||
public AOpCodeCcmpImm(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) { }
|
||||
}
|
||||
}
|
15
Ryujinx/Cpu/Decoder/AOpCodeCcmpReg.cs
Normal file
15
Ryujinx/Cpu/Decoder/AOpCodeCcmpReg.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeCcmpReg : AOpCodeCcmp, IAOpCodeAluRs
|
||||
{
|
||||
public int Rm => RmImm;
|
||||
|
||||
public int Shift => 0;
|
||||
|
||||
public AShiftType ShiftType => AShiftType.Lsl;
|
||||
|
||||
public AOpCodeCcmpReg(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) { }
|
||||
}
|
||||
}
|
17
Ryujinx/Cpu/Decoder/AOpCodeCsel.cs
Normal file
17
Ryujinx/Cpu/Decoder/AOpCodeCsel.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeCsel : AOpCodeAlu, IAOpCodeCond
|
||||
{
|
||||
public int Rm { get; private set; }
|
||||
|
||||
public ACond Cond { get; private set; }
|
||||
|
||||
public AOpCodeCsel(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
|
||||
{
|
||||
Rm = (OpCode >> 16) & 0x1f;
|
||||
Cond = (ACond)((OpCode >> 12) & 0xf);
|
||||
}
|
||||
}
|
||||
}
|
14
Ryujinx/Cpu/Decoder/AOpCodeException.cs
Normal file
14
Ryujinx/Cpu/Decoder/AOpCodeException.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeException : AOpCode
|
||||
{
|
||||
public int Id { get; private set; }
|
||||
|
||||
public AOpCodeException(AInst Inst, long Position, int OpCode) : base(Inst, Position)
|
||||
{
|
||||
Id = (OpCode >> 5) & 0xfff;
|
||||
}
|
||||
}
|
||||
}
|
19
Ryujinx/Cpu/Decoder/AOpCodeMem.cs
Normal file
19
Ryujinx/Cpu/Decoder/AOpCodeMem.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeMem : AOpCode
|
||||
{
|
||||
public int Rt { get; protected set; }
|
||||
public int Rn { get; protected set; }
|
||||
public int Size { get; protected set; }
|
||||
public bool Extend64 { get; protected set; }
|
||||
|
||||
public AOpCodeMem(AInst Inst, long Position, int OpCode) : base(Inst, Position)
|
||||
{
|
||||
Rt = (OpCode >> 0) & 0x1f;
|
||||
Rn = (OpCode >> 5) & 0x1f;
|
||||
Size = (OpCode >> 30) & 0x3;
|
||||
}
|
||||
}
|
||||
}
|
16
Ryujinx/Cpu/Decoder/AOpCodeMemEx.cs
Normal file
16
Ryujinx/Cpu/Decoder/AOpCodeMemEx.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeMemEx : AOpCodeMem
|
||||
{
|
||||
public int Rt2 { get; private set; }
|
||||
public int Rs { get; private set; }
|
||||
|
||||
public AOpCodeMemEx(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
|
||||
{
|
||||
Rt2 = (OpCode >> 10) & 0x1f;
|
||||
Rs = (OpCode >> 16) & 0x1f;
|
||||
}
|
||||
}
|
||||
}
|
53
Ryujinx/Cpu/Decoder/AOpCodeMemImm.cs
Normal file
53
Ryujinx/Cpu/Decoder/AOpCodeMemImm.cs
Normal file
|
@ -0,0 +1,53 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeMemImm : AOpCodeMem
|
||||
{
|
||||
public long Imm { get; protected set; }
|
||||
public bool WBack { get; protected set; }
|
||||
public bool PostIdx { get; protected set; }
|
||||
protected bool Unscaled { get; private set; }
|
||||
|
||||
private enum MemOp
|
||||
{
|
||||
Unscaled = 0,
|
||||
PostIndexed = 1,
|
||||
Unprivileged = 2,
|
||||
PreIndexed = 3,
|
||||
Unsigned
|
||||
}
|
||||
|
||||
public AOpCodeMemImm(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
|
||||
{
|
||||
Extend64 = ((OpCode >> 22) & 3) == 2;
|
||||
WBack = ((OpCode >> 24) & 1) == 0;
|
||||
|
||||
//The type is not valid for the Unsigned Immediate 12-bits encoding,
|
||||
//because the bits 11:10 are used for the larger Immediate offset.
|
||||
MemOp Type = WBack ? (MemOp)((OpCode >> 10) & 3) : MemOp.Unsigned;
|
||||
|
||||
PostIdx = Type == MemOp.PostIndexed;
|
||||
Unscaled = Type == MemOp.Unscaled ||
|
||||
Type == MemOp.Unprivileged;
|
||||
|
||||
//Unscaled and Unprivileged doesn't write back,
|
||||
//but they do use the 9-bits Signed Immediate.
|
||||
if (Unscaled)
|
||||
{
|
||||
WBack = false;
|
||||
}
|
||||
|
||||
if (WBack || Unscaled)
|
||||
{
|
||||
//9-bits Signed Immediate.
|
||||
Imm = (OpCode << 43) >> 55;
|
||||
}
|
||||
else
|
||||
{
|
||||
//12-bits Unsigned Immediate.
|
||||
Imm = ((OpCode >> 10) & 0xfff) << Size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
28
Ryujinx/Cpu/Decoder/AOpCodeMemLit.cs
Normal file
28
Ryujinx/Cpu/Decoder/AOpCodeMemLit.cs
Normal file
|
@ -0,0 +1,28 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeMemLit : AOpCode, IAOpCodeLit
|
||||
{
|
||||
public int Rt { get; private set; }
|
||||
public long Imm { get; private set; }
|
||||
public int Size { get; private set; }
|
||||
public bool Signed { get; private set; }
|
||||
public bool Prefetch { get; private set; }
|
||||
|
||||
public AOpCodeMemLit(AInst Inst, long Position, int OpCode) : base(Inst, Position)
|
||||
{
|
||||
Rt = OpCode & 0x1f;
|
||||
|
||||
Imm = Position + ADecoderHelper.DecodeImmS19_2(OpCode);
|
||||
|
||||
switch ((OpCode >> 30) & 3)
|
||||
{
|
||||
case 0: Size = 2; Signed = false; Prefetch = false; break;
|
||||
case 1: Size = 3; Signed = false; Prefetch = false; break;
|
||||
case 2: Size = 2; Signed = true; Prefetch = false; break;
|
||||
case 3: Size = 0; Signed = false; Prefetch = true; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
25
Ryujinx/Cpu/Decoder/AOpCodeMemPair.cs
Normal file
25
Ryujinx/Cpu/Decoder/AOpCodeMemPair.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeMemPair : AOpCodeMemImm
|
||||
{
|
||||
public int Rt2 { get; private set; }
|
||||
|
||||
public AOpCodeMemPair(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
|
||||
{
|
||||
Rt2 = (OpCode >> 10) & 0x1f;
|
||||
WBack = ((OpCode >> 23) & 0x1) != 0;
|
||||
PostIdx = ((OpCode >> 23) & 0x3) == 1;
|
||||
Extend64 = ((OpCode >> 30) & 0x3) == 1;
|
||||
Size = ((OpCode >> 31) & 0x1) | 2;
|
||||
|
||||
DecodeImm(OpCode);
|
||||
}
|
||||
|
||||
protected void DecodeImm(int OpCode)
|
||||
{
|
||||
Imm = ((long)(OpCode >> 15) << 57) >> (57 - Size);
|
||||
}
|
||||
}
|
||||
}
|
20
Ryujinx/Cpu/Decoder/AOpCodeMemReg.cs
Normal file
20
Ryujinx/Cpu/Decoder/AOpCodeMemReg.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeMemReg : AOpCodeMem
|
||||
{
|
||||
public bool Shift { get; private set; }
|
||||
public int Rm { get; private set; }
|
||||
|
||||
public AIntType IntType { get; private set; }
|
||||
|
||||
public AOpCodeMemReg(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
|
||||
{
|
||||
Shift = ((OpCode >> 12) & 0x1) != 0;
|
||||
IntType = (AIntType)((OpCode >> 13) & 0x7);
|
||||
Rm = (OpCode >> 16) & 0x1f;
|
||||
Extend64 = ((OpCode >> 22) & 0x3) == 2;
|
||||
}
|
||||
}
|
||||
}
|
36
Ryujinx/Cpu/Decoder/AOpCodeMov.cs
Normal file
36
Ryujinx/Cpu/Decoder/AOpCodeMov.cs
Normal file
|
@ -0,0 +1,36 @@
|
|||
using ChocolArm64.Instruction;
|
||||
using ChocolArm64.State;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeMov : AOpCode
|
||||
{
|
||||
public int Rd { get; private set; }
|
||||
public long Imm { get; private set; }
|
||||
public int Pos { get; private set; }
|
||||
|
||||
public AOpCodeMov(AInst Inst, long Position, int OpCode) : base(Inst, Position)
|
||||
{
|
||||
int P1 = (OpCode >> 22) & 1;
|
||||
int SF = (OpCode >> 31) & 1;
|
||||
|
||||
if (SF == 0 && P1 != 0)
|
||||
{
|
||||
Emitter = AInstEmit.Und;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Rd = (OpCode >> 0) & 0x1f;
|
||||
Imm = (OpCode >> 5) & 0xffff;
|
||||
Pos = (OpCode >> 21) & 0x3;
|
||||
|
||||
Pos <<= 4;
|
||||
Imm <<= Pos;
|
||||
|
||||
RegisterSize = (OpCode >> 31) != 0
|
||||
? ARegisterSize.Int64
|
||||
: ARegisterSize.Int32;
|
||||
}
|
||||
}
|
||||
}
|
16
Ryujinx/Cpu/Decoder/AOpCodeMul.cs
Normal file
16
Ryujinx/Cpu/Decoder/AOpCodeMul.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeMul : AOpCodeAlu
|
||||
{
|
||||
public int Rm { get; private set; }
|
||||
public int Ra { get; private set; }
|
||||
|
||||
public AOpCodeMul(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
|
||||
{
|
||||
Ra = (OpCode >> 10) & 0x1f;
|
||||
Rm = (OpCode >> 16) & 0x1f;
|
||||
}
|
||||
}
|
||||
}
|
27
Ryujinx/Cpu/Decoder/AOpCodeSimd.cs
Normal file
27
Ryujinx/Cpu/Decoder/AOpCodeSimd.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
using ChocolArm64.Instruction;
|
||||
using ChocolArm64.State;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeSimd : AOpCode, IAOpCodeSimd
|
||||
{
|
||||
public int Rd { get; private set; }
|
||||
public int Rn { get; private set; }
|
||||
public int Opc { get; private set; }
|
||||
public int Size { get; protected set; }
|
||||
|
||||
public int SizeF => Size & 1;
|
||||
|
||||
public AOpCodeSimd(AInst Inst, long Position, int OpCode) : base(Inst, Position)
|
||||
{
|
||||
Rd = (OpCode >> 0) & 0x1f;
|
||||
Rn = (OpCode >> 5) & 0x1f;
|
||||
Opc = (OpCode >> 15) & 0x3;
|
||||
Size = (OpCode >> 22) & 0x3;
|
||||
|
||||
RegisterSize = ((OpCode >> 30) & 1) != 0
|
||||
? ARegisterSize.SIMD128
|
||||
: ARegisterSize.SIMD64;
|
||||
}
|
||||
}
|
||||
}
|
31
Ryujinx/Cpu/Decoder/AOpCodeSimdCvt.cs
Normal file
31
Ryujinx/Cpu/Decoder/AOpCodeSimdCvt.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using ChocolArm64.Instruction;
|
||||
using ChocolArm64.State;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeSimdCvt : AOpCodeSimd
|
||||
{
|
||||
public int FBits { get; private set; }
|
||||
|
||||
public AOpCodeSimdCvt(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
|
||||
{
|
||||
//TODO:
|
||||
//Und of Fixed Point variants.
|
||||
int Scale = (OpCode >> 10) & 0x3f;
|
||||
int SF = (OpCode >> 31) & 0x1;
|
||||
|
||||
/*if (Type != SF && !(Type == 2 && SF == 1))
|
||||
{
|
||||
Emitter = AInstEmit.Und;
|
||||
|
||||
return;
|
||||
}*/
|
||||
|
||||
FBits = 64 - Scale;
|
||||
|
||||
RegisterSize = SF != 0
|
||||
? ARegisterSize.Int64
|
||||
: ARegisterSize.Int32;
|
||||
}
|
||||
}
|
||||
}
|
17
Ryujinx/Cpu/Decoder/AOpCodeSimdFcond.cs
Normal file
17
Ryujinx/Cpu/Decoder/AOpCodeSimdFcond.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeSimdFcond : AOpCodeSimdReg, IAOpCodeCond
|
||||
{
|
||||
public int NZCV { get; private set; }
|
||||
|
||||
public ACond Cond { get; private set; }
|
||||
|
||||
public AOpCodeSimdFcond(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
|
||||
{
|
||||
NZCV = (OpCode >> 0) & 0xf;
|
||||
Cond = (ACond)((OpCode >> 12) & 0xf);
|
||||
}
|
||||
}
|
||||
}
|
33
Ryujinx/Cpu/Decoder/AOpCodeSimdFmov.cs
Normal file
33
Ryujinx/Cpu/Decoder/AOpCodeSimdFmov.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeSimdFmov : AOpCode, IAOpCodeSimd
|
||||
{
|
||||
public int Rd { get; private set; }
|
||||
public long Imm { get; private set; }
|
||||
public int Size { get; private set; }
|
||||
|
||||
public AOpCodeSimdFmov(AInst Inst, long Position, int OpCode) : base(Inst, Position)
|
||||
{
|
||||
int Imm5 = (OpCode >> 5) & 0x1f;
|
||||
int Type = (OpCode >> 22) & 0x3;
|
||||
|
||||
if (Imm5 != 0b00000 || Type > 1)
|
||||
{
|
||||
Emitter = AInstEmit.Und;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Size = Type;
|
||||
|
||||
long Imm;
|
||||
|
||||
Rd = (OpCode >> 0) & 0x1f;
|
||||
Imm = (OpCode >> 13) & 0xff;
|
||||
|
||||
this.Imm = ADecoderHelper.DecodeImm8Float(Imm, Type);
|
||||
}
|
||||
}
|
||||
}
|
94
Ryujinx/Cpu/Decoder/AOpCodeSimdImm.cs
Normal file
94
Ryujinx/Cpu/Decoder/AOpCodeSimdImm.cs
Normal file
|
@ -0,0 +1,94 @@
|
|||
using ChocolArm64.Instruction;
|
||||
using ChocolArm64.State;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeSimdImm : AOpCode, IAOpCodeSimd
|
||||
{
|
||||
public int Rd { get; private set; }
|
||||
public long Imm { get; private set; }
|
||||
public int Size { get; private set; }
|
||||
|
||||
public AOpCodeSimdImm(AInst Inst, long Position, int OpCode) : base(Inst, Position)
|
||||
{
|
||||
Rd = OpCode & 0x1f;
|
||||
|
||||
int CMode = (OpCode >> 12) & 0xf;
|
||||
int Op = (OpCode >> 29) & 0x1;
|
||||
|
||||
int ModeLow = CMode & 1;
|
||||
int ModeHigh = CMode >> 1;
|
||||
|
||||
long Imm;
|
||||
|
||||
Imm = ((uint)OpCode >> 5) & 0x1f;
|
||||
Imm |= ((uint)OpCode >> 11) & 0xe0;
|
||||
|
||||
if (ModeHigh == 0b111)
|
||||
{
|
||||
Size = ModeLow != 0 ? Op : 3;
|
||||
|
||||
switch (Op | (ModeLow << 1))
|
||||
{
|
||||
case 0:
|
||||
//64-bits Immediate.
|
||||
//Transform abcd efgh into abcd efgh abcd efgh ...
|
||||
Imm = (long)((ulong)Imm * 0x0101010101010101);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
//64-bits Immediate.
|
||||
//Transform abcd efgh into aaaa aaaa bbbb bbbb ...
|
||||
Imm = (Imm & 0xf0) >> 4 | (Imm & 0x0f) << 4;
|
||||
Imm = (Imm & 0xcc) >> 2 | (Imm & 0x33) << 2;
|
||||
Imm = (Imm & 0xaa) >> 1 | (Imm & 0x55) << 1;
|
||||
|
||||
Imm = (long)((ulong)Imm * 0x8040201008040201);
|
||||
Imm = (long)((ulong)Imm & 0x8080808080808080);
|
||||
|
||||
Imm |= Imm >> 4;
|
||||
Imm |= Imm >> 2;
|
||||
Imm |= Imm >> 1;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
//Floating point Immediate.
|
||||
Imm = ADecoderHelper.DecodeImm8Float(Imm, Size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ((ModeHigh & 0b110) == 0b100)
|
||||
{
|
||||
//16-bits shifted Immediate.
|
||||
Size = 1; Imm <<= (ModeHigh & 1) << 3;
|
||||
}
|
||||
else if ((ModeHigh & 0b100) == 0b000)
|
||||
{
|
||||
//32-bits shifted Immediate.
|
||||
Size = 2; Imm <<= ModeHigh << 3;
|
||||
}
|
||||
else if ((ModeHigh & 0b111) == 0b110)
|
||||
{
|
||||
//32-bits shifted Immediate (fill with ones).
|
||||
Size = 2; Imm = ShlOnes(Imm, 8 << ModeLow);
|
||||
}
|
||||
else
|
||||
{
|
||||
//8 bits without shift.
|
||||
Size = 0;
|
||||
}
|
||||
|
||||
this.Imm = Imm;
|
||||
|
||||
RegisterSize = ((OpCode >> 30) & 1) != 0
|
||||
? ARegisterSize.SIMD128
|
||||
: ARegisterSize.SIMD64;
|
||||
}
|
||||
|
||||
private static long ShlOnes(long Value, int Shift)
|
||||
{
|
||||
return Value << Shift | (long)(ulong.MaxValue >> (64 - Shift));
|
||||
}
|
||||
}
|
||||
}
|
36
Ryujinx/Cpu/Decoder/AOpCodeSimdIns.cs
Normal file
36
Ryujinx/Cpu/Decoder/AOpCodeSimdIns.cs
Normal file
|
@ -0,0 +1,36 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeSimdIns : AOpCodeSimd
|
||||
{
|
||||
public int SrcIndex { get; private set; }
|
||||
public int DstIndex { get; private set; }
|
||||
|
||||
public AOpCodeSimdIns(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
|
||||
{
|
||||
int Imm4 = (OpCode >> 11) & 0xf;
|
||||
int Imm5 = (OpCode >> 16) & 0x1f;
|
||||
|
||||
if (Imm5 == 0b10000)
|
||||
{
|
||||
Emitter = AInstEmit.Und;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Size = Imm5 & -Imm5;
|
||||
|
||||
switch (Size)
|
||||
{
|
||||
case 1: Size = 0; break;
|
||||
case 2: Size = 1; break;
|
||||
case 4: Size = 2; break;
|
||||
case 8: Size = 3; break;
|
||||
}
|
||||
|
||||
SrcIndex = Imm4 >> Size;
|
||||
DstIndex = Imm5 >> (Size + 1);
|
||||
}
|
||||
}
|
||||
}
|
19
Ryujinx/Cpu/Decoder/AOpCodeSimdMemImm.cs
Normal file
19
Ryujinx/Cpu/Decoder/AOpCodeSimdMemImm.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeSimdMemImm : AOpCodeMemImm, IAOpCodeSimd
|
||||
{
|
||||
public AOpCodeSimdMemImm(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
|
||||
{
|
||||
Size |= (OpCode >> 21) & 4;
|
||||
|
||||
if (!WBack && !Unscaled && Size >= 4)
|
||||
{
|
||||
Imm <<= 4;
|
||||
}
|
||||
|
||||
Extend64 = false;
|
||||
}
|
||||
}
|
||||
}
|
31
Ryujinx/Cpu/Decoder/AOpCodeSimdMemLit.cs
Normal file
31
Ryujinx/Cpu/Decoder/AOpCodeSimdMemLit.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeSimdMemLit : AOpCode, IAOpCodeSimd, IAOpCodeLit
|
||||
{
|
||||
public int Rt { get; private set; }
|
||||
public long Imm { get; private set; }
|
||||
public int Size { get; private set; }
|
||||
public bool Signed => false;
|
||||
public bool Prefetch => false;
|
||||
|
||||
public AOpCodeSimdMemLit(AInst Inst, long Position, int OpCode) : base(Inst, Position)
|
||||
{
|
||||
int Opc = (OpCode >> 30) & 3;
|
||||
|
||||
if (Opc == 3)
|
||||
{
|
||||
Emitter = AInstEmit.Und;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Rt = OpCode & 0x1f;
|
||||
|
||||
Imm = Position + ADecoderHelper.DecodeImmS19_2(OpCode);
|
||||
|
||||
Size = Opc + 2;
|
||||
}
|
||||
}
|
||||
}
|
54
Ryujinx/Cpu/Decoder/AOpCodeSimdMemMult.cs
Normal file
54
Ryujinx/Cpu/Decoder/AOpCodeSimdMemMult.cs
Normal file
|
@ -0,0 +1,54 @@
|
|||
using ChocolArm64.Instruction;
|
||||
using ChocolArm64.State;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeSimdMemMult : AOpCode, IAOpCodeSimd
|
||||
{
|
||||
public int Rt { get; private set; }
|
||||
public int Rn { get; private set; }
|
||||
public int Size { get; private set; }
|
||||
public int Rm { get; private set; }
|
||||
public int Reps { get; private set; }
|
||||
public int SElems { get; private set; }
|
||||
public int Elems { get; private set; }
|
||||
public bool WBack { get; private set; }
|
||||
|
||||
public AOpCodeSimdMemMult(AInst Inst, long Position, int OpCode) : base(Inst, Position)
|
||||
{
|
||||
switch ((OpCode >> 12) & 0xf)
|
||||
{
|
||||
case 0b0000: Reps = 1; SElems = 4; break;
|
||||
case 0b0010: Reps = 4; SElems = 1; break;
|
||||
case 0b0100: Reps = 1; SElems = 3; break;
|
||||
case 0b0110: Reps = 3; SElems = 1; break;
|
||||
case 0b0111: Reps = 1; SElems = 1; break;
|
||||
case 0b1000: Reps = 1; SElems = 2; break;
|
||||
case 0b1010: Reps = 2; SElems = 1; break;
|
||||
|
||||
default: Inst = AInst.Undefined; return;
|
||||
}
|
||||
|
||||
Rt = (OpCode >> 0) & 0x1f;
|
||||
Rn = (OpCode >> 5) & 0x1f;
|
||||
Size = (OpCode >> 10) & 0x3;
|
||||
Rm = (OpCode >> 16) & 0x1f;
|
||||
WBack = ((OpCode >> 23) & 0x1) != 0;
|
||||
|
||||
bool Q = ((OpCode >> 30) & 1) != 0;
|
||||
|
||||
if (!Q && Size == 3 && SElems != 1)
|
||||
{
|
||||
Inst = AInst.Undefined;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
RegisterSize = Q
|
||||
? ARegisterSize.SIMD128
|
||||
: ARegisterSize.SIMD64;
|
||||
|
||||
Elems = (GetBitsCount() >> 3) >> Size;
|
||||
}
|
||||
}
|
||||
}
|
16
Ryujinx/Cpu/Decoder/AOpCodeSimdMemPair.cs
Normal file
16
Ryujinx/Cpu/Decoder/AOpCodeSimdMemPair.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeSimdMemPair : AOpCodeMemPair, IAOpCodeSimd
|
||||
{
|
||||
public AOpCodeSimdMemPair(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
|
||||
{
|
||||
Size = ((OpCode >> 30) & 3) + 2;
|
||||
|
||||
Extend64 = false;
|
||||
|
||||
DecodeImm(OpCode);
|
||||
}
|
||||
}
|
||||
}
|
14
Ryujinx/Cpu/Decoder/AOpCodeSimdMemReg.cs
Normal file
14
Ryujinx/Cpu/Decoder/AOpCodeSimdMemReg.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeSimdMemReg : AOpCodeMemReg, IAOpCodeSimd
|
||||
{
|
||||
public AOpCodeSimdMemReg(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
|
||||
{
|
||||
Size |= (OpCode >> 21) & 4;
|
||||
|
||||
Extend64 = false;
|
||||
}
|
||||
}
|
||||
}
|
16
Ryujinx/Cpu/Decoder/AOpCodeSimdReg.cs
Normal file
16
Ryujinx/Cpu/Decoder/AOpCodeSimdReg.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeSimdReg : AOpCodeSimd
|
||||
{
|
||||
public int Rm { get; private set; }
|
||||
public bool Bit3 { get; private set; }
|
||||
|
||||
public AOpCodeSimdReg(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
|
||||
{
|
||||
Rm = (OpCode >> 16) & 0x1f;
|
||||
Bit3 = ((OpCode >> 3) & 0x1) != 0;
|
||||
}
|
||||
}
|
||||
}
|
26
Ryujinx/Cpu/Decoder/AOpCodeSimdRegElem.cs
Normal file
26
Ryujinx/Cpu/Decoder/AOpCodeSimdRegElem.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeSimdRegElem : AOpCodeSimd
|
||||
{
|
||||
public int Rm { get; private set; }
|
||||
public int Index { get; private set; }
|
||||
|
||||
public AOpCodeSimdRegElem(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
|
||||
{
|
||||
Rm = (OpCode >> 16) & 0x1f;
|
||||
Size = (OpCode >> 22) & 0x1;
|
||||
|
||||
if (Size != 0)
|
||||
{
|
||||
Index = (OpCode >> 11) & 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
Index = (OpCode >> 21) & 1 |
|
||||
(OpCode >> 10) & 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
26
Ryujinx/Cpu/Decoder/AOpCodeSimdShImm.cs
Normal file
26
Ryujinx/Cpu/Decoder/AOpCodeSimdShImm.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
using ChocolArm64.Instruction;
|
||||
using ChocolArm64.State;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeSimdShImm : AOpCode, IAOpCodeSimd
|
||||
{
|
||||
public int Rd { get; private set; }
|
||||
public int Rn { get; private set; }
|
||||
public int Imm { get; private set; }
|
||||
public int Size { get; private set; }
|
||||
|
||||
public AOpCodeSimdShImm(AInst Inst, long Position, int OpCode) : base(Inst, Position)
|
||||
{
|
||||
Rd = (OpCode >> 0) & 0x1f;
|
||||
Rn = (OpCode >> 5) & 0x1f;
|
||||
Imm = (OpCode >> 16) & 0x7f;
|
||||
|
||||
Size = ABitUtils.HighestBitSet32(Imm >> 3);
|
||||
|
||||
RegisterSize = ((OpCode >> 30) & 1) != 0
|
||||
? ARegisterSize.SIMD128
|
||||
: ARegisterSize.SIMD64;
|
||||
}
|
||||
}
|
||||
}
|
12
Ryujinx/Cpu/Decoder/AOpCodeSimdTbl.cs
Normal file
12
Ryujinx/Cpu/Decoder/AOpCodeSimdTbl.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeSimdTbl : AOpCodeSimdReg
|
||||
{
|
||||
public AOpCodeSimdTbl(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
|
||||
{
|
||||
Size = ((OpCode >> 13) & 3) + 1;
|
||||
}
|
||||
}
|
||||
}
|
24
Ryujinx/Cpu/Decoder/AOpCodeSystem.cs
Normal file
24
Ryujinx/Cpu/Decoder/AOpCodeSystem.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
using ChocolArm64.Instruction;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
class AOpCodeSystem : AOpCode
|
||||
{
|
||||
public int Rt { get; private set; }
|
||||
public int Op2 { get; private set; }
|
||||
public int CRm { get; private set; }
|
||||
public int CRn { get; private set; }
|
||||
public int Op1 { get; private set; }
|
||||
public int Op0 { get; private set; }
|
||||
|
||||
public AOpCodeSystem(AInst Inst, long Position, int OpCode) : base(Inst, Position)
|
||||
{
|
||||
Rt = (OpCode >> 0) & 0x1f;
|
||||
Op2 = (OpCode >> 5) & 0x7;
|
||||
CRm = (OpCode >> 8) & 0xf;
|
||||
CRn = (OpCode >> 12) & 0xf;
|
||||
Op1 = (OpCode >> 16) & 0x7;
|
||||
Op0 = ((OpCode >> 19) & 0x1) | 2;
|
||||
}
|
||||
}
|
||||
}
|
10
Ryujinx/Cpu/Decoder/AShiftType.cs
Normal file
10
Ryujinx/Cpu/Decoder/AShiftType.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
enum AShiftType
|
||||
{
|
||||
Lsl,
|
||||
Lsr,
|
||||
Asr,
|
||||
Ror
|
||||
}
|
||||
}
|
13
Ryujinx/Cpu/Decoder/IAOpCode.cs
Normal file
13
Ryujinx/Cpu/Decoder/IAOpCode.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using ChocolArm64.Instruction;
|
||||
using ChocolArm64.State;
|
||||
|
||||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
interface IAOpCode
|
||||
{
|
||||
long Position { get; }
|
||||
|
||||
AInstEmitter Emitter { get; }
|
||||
ARegisterSize RegisterSize { get; }
|
||||
}
|
||||
}
|
10
Ryujinx/Cpu/Decoder/IAOpCodeAlu.cs
Normal file
10
Ryujinx/Cpu/Decoder/IAOpCodeAlu.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
interface IAOpCodeAlu : IAOpCode
|
||||
{
|
||||
int Rd { get; }
|
||||
int Rn { get; }
|
||||
|
||||
ADataOp DataOp { get; }
|
||||
}
|
||||
}
|
7
Ryujinx/Cpu/Decoder/IAOpCodeAluImm.cs
Normal file
7
Ryujinx/Cpu/Decoder/IAOpCodeAluImm.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
interface IAOpCodeAluImm : IAOpCodeAlu
|
||||
{
|
||||
long Imm { get; }
|
||||
}
|
||||
}
|
10
Ryujinx/Cpu/Decoder/IAOpCodeAluRs.cs
Normal file
10
Ryujinx/Cpu/Decoder/IAOpCodeAluRs.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
interface IAOpCodeAluRs : IAOpCodeAlu
|
||||
{
|
||||
int Shift { get; }
|
||||
int Rm { get; }
|
||||
|
||||
AShiftType ShiftType { get; }
|
||||
}
|
||||
}
|
10
Ryujinx/Cpu/Decoder/IAOpCodeAluRx.cs
Normal file
10
Ryujinx/Cpu/Decoder/IAOpCodeAluRx.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
interface IAOpCodeAluRx : IAOpCodeAlu
|
||||
{
|
||||
int Shift { get; }
|
||||
int Rm { get; }
|
||||
|
||||
AIntType IntType { get; }
|
||||
}
|
||||
}
|
7
Ryujinx/Cpu/Decoder/IAOpCodeCond.cs
Normal file
7
Ryujinx/Cpu/Decoder/IAOpCodeCond.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
interface IAOpCodeCond : IAOpCode
|
||||
{
|
||||
ACond Cond { get; }
|
||||
}
|
||||
}
|
11
Ryujinx/Cpu/Decoder/IAOpCodeLit.cs
Normal file
11
Ryujinx/Cpu/Decoder/IAOpCodeLit.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
interface IAOpCodeLit : IAOpCode
|
||||
{
|
||||
int Rt { get; }
|
||||
long Imm { get; }
|
||||
int Size { get; }
|
||||
bool Signed { get; }
|
||||
bool Prefetch { get; }
|
||||
}
|
||||
}
|
7
Ryujinx/Cpu/Decoder/IAOpCodeSimd.cs
Normal file
7
Ryujinx/Cpu/Decoder/IAOpCodeSimd.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace ChocolArm64.Decoder
|
||||
{
|
||||
interface IAOpCodeSimd : IAOpCode
|
||||
{
|
||||
int Size { get; }
|
||||
}
|
||||
}
|
14
Ryujinx/Cpu/Exceptions/VmmAccessViolationException.cs
Normal file
14
Ryujinx/Cpu/Exceptions/VmmAccessViolationException.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using ChocolArm64.Memory;
|
||||
using System;
|
||||
|
||||
namespace ChocolArm64.Exceptions
|
||||
{
|
||||
public class VmmAccessViolationException : Exception
|
||||
{
|
||||
private const string ExMsg = "Value at address 0x{0:x16} could not be \"{1}\"!";
|
||||
|
||||
public VmmAccessViolationException() { }
|
||||
|
||||
public VmmAccessViolationException(long Position, AMemoryPerm Perm) : base(string.Format(ExMsg, Position, Perm)) { }
|
||||
}
|
||||
}
|
13
Ryujinx/Cpu/Exceptions/VmmOutOfMemoryException.cs
Normal file
13
Ryujinx/Cpu/Exceptions/VmmOutOfMemoryException.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
|
||||
namespace ChocolArm64.Exceptions
|
||||
{
|
||||
public class VmmOutOfMemoryException : Exception
|
||||
{
|
||||
private const string ExMsg = "Failed to allocate {0} bytes of memory!";
|
||||
|
||||
public VmmOutOfMemoryException() { }
|
||||
|
||||
public VmmOutOfMemoryException(long Size) : base(string.Format(ExMsg, Size)) { }
|
||||
}
|
||||
}
|
13
Ryujinx/Cpu/Exceptions/VmmPageFaultException.cs
Normal file
13
Ryujinx/Cpu/Exceptions/VmmPageFaultException.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
|
||||
namespace ChocolArm64.Exceptions
|
||||
{
|
||||
public class VmmPageFaultException : Exception
|
||||
{
|
||||
private const string ExMsg = "Tried to access unmapped address 0x{0:x16}!";
|
||||
|
||||
public VmmPageFaultException() { }
|
||||
|
||||
public VmmPageFaultException(long Position) : base(string.Format(ExMsg, Position)) { }
|
||||
}
|
||||
}
|
18
Ryujinx/Cpu/Instruction/AInst.cs
Normal file
18
Ryujinx/Cpu/Instruction/AInst.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using System;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
struct AInst
|
||||
{
|
||||
public AInstEmitter Emitter { get; private set; }
|
||||
public Type Type { get; private set; }
|
||||
|
||||
public static AInst Undefined => new AInst(AInstEmit.Und, null);
|
||||
|
||||
public AInst(AInstEmitter Emitter, Type Type)
|
||||
{
|
||||
this.Emitter = Emitter;
|
||||
this.Type = Type;
|
||||
}
|
||||
}
|
||||
}
|
296
Ryujinx/Cpu/Instruction/AInstEmitAlu.cs
Normal file
296
Ryujinx/Cpu/Instruction/AInstEmitAlu.cs
Normal file
|
@ -0,0 +1,296 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
using static ChocolArm64.Instruction.AInstEmitAluHelper;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
public static void Add(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.Add);
|
||||
|
||||
public static void Adds(AILEmitterCtx Context)
|
||||
{
|
||||
Context.TryOptMarkCondWithoutCmp();
|
||||
|
||||
EmitDataLoadOpers(Context);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
Context.EmitZNFlagCheck();
|
||||
|
||||
EmitAddsCCheck(Context);
|
||||
EmitAddsVCheck(Context);
|
||||
EmitDataStoreS(Context);
|
||||
}
|
||||
|
||||
public static void And(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.And);
|
||||
|
||||
public static void Ands(AILEmitterCtx Context)
|
||||
{
|
||||
EmitDataLoadOpers(Context);
|
||||
|
||||
Context.Emit(OpCodes.And);
|
||||
|
||||
Context.EmitZNFlagCheck();
|
||||
|
||||
EmitDataStoreS(Context);
|
||||
}
|
||||
|
||||
public static void Asrv(AILEmitterCtx Context) => EmitDataOpShift(Context, OpCodes.Shr);
|
||||
|
||||
public static void Bic(AILEmitterCtx Context) => EmitBic(Context, false);
|
||||
public static void Bics(AILEmitterCtx Context) => EmitBic(Context, true);
|
||||
|
||||
private static void EmitBic(AILEmitterCtx Context, bool SetFlags)
|
||||
{
|
||||
EmitDataLoadOpers(Context);
|
||||
|
||||
Context.Emit(OpCodes.Not);
|
||||
Context.Emit(OpCodes.And);
|
||||
|
||||
if (SetFlags)
|
||||
{
|
||||
Context.EmitZNFlagCheck();
|
||||
}
|
||||
|
||||
EmitDataStore(Context, SetFlags);
|
||||
}
|
||||
|
||||
public static void Clz(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
if (Op.RegisterSize == ARegisterSize.Int32)
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountLeadingZeros32));
|
||||
}
|
||||
else
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountLeadingZeros64));
|
||||
}
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Eor(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.Xor);
|
||||
|
||||
public static void Extr(AILEmitterCtx Context)
|
||||
{
|
||||
//TODO: Ensure that the Shift is valid for the Is64Bits.
|
||||
AOpCodeAluRs Op = (AOpCodeAluRs)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rm);
|
||||
|
||||
if (Op.Shift > 0)
|
||||
{
|
||||
Context.EmitLdc_I4(Op.Shift);
|
||||
|
||||
Context.Emit(OpCodes.Shr_Un);
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.GetBitsCount() - Op.Shift);
|
||||
|
||||
Context.Emit(OpCodes.Shl);
|
||||
Context.Emit(OpCodes.Or);
|
||||
}
|
||||
|
||||
EmitDataStore(Context);
|
||||
}
|
||||
|
||||
public static void Lslv(AILEmitterCtx Context) => EmitDataOpShift(Context, OpCodes.Shl);
|
||||
public static void Lsrv(AILEmitterCtx Context) => EmitDataOpShift(Context, OpCodes.Shr_Un);
|
||||
|
||||
public static void Sub(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.Sub);
|
||||
|
||||
public static void Subs(AILEmitterCtx Context)
|
||||
{
|
||||
Context.TryOptMarkCondWithoutCmp();
|
||||
|
||||
EmitDataLoadOpers(Context);
|
||||
|
||||
Context.Emit(OpCodes.Sub);
|
||||
|
||||
Context.EmitZNFlagCheck();
|
||||
|
||||
EmitSubsCCheck(Context);
|
||||
EmitSubsVCheck(Context);
|
||||
EmitDataStoreS(Context);
|
||||
}
|
||||
|
||||
public static void Orn(AILEmitterCtx Context)
|
||||
{
|
||||
EmitDataLoadOpers(Context);
|
||||
|
||||
Context.Emit(OpCodes.Not);
|
||||
Context.Emit(OpCodes.Or);
|
||||
|
||||
EmitDataStore(Context);
|
||||
}
|
||||
|
||||
public static void Orr(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.Or);
|
||||
|
||||
public static void Rbit(AILEmitterCtx Context) => EmitFallback32_64(Context,
|
||||
nameof(ASoftFallback.ReverseBits32),
|
||||
nameof(ASoftFallback.ReverseBits64));
|
||||
|
||||
public static void Rev16(AILEmitterCtx Context) => EmitFallback32_64(Context,
|
||||
nameof(ASoftFallback.ReverseBytes16_32),
|
||||
nameof(ASoftFallback.ReverseBytes16_64));
|
||||
|
||||
public static void Rev32(AILEmitterCtx Context) => EmitFallback32_64(Context,
|
||||
nameof(ASoftFallback.ReverseBytes32_32),
|
||||
nameof(ASoftFallback.ReverseBytes32_64));
|
||||
|
||||
public static void EmitFallback32_64(AILEmitterCtx Context, string Name32, string Name64)
|
||||
{
|
||||
AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
if (Op.RegisterSize == ARegisterSize.Int32)
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, Name32);
|
||||
}
|
||||
else
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, Name64);
|
||||
}
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Rev64(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.ReverseBytes64));
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
private static void EmitRev(AILEmitterCtx Context, string Name)
|
||||
{
|
||||
AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
ASoftFallback.EmitCall(Context, Name);
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Rorv(AILEmitterCtx Context)
|
||||
{
|
||||
EmitDataLoadRn(Context);
|
||||
EmitDataLoadShift(Context);
|
||||
|
||||
Context.Emit(OpCodes.Shr_Un);
|
||||
|
||||
EmitDataLoadRn(Context);
|
||||
|
||||
Context.EmitLdc_I4(Context.CurrOp.GetBitsCount());
|
||||
|
||||
EmitDataLoadShift(Context);
|
||||
|
||||
Context.Emit(OpCodes.Sub);
|
||||
Context.Emit(OpCodes.Shl);
|
||||
Context.Emit(OpCodes.Or);
|
||||
|
||||
EmitDataStore(Context);
|
||||
}
|
||||
|
||||
public static void Sdiv(AILEmitterCtx Context) => EmitDiv(Context, OpCodes.Div);
|
||||
public static void Udiv(AILEmitterCtx Context) => EmitDiv(Context, OpCodes.Div_Un);
|
||||
|
||||
private static void EmitDiv(AILEmitterCtx Context, OpCode ILOp)
|
||||
{
|
||||
//If Rm == 0, Rd = 0 (division by zero).
|
||||
Context.EmitLdc_I(0);
|
||||
|
||||
EmitDataLoadRm(Context);
|
||||
|
||||
Context.EmitLdc_I(0);
|
||||
|
||||
AILLabel BadDiv = new AILLabel();
|
||||
|
||||
Context.Emit(OpCodes.Beq_S, BadDiv);
|
||||
Context.Emit(OpCodes.Pop);
|
||||
|
||||
if (ILOp == OpCodes.Div)
|
||||
{
|
||||
//If Rn == INT_MIN && Rm == -1, Rd = INT_MIN (overflow).
|
||||
long IntMin = 1L << (Context.CurrOp.GetBitsCount() - 1);
|
||||
|
||||
Context.EmitLdc_I(IntMin);
|
||||
|
||||
EmitDataLoadRn(Context);
|
||||
|
||||
Context.EmitLdc_I(IntMin);
|
||||
|
||||
Context.Emit(OpCodes.Ceq);
|
||||
|
||||
EmitDataLoadRm(Context);
|
||||
|
||||
Context.EmitLdc_I(-1);
|
||||
|
||||
Context.Emit(OpCodes.Ceq);
|
||||
Context.Emit(OpCodes.And);
|
||||
Context.Emit(OpCodes.Brtrue_S, BadDiv);
|
||||
Context.Emit(OpCodes.Pop);
|
||||
}
|
||||
|
||||
EmitDataLoadRn(Context);
|
||||
EmitDataLoadRm(Context);
|
||||
|
||||
Context.Emit(ILOp);
|
||||
|
||||
Context.MarkLabel(BadDiv);
|
||||
|
||||
EmitDataStore(Context);
|
||||
}
|
||||
|
||||
private static void EmitDataOp(AILEmitterCtx Context, OpCode ILOp)
|
||||
{
|
||||
EmitDataLoadOpers(Context);
|
||||
|
||||
Context.Emit(ILOp);
|
||||
|
||||
EmitDataStore(Context);
|
||||
}
|
||||
|
||||
private static void EmitDataOpShift(AILEmitterCtx Context, OpCode ILOp)
|
||||
{
|
||||
EmitDataLoadRn(Context);
|
||||
EmitDataLoadShift(Context);
|
||||
|
||||
Context.Emit(ILOp);
|
||||
|
||||
EmitDataStore(Context);
|
||||
}
|
||||
|
||||
private static void EmitDataLoadShift(AILEmitterCtx Context)
|
||||
{
|
||||
EmitDataLoadRm(Context);
|
||||
|
||||
Context.EmitLdc_I(Context.CurrOp.GetBitsCount() - 1);
|
||||
|
||||
Context.Emit(OpCodes.And);
|
||||
|
||||
//Note: Only 32-bits shift values are valid, so when the value is 64-bits
|
||||
//we need to cast it to a 32-bits integer. This is fine because we
|
||||
//AND the value and only keep the lower 5 or 6 bits anyway -- it
|
||||
//could very well fit on a byte.
|
||||
if (Context.CurrOp.RegisterSize != ARegisterSize.Int32)
|
||||
{
|
||||
Context.Emit(OpCodes.Conv_I4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
159
Ryujinx/Cpu/Instruction/AInstEmitAluHelper.cs
Normal file
159
Ryujinx/Cpu/Instruction/AInstEmitAluHelper.cs
Normal file
|
@ -0,0 +1,159 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static class AInstEmitAluHelper
|
||||
{
|
||||
public static void EmitAddsCCheck(AILEmitterCtx Context)
|
||||
{
|
||||
//C = Rd < Rn
|
||||
Context.Emit(OpCodes.Dup);
|
||||
|
||||
EmitDataLoadRn(Context);
|
||||
|
||||
Context.Emit(OpCodes.Clt_Un);
|
||||
|
||||
Context.EmitStflg((int)APState.CBit);
|
||||
}
|
||||
|
||||
public static void EmitAddsVCheck(AILEmitterCtx Context)
|
||||
{
|
||||
//V = (Rd ^ Rn) & (Rd ^ Rm) & ~(Rn ^ Rm) < 0
|
||||
Context.EmitSttmp();
|
||||
Context.EmitLdtmp();
|
||||
Context.EmitLdtmp();
|
||||
|
||||
EmitDataLoadRn(Context);
|
||||
|
||||
Context.Emit(OpCodes.Xor);
|
||||
|
||||
Context.EmitLdtmp();
|
||||
|
||||
EmitDataLoadOper2(Context);
|
||||
|
||||
Context.Emit(OpCodes.Xor);
|
||||
Context.Emit(OpCodes.And);
|
||||
|
||||
EmitDataLoadOpers(Context);
|
||||
|
||||
Context.Emit(OpCodes.Xor);
|
||||
Context.Emit(OpCodes.Not);
|
||||
Context.Emit(OpCodes.And);
|
||||
|
||||
Context.EmitLdc_I(0);
|
||||
|
||||
Context.Emit(OpCodes.Clt);
|
||||
|
||||
Context.EmitStflg((int)APState.VBit);
|
||||
}
|
||||
|
||||
public static void EmitSubsCCheck(AILEmitterCtx Context)
|
||||
{
|
||||
//C = Rn == Rm || Rn > Rm
|
||||
EmitDataLoadOpers(Context);
|
||||
|
||||
Context.Emit(OpCodes.Ceq);
|
||||
|
||||
EmitDataLoadOpers(Context);
|
||||
|
||||
Context.Emit(OpCodes.Cgt_Un);
|
||||
Context.Emit(OpCodes.Or);
|
||||
|
||||
Context.EmitStflg((int)APState.CBit);
|
||||
}
|
||||
|
||||
public static void EmitSubsVCheck(AILEmitterCtx Context)
|
||||
{
|
||||
//V = (Rd ^ Rn) & (Rn ^ Rm) < 0
|
||||
Context.Emit(OpCodes.Dup);
|
||||
|
||||
EmitDataLoadRn(Context);
|
||||
|
||||
Context.Emit(OpCodes.Xor);
|
||||
|
||||
EmitDataLoadOpers(Context);
|
||||
|
||||
Context.Emit(OpCodes.Xor);
|
||||
Context.Emit(OpCodes.And);
|
||||
|
||||
Context.EmitLdc_I(0);
|
||||
|
||||
Context.Emit(OpCodes.Clt);
|
||||
|
||||
Context.EmitStflg((int)APState.VBit);
|
||||
}
|
||||
|
||||
public static void EmitDataLoadRm(AILEmitterCtx Context)
|
||||
{
|
||||
Context.EmitLdintzr(((IAOpCodeAluRs)Context.CurrOp).Rm);
|
||||
}
|
||||
|
||||
public static void EmitDataLoadOpers(AILEmitterCtx Context)
|
||||
{
|
||||
EmitDataLoadRn(Context);
|
||||
EmitDataLoadOper2(Context);
|
||||
}
|
||||
|
||||
public static void EmitDataLoadRn(AILEmitterCtx Context)
|
||||
{
|
||||
IAOpCodeAlu Op = (IAOpCodeAlu)Context.CurrOp;
|
||||
|
||||
if (Op.DataOp == ADataOp.Logical || Op is IAOpCodeAluRs)
|
||||
{
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.EmitLdint(Op.Rn);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitDataLoadOper2(AILEmitterCtx Context)
|
||||
{
|
||||
switch (Context.CurrOp)
|
||||
{
|
||||
case IAOpCodeAluImm Op:
|
||||
Context.EmitLdc_I(Op.Imm);
|
||||
break;
|
||||
|
||||
case IAOpCodeAluRs Op:
|
||||
Context.EmitLdintzr(Op.Rm);
|
||||
|
||||
switch (Op.ShiftType)
|
||||
{
|
||||
case AShiftType.Lsl: Context.EmitLsl(Op.Shift); break;
|
||||
case AShiftType.Lsr: Context.EmitLsr(Op.Shift); break;
|
||||
case AShiftType.Asr: Context.EmitAsr(Op.Shift); break;
|
||||
case AShiftType.Ror: Context.EmitRor(Op.Shift); break;
|
||||
}
|
||||
break;
|
||||
|
||||
case IAOpCodeAluRx Op:
|
||||
Context.EmitLdintzr(Op.Rm);
|
||||
Context.EmitCast(Op.IntType);
|
||||
Context.EmitLsl(Op.Shift);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitDataStore(AILEmitterCtx Context) => EmitDataStore(Context, false);
|
||||
public static void EmitDataStoreS(AILEmitterCtx Context) => EmitDataStore(Context, true);
|
||||
|
||||
public static void EmitDataStore(AILEmitterCtx Context, bool SetFlags)
|
||||
{
|
||||
IAOpCodeAlu Op = (IAOpCodeAlu)Context.CurrOp;
|
||||
|
||||
if (SetFlags || Op is IAOpCodeAluRs)
|
||||
{
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.EmitStint(Op.Rd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
208
Ryujinx/Cpu/Instruction/AInstEmitBfm.cs
Normal file
208
Ryujinx/Cpu/Instruction/AInstEmitBfm.cs
Normal file
|
@ -0,0 +1,208 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
public static void Bfm(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp;
|
||||
|
||||
EmitBfmLoadRn(Context);
|
||||
|
||||
Context.EmitLdintzr(Op.Rd);
|
||||
Context.EmitLdc_I(~Op.WMask & Op.TMask);
|
||||
|
||||
Context.Emit(OpCodes.And);
|
||||
Context.Emit(OpCodes.Or);
|
||||
|
||||
Context.EmitLdintzr(Op.Rd);
|
||||
Context.EmitLdc_I(~Op.TMask);
|
||||
|
||||
Context.Emit(OpCodes.And);
|
||||
Context.Emit(OpCodes.Or);
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Sbfm(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp;
|
||||
|
||||
int BitsCount = Op.GetBitsCount();
|
||||
|
||||
if (Op.Pos + 1 == BitsCount)
|
||||
{
|
||||
EmitBfmShift(Context, OpCodes.Shr);
|
||||
}
|
||||
else if (Op.Pos < Op.Shift)
|
||||
{
|
||||
EmitSbfiz(Context);
|
||||
}
|
||||
else if (Op.Pos == 7 && Op.Shift == 0)
|
||||
{
|
||||
EmitSbfmCast(Context, OpCodes.Conv_I1);
|
||||
}
|
||||
else if (Op.Pos == 15 && Op.Shift == 0)
|
||||
{
|
||||
EmitSbfmCast(Context, OpCodes.Conv_I2);
|
||||
}
|
||||
else if (Op.Pos == 31 && Op.Shift == 0)
|
||||
{
|
||||
EmitSbfmCast(Context, OpCodes.Conv_I4);
|
||||
}
|
||||
else if (Op.Shift == 0)
|
||||
{
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
Context.EmitLsl(BitsCount - 1 - Op.Pos);
|
||||
Context.EmitAsr(BitsCount - 1);
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitBfmLoadRn(Context);
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
Context.EmitLsl(BitsCount - 1 - Op.Pos);
|
||||
Context.EmitAsr(BitsCount - 1);
|
||||
|
||||
Context.EmitLdc_I(~Op.TMask);
|
||||
|
||||
Context.Emit(OpCodes.And);
|
||||
Context.Emit(OpCodes.Or);
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Ubfm(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp;
|
||||
|
||||
if (Op.Pos + 1 == Op.GetBitsCount())
|
||||
{
|
||||
EmitBfmShift(Context, OpCodes.Shr_Un);
|
||||
}
|
||||
else if (Op.Pos < Op.Shift)
|
||||
{
|
||||
EmitUbfiz(Context);
|
||||
}
|
||||
else if (Op.Pos + 1 == Op.Shift)
|
||||
{
|
||||
EmitBfmLsl(Context);
|
||||
}
|
||||
else if (Op.Pos == 7 && Op.Shift == 0)
|
||||
{
|
||||
EmitUbfmCast(Context, OpCodes.Conv_U1);
|
||||
}
|
||||
else if (Op.Pos == 15 && Op.Shift == 0)
|
||||
{
|
||||
EmitUbfmCast(Context, OpCodes.Conv_U2);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitBfmLoadRn(Context);
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitSbfiz(AILEmitterCtx Context) => EmitBfiz(Context, true);
|
||||
private static void EmitUbfiz(AILEmitterCtx Context) => EmitBfiz(Context, false);
|
||||
|
||||
private static void EmitBfiz(AILEmitterCtx Context, bool Signed)
|
||||
{
|
||||
AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp;
|
||||
|
||||
int Width = Op.Pos + 1;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
Context.EmitLsl(Op.GetBitsCount() - Width);
|
||||
|
||||
if (Signed)
|
||||
{
|
||||
Context.EmitAsr(Op.Shift - Width);
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.EmitLsr(Op.Shift - Width);
|
||||
}
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
private static void EmitSbfmCast(AILEmitterCtx Context, OpCode ILOp)
|
||||
{
|
||||
EmitBfmCast(Context, ILOp, true);
|
||||
}
|
||||
|
||||
private static void EmitUbfmCast(AILEmitterCtx Context, OpCode ILOp)
|
||||
{
|
||||
EmitBfmCast(Context, ILOp, false);
|
||||
}
|
||||
|
||||
private static void EmitBfmCast(AILEmitterCtx Context, OpCode ILOp, bool Signed)
|
||||
{
|
||||
AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
Context.Emit(ILOp);
|
||||
|
||||
if (Op.RegisterSize != ARegisterSize.Int32)
|
||||
{
|
||||
Context.Emit(Signed
|
||||
? OpCodes.Conv_I8
|
||||
: OpCodes.Conv_U8);
|
||||
}
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
private static void EmitBfmShift(AILEmitterCtx Context, OpCode ILOp)
|
||||
{
|
||||
AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp;
|
||||
|
||||
if (Op.Shift > 0)
|
||||
{
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.Shift);
|
||||
|
||||
Context.Emit(ILOp);
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitBfmLsl(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
Context.EmitLsl(Op.GetBitsCount() - Op.Shift);
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
private static void EmitBfmLoadRn(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
Context.EmitRor(Op.Shift);
|
||||
|
||||
Context.EmitLdc_I(Op.WMask & Op.TMask);
|
||||
|
||||
Context.Emit(OpCodes.And);
|
||||
}
|
||||
}
|
||||
}
|
81
Ryujinx/Cpu/Instruction/AInstEmitCcmp.cs
Normal file
81
Ryujinx/Cpu/Instruction/AInstEmitCcmp.cs
Normal file
|
@ -0,0 +1,81 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
using static ChocolArm64.Instruction.AInstEmitAluHelper;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
private enum CcmpOp
|
||||
{
|
||||
Cmp,
|
||||
Cmn
|
||||
}
|
||||
|
||||
public static void Ccmn(AILEmitterCtx Context) => EmitCcmp(Context, CcmpOp.Cmn);
|
||||
public static void Ccmp(AILEmitterCtx Context) => EmitCcmp(Context, CcmpOp.Cmp);
|
||||
|
||||
private static void EmitCcmp(AILEmitterCtx Context, CcmpOp CmpOp)
|
||||
{
|
||||
AOpCodeCcmp Op = (AOpCodeCcmp)Context.CurrOp;
|
||||
|
||||
AILLabel LblTrue = new AILLabel();
|
||||
AILLabel LblEnd = new AILLabel();
|
||||
|
||||
Context.EmitCondBranch(LblTrue, Op.Cond);
|
||||
|
||||
Context.EmitLdc_I4((Op.NZCV >> 0) & 1);
|
||||
|
||||
Context.EmitStflg((int)APState.VBit);
|
||||
|
||||
Context.EmitLdc_I4((Op.NZCV >> 1) & 1);
|
||||
|
||||
Context.EmitStflg((int)APState.CBit);
|
||||
|
||||
Context.EmitLdc_I4((Op.NZCV >> 2) & 1);
|
||||
|
||||
Context.EmitStflg((int)APState.ZBit);
|
||||
|
||||
Context.EmitLdc_I4((Op.NZCV >> 3) & 1);
|
||||
|
||||
Context.EmitStflg((int)APState.NBit);
|
||||
|
||||
Context.Emit(OpCodes.Br_S, LblEnd);
|
||||
|
||||
Context.MarkLabel(LblTrue);
|
||||
|
||||
EmitDataLoadOpers(Context);
|
||||
|
||||
if (CmpOp == CcmpOp.Cmp)
|
||||
{
|
||||
Context.Emit(OpCodes.Sub);
|
||||
|
||||
Context.EmitZNFlagCheck();
|
||||
|
||||
EmitSubsCCheck(Context);
|
||||
EmitSubsVCheck(Context);
|
||||
}
|
||||
else if (CmpOp == CcmpOp.Cmn)
|
||||
{
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
Context.EmitZNFlagCheck();
|
||||
|
||||
EmitAddsCCheck(Context);
|
||||
EmitAddsVCheck(Context);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException(nameof(CmpOp));
|
||||
}
|
||||
|
||||
Context.Emit(OpCodes.Pop);
|
||||
|
||||
Context.MarkLabel(LblEnd);
|
||||
}
|
||||
}
|
||||
}
|
59
Ryujinx/Cpu/Instruction/AInstEmitCsel.cs
Normal file
59
Ryujinx/Cpu/Instruction/AInstEmitCsel.cs
Normal file
|
@ -0,0 +1,59 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.Translation;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
private enum CselOperation
|
||||
{
|
||||
None,
|
||||
Increment,
|
||||
Invert,
|
||||
Negate
|
||||
}
|
||||
|
||||
public static void Csel(AILEmitterCtx Context) => EmitCsel(Context, CselOperation.None);
|
||||
public static void Csinc(AILEmitterCtx Context) => EmitCsel(Context, CselOperation.Increment);
|
||||
public static void Csinv(AILEmitterCtx Context) => EmitCsel(Context, CselOperation.Invert);
|
||||
public static void Csneg(AILEmitterCtx Context) => EmitCsel(Context, CselOperation.Negate);
|
||||
|
||||
private static void EmitCsel(AILEmitterCtx Context, CselOperation CselOp)
|
||||
{
|
||||
AOpCodeCsel Op = (AOpCodeCsel)Context.CurrOp;
|
||||
|
||||
AILLabel LblTrue = new AILLabel();
|
||||
AILLabel LblEnd = new AILLabel();
|
||||
|
||||
Context.EmitCondBranch(LblTrue, Op.Cond);
|
||||
Context.EmitLdintzr(Op.Rm);
|
||||
|
||||
if (CselOp == CselOperation.Increment)
|
||||
{
|
||||
Context.EmitLdc_I(1);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
}
|
||||
else if (CselOp == CselOperation.Invert)
|
||||
{
|
||||
Context.Emit(OpCodes.Not);
|
||||
}
|
||||
else if (CselOp == CselOperation.Negate)
|
||||
{
|
||||
Context.Emit(OpCodes.Neg);
|
||||
}
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
|
||||
Context.Emit(OpCodes.Br_S, LblEnd);
|
||||
|
||||
Context.MarkLabel(LblTrue);
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
|
||||
Context.MarkLabel(LblEnd);
|
||||
}
|
||||
}
|
||||
}
|
33
Ryujinx/Cpu/Instruction/AInstEmitException.cs
Normal file
33
Ryujinx/Cpu/Instruction/AInstEmitException.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
public static void Svc(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeException Op = (AOpCodeException)Context.CurrOp;
|
||||
|
||||
Context.EmitStoreState();
|
||||
|
||||
Context.EmitLdarg(ATranslatedSub.RegistersArgIdx);
|
||||
|
||||
Context.EmitLdc_I4(Op.Id);
|
||||
|
||||
Context.EmitCall(typeof(ARegisters), nameof(ARegisters.OnSvcCall));
|
||||
|
||||
if (Context.CurrBlock.Next != null)
|
||||
{
|
||||
Context.EmitLoadState(Context.CurrBlock.Next);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Und(AILEmitterCtx Context)
|
||||
{
|
||||
throw new Exception("und inst! " + Context.CurrOp.Position.ToString("x8"));
|
||||
}
|
||||
}
|
||||
}
|
124
Ryujinx/Cpu/Instruction/AInstEmitFlow.cs
Normal file
124
Ryujinx/Cpu/Instruction/AInstEmitFlow.cs
Normal file
|
@ -0,0 +1,124 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
public static void B(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeBImmAl Op = (AOpCodeBImmAl)Context.CurrOp;
|
||||
|
||||
Context.Emit(OpCodes.Br, Context.GetLabel(Op.Imm));
|
||||
}
|
||||
|
||||
public static void B_Cond(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeBImmCond Op = (AOpCodeBImmCond)Context.CurrOp;
|
||||
|
||||
Context.EmitCondBranch(Context.GetLabel(Op.Imm), Op.Cond);
|
||||
}
|
||||
|
||||
public static void Bl(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeBImmAl Op = (AOpCodeBImmAl)Context.CurrOp;
|
||||
|
||||
Context.EmitLdc_I(Op.Position + 4);
|
||||
Context.EmitStint(ARegisters.LRIndex);
|
||||
Context.EmitStoreState();
|
||||
|
||||
if (Context.TryOptEmitSubroutineCall())
|
||||
{
|
||||
//Note: the return value of the called method will be placed
|
||||
//at the Stack, the return value is always a Int64 with the
|
||||
//return address of the function. We check if the address is
|
||||
//correct, if it isn't we keep returning until we reach the dispatcher.
|
||||
Context.Emit(OpCodes.Dup);
|
||||
|
||||
Context.EmitLdc_I8(Op.Position + 4);
|
||||
|
||||
AILLabel LblContinue = new AILLabel();
|
||||
|
||||
Context.Emit(OpCodes.Beq_S, LblContinue);
|
||||
Context.Emit(OpCodes.Ret);
|
||||
|
||||
Context.MarkLabel(LblContinue);
|
||||
|
||||
Context.Emit(OpCodes.Pop);
|
||||
|
||||
if (Context.CurrBlock.Next != null)
|
||||
{
|
||||
Context.EmitLoadState(Context.CurrBlock.Next);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.EmitLdc_I8(Op.Imm);
|
||||
|
||||
Context.Emit(OpCodes.Ret);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Blr(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp;
|
||||
|
||||
Context.EmitLdc_I(Op.Position + 4);
|
||||
Context.EmitStint(ARegisters.LRIndex);
|
||||
Context.EmitStoreState();
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
Context.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
public static void Br(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp;
|
||||
|
||||
Context.EmitStoreState();
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
Context.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
public static void Cbnz(AILEmitterCtx Context) => EmitCb(Context, OpCodes.Bne_Un);
|
||||
public static void Cbz(AILEmitterCtx Context) => EmitCb(Context, OpCodes.Beq);
|
||||
|
||||
private static void EmitCb(AILEmitterCtx Context, OpCode ILOp)
|
||||
{
|
||||
AOpCodeBImmCmp Op = (AOpCodeBImmCmp)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rt);
|
||||
Context.EmitLdc_I(0);
|
||||
|
||||
Context.Emit(ILOp, Context.GetLabel(Op.Imm));
|
||||
}
|
||||
|
||||
public static void Ret(AILEmitterCtx Context)
|
||||
{
|
||||
Context.EmitStoreState();
|
||||
Context.EmitLdint(ARegisters.LRIndex);
|
||||
|
||||
Context.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
public static void Tbnz(AILEmitterCtx Context) => EmitTb(Context, OpCodes.Bne_Un);
|
||||
public static void Tbz(AILEmitterCtx Context) => EmitTb(Context, OpCodes.Beq);
|
||||
|
||||
private static void EmitTb(AILEmitterCtx Context, OpCode ILOp)
|
||||
{
|
||||
AOpCodeBImmTest Op = (AOpCodeBImmTest)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rt);
|
||||
Context.EmitLdc_I(1L << Op.Pos);
|
||||
|
||||
Context.Emit(OpCodes.And);
|
||||
|
||||
Context.EmitLdc_I(0);
|
||||
|
||||
Context.Emit(ILOp, Context.GetLabel(Op.Imm));
|
||||
}
|
||||
}
|
||||
}
|
252
Ryujinx/Cpu/Instruction/AInstEmitMemory.cs
Normal file
252
Ryujinx/Cpu/Instruction/AInstEmitMemory.cs
Normal file
|
@ -0,0 +1,252 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.Translation;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
using static ChocolArm64.Instruction.AInstEmitMemoryHelper;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
public static void Adr(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeAdr Op = (AOpCodeAdr)Context.CurrOp;
|
||||
|
||||
Context.EmitLdc_I(Op.Position + Op.Imm);
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Adrp(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeAdr Op = (AOpCodeAdr)Context.CurrOp;
|
||||
|
||||
Context.EmitLdc_I((Op.Position & ~0xfff) + (Op.Imm << 12));
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Ldr(AILEmitterCtx Context) => EmitLdr(Context, false);
|
||||
public static void Ldrs(AILEmitterCtx Context) => EmitLdr(Context, true);
|
||||
|
||||
public static void EmitLdr(AILEmitterCtx Context, bool Signed)
|
||||
{
|
||||
AOpCodeMem Op = (AOpCodeMem)Context.CurrOp;
|
||||
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
|
||||
EmitLoadAddress(Context);
|
||||
|
||||
if (Signed && Op.Extend64)
|
||||
{
|
||||
EmitReadSx64Call(Context, Op.Size);
|
||||
}
|
||||
else if (Signed)
|
||||
{
|
||||
EmitReadSx32Call(Context, Op.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitReadZxCall(Context, Op.Size);
|
||||
}
|
||||
|
||||
if (Op is IAOpCodeSimd)
|
||||
{
|
||||
Context.EmitStvec(Op.Rt);
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.EmitStintzr(Op.Rt);
|
||||
}
|
||||
|
||||
EmitWBackIfNeeded(Context);
|
||||
}
|
||||
|
||||
public static void LdrLit(AILEmitterCtx Context)
|
||||
{
|
||||
IAOpCodeLit Op = (IAOpCodeLit)Context.CurrOp;
|
||||
|
||||
if (Op.Prefetch)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
Context.EmitLdc_I8(Op.Imm);
|
||||
|
||||
if (Op.Signed)
|
||||
{
|
||||
EmitReadSx64Call(Context, Op.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitReadZxCall(Context, Op.Size);
|
||||
}
|
||||
|
||||
if (Op is IAOpCodeSimd)
|
||||
{
|
||||
Context.EmitStvec(Op.Rt);
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.EmitStint(Op.Rt);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Ldp(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeMemPair Op = (AOpCodeMemPair)Context.CurrOp;
|
||||
|
||||
void EmitReadAndStore(int Rt)
|
||||
{
|
||||
if (Op.Extend64)
|
||||
{
|
||||
EmitReadSx64Call(Context, Op.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitReadZxCall(Context, Op.Size);
|
||||
}
|
||||
|
||||
if (Op is IAOpCodeSimd)
|
||||
{
|
||||
Context.EmitStvec(Rt);
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.EmitStintzr(Rt);
|
||||
}
|
||||
}
|
||||
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
|
||||
EmitLoadAddress(Context);
|
||||
|
||||
EmitReadAndStore(Op.Rt);
|
||||
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
Context.EmitLdtmp();
|
||||
Context.EmitLdc_I8(1 << Op.Size);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
EmitReadAndStore(Op.Rt2);
|
||||
|
||||
EmitWBackIfNeeded(Context);
|
||||
}
|
||||
|
||||
public static void Str(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeMem Op = (AOpCodeMem)Context.CurrOp;
|
||||
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
|
||||
EmitLoadAddress(Context);
|
||||
|
||||
if (Op is IAOpCodeSimd)
|
||||
{
|
||||
Context.EmitLdvec(Op.Rt);
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.EmitLdintzr(Op.Rt);
|
||||
}
|
||||
|
||||
EmitWriteCall(Context, Op.Size);
|
||||
|
||||
EmitWBackIfNeeded(Context);
|
||||
}
|
||||
|
||||
public static void Stp(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeMemPair Op = (AOpCodeMemPair)Context.CurrOp;
|
||||
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
|
||||
EmitLoadAddress(Context);
|
||||
|
||||
if (Op is IAOpCodeSimd)
|
||||
{
|
||||
Context.EmitLdvec(Op.Rt);
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.EmitLdintzr(Op.Rt);
|
||||
}
|
||||
|
||||
EmitWriteCall(Context, Op.Size);
|
||||
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
Context.EmitLdtmp();
|
||||
Context.EmitLdc_I8(1 << Op.Size);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
if (Op is IAOpCodeSimd)
|
||||
{
|
||||
Context.EmitLdvec(Op.Rt2);
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.EmitLdintzr(Op.Rt2);
|
||||
}
|
||||
|
||||
EmitWriteCall(Context, Op.Size);
|
||||
|
||||
EmitWBackIfNeeded(Context);
|
||||
}
|
||||
|
||||
private static void EmitLoadAddress(AILEmitterCtx Context)
|
||||
{
|
||||
switch (Context.CurrOp)
|
||||
{
|
||||
case AOpCodeMemImm Op:
|
||||
Context.EmitLdint(Op.Rn);
|
||||
|
||||
if (!Op.PostIdx)
|
||||
{
|
||||
//Pre-indexing.
|
||||
Context.EmitLdc_I(Op.Imm);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
}
|
||||
break;
|
||||
|
||||
case AOpCodeMemReg Op:
|
||||
Context.EmitLdint(Op.Rn);
|
||||
Context.EmitLdintzr(Op.Rm);
|
||||
Context.EmitCast(Op.IntType);
|
||||
|
||||
if (Op.Shift)
|
||||
{
|
||||
Context.EmitLsl(Op.Size);
|
||||
}
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
break;
|
||||
}
|
||||
|
||||
//Save address to Scratch var since the register value may change.
|
||||
Context.Emit(OpCodes.Dup);
|
||||
|
||||
Context.EmitSttmp();
|
||||
}
|
||||
|
||||
private static void EmitWBackIfNeeded(AILEmitterCtx Context)
|
||||
{
|
||||
//Check whenever the current OpCode has post-indexed write back, if so write it.
|
||||
//Note: AOpCodeMemPair inherits from AOpCodeMemImm, so this works for both.
|
||||
if (Context.CurrOp is AOpCodeMemImm Op && Op.WBack)
|
||||
{
|
||||
Context.EmitLdtmp();
|
||||
|
||||
if (Op.PostIdx)
|
||||
{
|
||||
Context.EmitLdc_I(Op.Imm);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
}
|
||||
|
||||
Context.EmitStint(Op.Rn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
180
Ryujinx/Cpu/Instruction/AInstEmitMemoryEx.cs
Normal file
180
Ryujinx/Cpu/Instruction/AInstEmitMemoryEx.cs
Normal file
|
@ -0,0 +1,180 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.Memory;
|
||||
using ChocolArm64.Translation;
|
||||
using System;
|
||||
using System.Reflection.Emit;
|
||||
using System.Threading;
|
||||
|
||||
using static ChocolArm64.Instruction.AInstEmitMemoryHelper;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
[Flags]
|
||||
private enum AccessType
|
||||
{
|
||||
None = 0,
|
||||
Ordered = 1,
|
||||
Exclusive = 2,
|
||||
OrderedEx = Ordered | Exclusive
|
||||
}
|
||||
|
||||
public static void Clrex(AILEmitterCtx Context)
|
||||
{
|
||||
EmitMemoryCall(Context, nameof(AMemory.ClearExclusive));
|
||||
}
|
||||
|
||||
public static void Dmb(AILEmitterCtx Context) => EmitBarrier(Context);
|
||||
public static void Dsb(AILEmitterCtx Context) => EmitBarrier(Context);
|
||||
|
||||
public static void Ldar(AILEmitterCtx Context) => EmitLdr(Context, AccessType.Ordered);
|
||||
public static void Ldaxr(AILEmitterCtx Context) => EmitLdr(Context, AccessType.OrderedEx);
|
||||
public static void Ldxr(AILEmitterCtx Context) => EmitLdr(Context, AccessType.Exclusive);
|
||||
public static void Ldxp(AILEmitterCtx Context) => EmitLdp(Context, AccessType.Exclusive);
|
||||
public static void Ldaxp(AILEmitterCtx Context) => EmitLdp(Context, AccessType.OrderedEx);
|
||||
|
||||
private static void EmitLdr(AILEmitterCtx Context, AccessType AccType)
|
||||
{
|
||||
EmitLoad(Context, AccType, false);
|
||||
}
|
||||
|
||||
private static void EmitLdp(AILEmitterCtx Context, AccessType AccType)
|
||||
{
|
||||
EmitLoad(Context, AccType, true);
|
||||
}
|
||||
|
||||
private static void EmitLoad(AILEmitterCtx Context, AccessType AccType, bool Pair)
|
||||
{
|
||||
AOpCodeMemEx Op = (AOpCodeMemEx)Context.CurrOp;
|
||||
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
Context.EmitLdint(Op.Rn);
|
||||
|
||||
EmitReadZxCall(Context, Op.Size);
|
||||
|
||||
Context.EmitStintzr(Op.Rt);
|
||||
|
||||
if (Pair)
|
||||
{
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
Context.EmitLdint(Op.Rn);
|
||||
Context.EmitLdc_I(8 << Op.Size);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
EmitReadZxCall(Context, Op.Size);
|
||||
|
||||
Context.EmitStintzr(Op.Rt2);
|
||||
}
|
||||
|
||||
if (AccType.HasFlag(AccessType.Exclusive))
|
||||
{
|
||||
EmitMemoryCall(Context, nameof(AMemory.SetExclusive), Op.Rn);
|
||||
}
|
||||
|
||||
if (AccType.HasFlag(AccessType.Ordered))
|
||||
{
|
||||
EmitBarrier(Context);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Pfrm(AILEmitterCtx Context)
|
||||
{
|
||||
//Memory Prefetch, execute as no-op.
|
||||
}
|
||||
|
||||
public static void Stlr(AILEmitterCtx Context) => EmitStr(Context, AccessType.Ordered);
|
||||
public static void Stlxr(AILEmitterCtx Context) => EmitStr(Context, AccessType.OrderedEx);
|
||||
public static void Stxr(AILEmitterCtx Context) => EmitStr(Context, AccessType.Exclusive);
|
||||
public static void Stxp(AILEmitterCtx Context) => EmitStp(Context, AccessType.Exclusive);
|
||||
public static void Stlxp(AILEmitterCtx Context) => EmitStp(Context, AccessType.OrderedEx);
|
||||
|
||||
private static void EmitStr(AILEmitterCtx Context, AccessType AccType)
|
||||
{
|
||||
EmitStore(Context, AccType, false);
|
||||
}
|
||||
|
||||
private static void EmitStp(AILEmitterCtx Context, AccessType AccType)
|
||||
{
|
||||
EmitStore(Context, AccType, true);
|
||||
}
|
||||
|
||||
private static void EmitStore(AILEmitterCtx Context, AccessType AccType, bool Pair)
|
||||
{
|
||||
AOpCodeMemEx Op = (AOpCodeMemEx)Context.CurrOp;
|
||||
|
||||
if (AccType.HasFlag(AccessType.Ordered))
|
||||
{
|
||||
EmitBarrier(Context);
|
||||
}
|
||||
|
||||
AILLabel LblEx = new AILLabel();
|
||||
AILLabel LblEnd = new AILLabel();
|
||||
|
||||
if (AccType.HasFlag(AccessType.Exclusive))
|
||||
{
|
||||
EmitMemoryCall(Context, nameof(AMemory.TestExclusive), Op.Rn);
|
||||
|
||||
Context.Emit(OpCodes.Brtrue_S, LblEx);
|
||||
|
||||
Context.EmitLdc_I8(1);
|
||||
Context.EmitStintzr(Op.Rs);
|
||||
|
||||
Context.Emit(OpCodes.Br_S, LblEnd);
|
||||
}
|
||||
|
||||
Context.MarkLabel(LblEx);
|
||||
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
Context.EmitLdint(Op.Rn);
|
||||
Context.EmitLdintzr(Op.Rt);
|
||||
|
||||
EmitWriteCall(Context, Op.Size);
|
||||
|
||||
if (Pair)
|
||||
{
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
Context.EmitLdint(Op.Rn);
|
||||
Context.EmitLdc_I(8 << Op.Size);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
Context.EmitLdintzr(Op.Rt2);
|
||||
|
||||
EmitWriteCall(Context, Op.Size);
|
||||
}
|
||||
|
||||
if (AccType.HasFlag(AccessType.Exclusive))
|
||||
{
|
||||
Context.EmitLdc_I8(0);
|
||||
Context.EmitStintzr(Op.Rs);
|
||||
|
||||
Clrex(Context);
|
||||
}
|
||||
|
||||
Context.MarkLabel(LblEnd);
|
||||
}
|
||||
|
||||
private static void EmitMemoryCall(AILEmitterCtx Context, string Name, int Rn = -1)
|
||||
{
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
Context.EmitLdarg(ATranslatedSub.RegistersArgIdx);
|
||||
|
||||
if (Rn != -1)
|
||||
{
|
||||
Context.EmitLdint(Rn);
|
||||
}
|
||||
|
||||
Context.EmitCall(typeof(AMemory), Name);
|
||||
}
|
||||
|
||||
private static void EmitBarrier(AILEmitterCtx Context)
|
||||
{
|
||||
//Note: This barrier is most likely not necessary, and probably
|
||||
//doesn't make any difference since we need to do a ton of stuff
|
||||
//(software MMU emulation) to read or write anything anyway.
|
||||
Context.EmitCall(typeof(Thread), nameof(Thread.MemoryBarrier));
|
||||
}
|
||||
}
|
||||
}
|
97
Ryujinx/Cpu/Instruction/AInstEmitMemoryHelper.cs
Normal file
97
Ryujinx/Cpu/Instruction/AInstEmitMemoryHelper.cs
Normal file
|
@ -0,0 +1,97 @@
|
|||
using ChocolArm64.Memory;
|
||||
using ChocolArm64.Translation;
|
||||
using System;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static class AInstEmitMemoryHelper
|
||||
{
|
||||
private enum Extension
|
||||
{
|
||||
Zx,
|
||||
Sx32,
|
||||
Sx64
|
||||
}
|
||||
|
||||
public static void EmitReadZxCall(AILEmitterCtx Context, int Size)
|
||||
{
|
||||
EmitReadCall(Context, Extension.Zx, Size);
|
||||
}
|
||||
|
||||
public static void EmitReadSx32Call(AILEmitterCtx Context, int Size)
|
||||
{
|
||||
EmitReadCall(Context, Extension.Sx32, Size);
|
||||
}
|
||||
|
||||
public static void EmitReadSx64Call(AILEmitterCtx Context, int Size)
|
||||
{
|
||||
EmitReadCall(Context, Extension.Sx64, Size);
|
||||
}
|
||||
|
||||
private static void EmitReadCall(AILEmitterCtx Context, Extension Ext, int Size)
|
||||
{
|
||||
if (Size < 0 || Size > 4)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
|
||||
string Name = null;
|
||||
|
||||
switch (Size)
|
||||
{
|
||||
case 0: Name = nameof(AMemory.ReadByte); break;
|
||||
case 1: Name = nameof(AMemory.ReadUInt16); break;
|
||||
case 2: Name = nameof(AMemory.ReadUInt32); break;
|
||||
case 3: Name = nameof(AMemory.ReadUInt64); break;
|
||||
case 4: Name = nameof(AMemory.ReadVector128); break;
|
||||
}
|
||||
|
||||
Context.EmitCall(typeof(AMemory), Name);
|
||||
|
||||
if (Ext == Extension.Sx32 ||
|
||||
Ext == Extension.Sx64)
|
||||
{
|
||||
switch (Size)
|
||||
{
|
||||
case 0: Context.Emit(OpCodes.Conv_I1); break;
|
||||
case 1: Context.Emit(OpCodes.Conv_I2); break;
|
||||
case 2: Context.Emit(OpCodes.Conv_I4); break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Size < 3)
|
||||
{
|
||||
Context.Emit(Ext == Extension.Sx64
|
||||
? OpCodes.Conv_I8
|
||||
: OpCodes.Conv_U8);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitWriteCall(AILEmitterCtx Context, int Size)
|
||||
{
|
||||
if (Size < 0 || Size > 4)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
|
||||
if (Size < 3)
|
||||
{
|
||||
Context.Emit(OpCodes.Conv_I4);
|
||||
}
|
||||
|
||||
string Name = null;
|
||||
|
||||
switch (Size)
|
||||
{
|
||||
case 0: Name = nameof(AMemory.WriteByte); break;
|
||||
case 1: Name = nameof(AMemory.WriteUInt16); break;
|
||||
case 2: Name = nameof(AMemory.WriteUInt32); break;
|
||||
case 3: Name = nameof(AMemory.WriteUInt64); break;
|
||||
case 4: Name = nameof(AMemory.WriteVector128); break;
|
||||
}
|
||||
|
||||
Context.EmitCall(typeof(AMemory), Name);
|
||||
}
|
||||
}
|
||||
}
|
41
Ryujinx/Cpu/Instruction/AInstEmitMove.cs
Normal file
41
Ryujinx/Cpu/Instruction/AInstEmitMove.cs
Normal file
|
@ -0,0 +1,41 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.Translation;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
public static void Movk(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeMov Op = (AOpCodeMov)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rd);
|
||||
Context.EmitLdc_I(~(0xffffL << Op.Pos));
|
||||
|
||||
Context.Emit(OpCodes.And);
|
||||
|
||||
Context.EmitLdc_I(Op.Imm);
|
||||
|
||||
Context.Emit(OpCodes.Or);
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Movn(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeMov Op = (AOpCodeMov)Context.CurrOp;
|
||||
|
||||
Context.EmitLdc_I(~Op.Imm);
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Movz(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeMov Op = (AOpCodeMov)Context.CurrOp;
|
||||
|
||||
Context.EmitLdc_I(Op.Imm);
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
}
|
||||
}
|
80
Ryujinx/Cpu/Instruction/AInstEmitMul.cs
Normal file
80
Ryujinx/Cpu/Instruction/AInstEmitMul.cs
Normal file
|
@ -0,0 +1,80 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.Translation;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
public static void Madd(AILEmitterCtx Context) => EmitMul(Context, OpCodes.Add);
|
||||
public static void Msub(AILEmitterCtx Context) => EmitMul(Context, OpCodes.Sub);
|
||||
|
||||
private static void EmitMul(AILEmitterCtx Context, OpCode ILOp)
|
||||
{
|
||||
AOpCodeMul Op = (AOpCodeMul)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Ra);
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
Context.EmitLdintzr(Op.Rm);
|
||||
|
||||
Context.Emit(OpCodes.Mul);
|
||||
Context.Emit(ILOp);
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Smaddl(AILEmitterCtx Context) => EmitMull(Context, OpCodes.Add, true);
|
||||
public static void Smsubl(AILEmitterCtx Context) => EmitMull(Context, OpCodes.Sub, true);
|
||||
public static void Umaddl(AILEmitterCtx Context) => EmitMull(Context, OpCodes.Add, false);
|
||||
public static void Umsubl(AILEmitterCtx Context) => EmitMull(Context, OpCodes.Sub, false);
|
||||
|
||||
private static void EmitMull(AILEmitterCtx Context, OpCode AddSubOp, bool Signed)
|
||||
{
|
||||
AOpCodeMul Op = (AOpCodeMul)Context.CurrOp;
|
||||
|
||||
OpCode CastOp = Signed
|
||||
? OpCodes.Conv_I8
|
||||
: OpCodes.Conv_U8;
|
||||
|
||||
Context.EmitLdintzr(Op.Ra);
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
Context.Emit(OpCodes.Conv_I4);
|
||||
Context.Emit(CastOp);
|
||||
|
||||
Context.EmitLdintzr(Op.Rm);
|
||||
|
||||
Context.Emit(OpCodes.Conv_I4);
|
||||
Context.Emit(CastOp);
|
||||
Context.Emit(OpCodes.Mul);
|
||||
|
||||
Context.Emit(AddSubOp);
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Smulh(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeMul Op = (AOpCodeMul)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
Context.EmitLdintzr(Op.Rm);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SMulHi128));
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Umulh(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeMul Op = (AOpCodeMul)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
Context.EmitLdintzr(Op.Rm);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.UMulHi128));
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
}
|
||||
}
|
659
Ryujinx/Cpu/Instruction/AInstEmitScalar.cs
Normal file
659
Ryujinx/Cpu/Instruction/AInstEmitScalar.cs
Normal file
|
@ -0,0 +1,659 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
public static void Addp_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Addp_S));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Dup_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.DstIndex);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Dup_S));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fabs_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsf(Op.Rn);
|
||||
|
||||
MethodInfo MthdInfo;
|
||||
|
||||
if (Op.Size == 0)
|
||||
{
|
||||
MthdInfo = typeof(MathF).GetMethod(nameof(MathF.Abs), new Type[] { typeof(float) });
|
||||
}
|
||||
else if (Op.Size == 1)
|
||||
{
|
||||
MthdInfo = typeof(Math).GetMethod(nameof(Math.Abs), new Type[] { typeof(double) });
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
Context.EmitCall(MthdInfo);
|
||||
|
||||
Context.EmitStvecsf(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fadd_S(AILEmitterCtx Context) => EmitScalarOp(Context, OpCodes.Add);
|
||||
|
||||
public static void Fccmp_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdFcond Op = (AOpCodeSimdFcond)Context.CurrOp;
|
||||
|
||||
AILLabel LblTrue = new AILLabel();
|
||||
AILLabel LblEnd = new AILLabel();
|
||||
|
||||
Context.EmitCondBranch(LblTrue, Op.Cond);
|
||||
|
||||
//TODO: Share this logic with Ccmp.
|
||||
Context.EmitLdc_I4((Op.NZCV >> 0) & 1);
|
||||
|
||||
Context.EmitStflg((int)APState.VBit);
|
||||
|
||||
Context.EmitLdc_I4((Op.NZCV >> 1) & 1);
|
||||
|
||||
Context.EmitStflg((int)APState.CBit);
|
||||
|
||||
Context.EmitLdc_I4((Op.NZCV >> 2) & 1);
|
||||
|
||||
Context.EmitStflg((int)APState.ZBit);
|
||||
|
||||
Context.EmitLdc_I4((Op.NZCV >> 3) & 1);
|
||||
|
||||
Context.EmitStflg((int)APState.NBit);
|
||||
|
||||
Context.Emit(OpCodes.Br_S, LblEnd);
|
||||
|
||||
Context.MarkLabel(LblTrue);
|
||||
|
||||
Fcmp_S(Context);
|
||||
|
||||
Context.MarkLabel(LblEnd);
|
||||
}
|
||||
|
||||
public static void Fcmp_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
bool CmpWithZero = !(Op is AOpCodeSimdFcond) ? Op.Bit3 : false;
|
||||
|
||||
//todo
|
||||
//Context.TryMarkCondWithoutCmp();
|
||||
|
||||
void EmitLoadOpers()
|
||||
{
|
||||
Context.EmitLdvecsf(Op.Rn);
|
||||
|
||||
if (CmpWithZero)
|
||||
{
|
||||
EmitLdcImmF(Context, 0, Op.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.EmitLdvecsf(Op.Rm);
|
||||
}
|
||||
}
|
||||
|
||||
//Z = Rn == Rm
|
||||
EmitLoadOpers();
|
||||
|
||||
Context.Emit(OpCodes.Ceq);
|
||||
Context.Emit(OpCodes.Dup);
|
||||
|
||||
Context.EmitStflg((int)APState.ZBit);
|
||||
|
||||
//C = Rn >= Rm
|
||||
EmitLoadOpers();
|
||||
|
||||
Context.Emit(OpCodes.Cgt);
|
||||
Context.Emit(OpCodes.Or);
|
||||
|
||||
Context.EmitStflg((int)APState.CBit);
|
||||
|
||||
//N = Rn < Rm
|
||||
EmitLoadOpers();
|
||||
|
||||
Context.Emit(OpCodes.Clt);
|
||||
|
||||
Context.EmitStflg((int)APState.NBit);
|
||||
|
||||
//Handle NaN case. If any number is NaN, then NZCV = 0011.
|
||||
AILLabel LblNotNaN = new AILLabel();
|
||||
|
||||
if (CmpWithZero)
|
||||
{
|
||||
EmitNaNCheck(Context, Op.Rn);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitNaNCheck(Context, Op.Rn);
|
||||
EmitNaNCheck(Context, Op.Rm);
|
||||
|
||||
Context.Emit(OpCodes.Or);
|
||||
}
|
||||
|
||||
Context.Emit(OpCodes.Brfalse_S, LblNotNaN);
|
||||
|
||||
Context.EmitLdc_I4(1);
|
||||
Context.EmitLdc_I4(1);
|
||||
|
||||
Context.EmitStflg((int)APState.CBit);
|
||||
Context.EmitStflg((int)APState.VBit);
|
||||
|
||||
Context.MarkLabel(LblNotNaN);
|
||||
}
|
||||
|
||||
public static void Fcsel_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdFcond Op = (AOpCodeSimdFcond)Context.CurrOp;
|
||||
|
||||
AILLabel LblTrue = new AILLabel();
|
||||
AILLabel LblEnd = new AILLabel();
|
||||
|
||||
Context.EmitCondBranch(LblTrue, Op.Cond);
|
||||
Context.EmitLdvecsf(Op.Rm);
|
||||
Context.EmitStvecsf(Op.Rd);
|
||||
|
||||
Context.Emit(OpCodes.Br_S, LblEnd);
|
||||
|
||||
Context.MarkLabel(LblTrue);
|
||||
|
||||
Context.EmitLdvecsf(Op.Rn);
|
||||
Context.EmitStvecsf(Op.Rd);
|
||||
|
||||
Context.MarkLabel(LblEnd);
|
||||
}
|
||||
|
||||
public static void Fcvt_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsf(Op.Rn);
|
||||
|
||||
EmitFloatCast(Context, Op.Opc);
|
||||
|
||||
Context.EmitStvecsf(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fcvtms_S(AILEmitterCtx Context) => EmitMathOpCvtToInt(Context, nameof(Math.Floor));
|
||||
public static void Fcvtps_S(AILEmitterCtx Context) => EmitMathOpCvtToInt(Context, nameof(Math.Ceiling));
|
||||
|
||||
public static void Fcvtzs_S(AILEmitterCtx Context) => EmitFcvtz_(Context, true);
|
||||
public static void Fcvtzu_S(AILEmitterCtx Context) => EmitFcvtz_(Context, false);
|
||||
|
||||
private static void EmitFcvtz_(AILEmitterCtx Context, bool Signed)
|
||||
{
|
||||
AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsf(Op.Rn);
|
||||
|
||||
if (Signed)
|
||||
{
|
||||
EmitCvtToInt(Context, Op.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCvtToUInt(Context, Op.Size);
|
||||
}
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fcvtzs_Fix(AILEmitterCtx Context) => EmitFcvtz__Fix(Context, true);
|
||||
public static void Fcvtzu_Fix(AILEmitterCtx Context) => EmitFcvtz__Fix(Context, false);
|
||||
|
||||
private static void EmitFcvtz__Fix(AILEmitterCtx Context, bool Signed)
|
||||
{
|
||||
AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsf(Op.Rn);
|
||||
|
||||
EmitLdcImmF(Context, 1L << Op.FBits, Op.Size);
|
||||
|
||||
Context.Emit(OpCodes.Mul);
|
||||
|
||||
if (Signed)
|
||||
{
|
||||
EmitCvtToInt(Context, Op.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCvtToUInt(Context, Op.Size);
|
||||
}
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fdiv_S(AILEmitterCtx Context) => EmitScalarOp(Context, OpCodes.Div);
|
||||
|
||||
public static void Fmax_S(AILEmitterCtx Context) => EmitMathOp3(Context, nameof(Math.Max));
|
||||
public static void Fmin_S(AILEmitterCtx Context) => EmitMathOp3(Context, nameof(Math.Min));
|
||||
|
||||
public static void Fmaxnm_S(AILEmitterCtx Context) => EmitMathOp3(Context, nameof(Math.Max));
|
||||
public static void Fminnm_S(AILEmitterCtx Context) => EmitMathOp3(Context, nameof(Math.Min));
|
||||
|
||||
public static void Fmov_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdFmov Op = (AOpCodeSimdFmov)Context.CurrOp;
|
||||
|
||||
Context.EmitLdc_I8(Op.Imm);
|
||||
Context.EmitLdc_I4(0);
|
||||
Context.EmitLdc_I4(Op.Size + 2);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Fmov_S));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fmov_Ftoi(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsi(Op.Rn);
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fmov_Itof(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
Context.EmitStvecsi(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fmov_Ftoi1(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4(1);
|
||||
Context.EmitLdc_I4(3);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.ExtractVec));
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fmov_Itof1(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
Context.EmitLdc_I4(1);
|
||||
Context.EmitLdc_I4(3);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Fmov_S));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fmul_S(AILEmitterCtx Context) => EmitScalarOp(Context, OpCodes.Mul);
|
||||
|
||||
public static void Fneg_S(AILEmitterCtx Context) => EmitScalarOp(Context, OpCodes.Neg);
|
||||
|
||||
public static void Fnmul_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsf(Op.Rn);
|
||||
Context.EmitLdvecsf(Op.Rm);
|
||||
|
||||
Context.Emit(OpCodes.Mul);
|
||||
Context.Emit(OpCodes.Neg);
|
||||
|
||||
Context.EmitStvecsf(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Frinta_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsf(Op.Rn);
|
||||
Context.EmitLdc_I4((int)MidpointRounding.AwayFromZero);
|
||||
|
||||
MethodInfo MthdInfo;
|
||||
|
||||
if (Op.Size == 0)
|
||||
{
|
||||
Type[] Types = new Type[] { typeof(float), typeof(MidpointRounding) };
|
||||
|
||||
MthdInfo = typeof(MathF).GetMethod(nameof(MathF.Round), Types);
|
||||
}
|
||||
else if (Op.Size == 1)
|
||||
{
|
||||
Type[] Types = new Type[] { typeof(double), typeof(MidpointRounding) };
|
||||
|
||||
MthdInfo = typeof(Math).GetMethod(nameof(Math.Round), Types);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
Context.EmitCall(MthdInfo);
|
||||
|
||||
Context.EmitStvecsf(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Frintm_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsf(Op.Rn);
|
||||
|
||||
MethodInfo MthdInfo;
|
||||
|
||||
if (Op.Size == 0)
|
||||
{
|
||||
MthdInfo = typeof(MathF).GetMethod(nameof(MathF.Floor), new Type[] { typeof(float) });
|
||||
}
|
||||
else if (Op.Size == 1)
|
||||
{
|
||||
MthdInfo = typeof(Math).GetMethod(nameof(Math.Floor), new Type[] { typeof(double) });
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
Context.EmitCall(MthdInfo);
|
||||
|
||||
Context.EmitStvecsf(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fsqrt_S(AILEmitterCtx Context) => EmitMathOp2(Context, nameof(Math.Sqrt));
|
||||
|
||||
public static void Fsub_S(AILEmitterCtx Context) => EmitScalarOp(Context, OpCodes.Sub);
|
||||
|
||||
public static void Scvtf_Gp(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
EmitFloatCast(Context, Op.Size);
|
||||
|
||||
Context.EmitStvecsf(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Scvtf_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsi(Op.Rn);
|
||||
|
||||
EmitFloatCast(Context, Op.Size);
|
||||
|
||||
Context.EmitStvecsf(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Shl_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsi(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.Imm - (8 << Op.Size));
|
||||
|
||||
Context.Emit(OpCodes.Shl);
|
||||
|
||||
Context.EmitStvecsi(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Sshr_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsi(Op.Rn);
|
||||
Context.EmitLdc_I4((8 << (Op.Size + 1)) - Op.Imm);
|
||||
|
||||
Context.Emit(OpCodes.Shr);
|
||||
|
||||
Context.EmitStvecsi(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Sub_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsi(Op.Rn);
|
||||
Context.EmitLdvecsi(Op.Rm);
|
||||
|
||||
Context.Emit(OpCodes.Sub);
|
||||
|
||||
Context.EmitStvecsi(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Ucvtf_Gp(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
Context.Emit(OpCodes.Conv_R_Un);
|
||||
|
||||
EmitFloatCast(Context, Op.Size);
|
||||
|
||||
Context.EmitStvecsf(Op.Rd);
|
||||
}
|
||||
|
||||
private static void EmitScalarOp(AILEmitterCtx Context, OpCode ILOp)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsf(Op.Rn);
|
||||
|
||||
//Negate and Not are the only unary operations supported on IL.
|
||||
//"Not" doesn't work with floats, so we don't need to compare it.
|
||||
if (ILOp != OpCodes.Neg)
|
||||
{
|
||||
Context.EmitLdvecsf(Op.Rm);
|
||||
}
|
||||
|
||||
Context.Emit(ILOp);
|
||||
|
||||
Context.EmitStvecsf(Op.Rd);
|
||||
}
|
||||
|
||||
private static void EmitMathOp2(AILEmitterCtx Context, string Name)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsf(Op.Rn);
|
||||
|
||||
EmitMathOpCall(Context, Name);
|
||||
|
||||
Context.EmitStvecsf(Op.Rd);
|
||||
}
|
||||
|
||||
private static void EmitMathOp3(AILEmitterCtx Context, string Name)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsf(Op.Rn);
|
||||
Context.EmitLdvecsf(Op.Rm);
|
||||
|
||||
EmitMathOpCall(Context, Name);
|
||||
|
||||
Context.EmitStvecsf(Op.Rd);
|
||||
}
|
||||
|
||||
public static void EmitMathOpCvtToInt(AILEmitterCtx Context, string Name)
|
||||
{
|
||||
AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsf(Op.Rn);
|
||||
|
||||
EmitMathOpCall(Context, Name);
|
||||
|
||||
EmitCvtToInt(Context, Op.Size);
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
private static void EmitMathOpCall(AILEmitterCtx Context, string Name)
|
||||
{
|
||||
IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp;
|
||||
|
||||
MethodInfo MthdInfo;
|
||||
|
||||
if (Op.Size == 0)
|
||||
{
|
||||
MthdInfo = typeof(MathF).GetMethod(Name);
|
||||
}
|
||||
else if (Op.Size == 1)
|
||||
{
|
||||
MthdInfo = typeof(Math).GetMethod(Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
Context.EmitCall(MthdInfo);
|
||||
}
|
||||
|
||||
private static void EmitCvtToInt(AILEmitterCtx Context, int Size)
|
||||
{
|
||||
if (Size < 0 || Size > 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
|
||||
Context.EmitLdc_I4(0);
|
||||
|
||||
if (Context.CurrOp.RegisterSize == ARegisterSize.Int32)
|
||||
{
|
||||
if (Size == 0)
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatSingleToInt32));
|
||||
}
|
||||
else /* if (Size == 1) */
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatDoubleToInt32));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Size == 0)
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatSingleToInt64));
|
||||
}
|
||||
else /* if (Size == 1) */
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatDoubleToInt64));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitCvtToUInt(AILEmitterCtx Context, int Size)
|
||||
{
|
||||
if (Size < 0 || Size > 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
|
||||
Context.EmitLdc_I4(0);
|
||||
|
||||
if (Context.CurrOp.RegisterSize == ARegisterSize.Int32)
|
||||
{
|
||||
if (Size == 0)
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatSingleToUInt32));
|
||||
}
|
||||
else /* if (Size == 1) */
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatDoubleToUInt32));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Size == 0)
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatSingleToUInt64));
|
||||
}
|
||||
else /* if (Size == 1) */
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatDoubleToUInt64));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitFloatCast(AILEmitterCtx Context, int Size)
|
||||
{
|
||||
if (Size == 0)
|
||||
{
|
||||
Context.Emit(OpCodes.Conv_R4);
|
||||
}
|
||||
else if (Size == 1)
|
||||
{
|
||||
Context.Emit(OpCodes.Conv_R8);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitLdcImmF(AILEmitterCtx Context, double ImmF, int Size)
|
||||
{
|
||||
if (Size == 0)
|
||||
{
|
||||
Context.EmitLdc_R4((float)ImmF);
|
||||
}
|
||||
else if (Size == 1)
|
||||
{
|
||||
Context.EmitLdc_R8(ImmF);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitNaNCheck(AILEmitterCtx Context, int Index)
|
||||
{
|
||||
IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvecsf(Index);
|
||||
|
||||
if (Op.Size == 0)
|
||||
{
|
||||
Context.EmitCall(typeof(float), nameof(float.IsNaN));
|
||||
}
|
||||
else if (Op.Size == 1)
|
||||
{
|
||||
Context.EmitCall(typeof(double), nameof(double.IsNaN));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
965
Ryujinx/Cpu/Instruction/AInstEmitSimd.cs
Normal file
965
Ryujinx/Cpu/Instruction/AInstEmitSimd.cs
Normal file
|
@ -0,0 +1,965 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
using static ChocolArm64.Instruction.AInstEmitMemoryHelper;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
public static void Add_V(AILEmitterCtx Context) => EmitVectorBinaryZx(Context, OpCodes.Add);
|
||||
|
||||
public static void Addp_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdvec(Op.Rm);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Addp64),
|
||||
nameof(ASoftFallback.Addp128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Addv_V(AILEmitterCtx Context) => EmitVectorAddv(Context);
|
||||
|
||||
public static void And_V(AILEmitterCtx Context) => EmitVectorBinaryZx(Context, OpCodes.And);
|
||||
|
||||
public static void Bic_V(AILEmitterCtx Context) => EmitVectorBic(Context);
|
||||
public static void Bic_Vi(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdImm Op = (AOpCodeSimdImm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rd);
|
||||
Context.EmitLdc_I8(Op.Imm);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Bic_Vi64),
|
||||
nameof(ASoftFallback.Bic_Vi128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Bsl_V(AILEmitterCtx Context) => EmitVectorBsl(Context);
|
||||
|
||||
public static void Cmeq_V(AILEmitterCtx Context) => EmitVectorCmp(Context, OpCodes.Beq_S);
|
||||
public static void Cmge_V(AILEmitterCtx Context) => EmitVectorCmp(Context, OpCodes.Bge_S);
|
||||
public static void Cmgt_V(AILEmitterCtx Context) => EmitVectorCmp(Context, OpCodes.Bgt_S);
|
||||
public static void Cmhi_V(AILEmitterCtx Context) => EmitVectorCmp(Context, OpCodes.Bgt_Un_S);
|
||||
public static void Cmhs_V(AILEmitterCtx Context) => EmitVectorCmp(Context, OpCodes.Bge_Un_S);
|
||||
public static void Cmle_V(AILEmitterCtx Context) => EmitVectorCmp(Context, OpCodes.Ble_S);
|
||||
public static void Cmlt_V(AILEmitterCtx Context) => EmitVectorCmp(Context, OpCodes.Blt_S);
|
||||
|
||||
public static void Cnt_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Cnt64),
|
||||
nameof(ASoftFallback.Cnt128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Dup_Gp(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp;
|
||||
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Dup_Gp64),
|
||||
nameof(ASoftFallback.Dup_Gp128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Dup_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.DstIndex);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Dup_V64),
|
||||
nameof(ASoftFallback.Dup_V128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Eor_V(AILEmitterCtx Context) => EmitVectorBinaryZx(Context, OpCodes.Xor);
|
||||
|
||||
public static void Fadd_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdvec(Op.Rm);
|
||||
Context.EmitLdc_I4(Op.SizeF);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Fadd64),
|
||||
nameof(ASoftFallback.Fadd128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fcvtzs_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.SizeF);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Fcvtzs_V64),
|
||||
nameof(ASoftFallback.Fcvtzs_V128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fcvtzu_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4(0);
|
||||
Context.EmitLdc_I4(Op.SizeF);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Fcvtzu_V_64),
|
||||
nameof(ASoftFallback.Fcvtzu_V_128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fcvtzu_V_Fix(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4((8 << (Op.Size + 1)) - Op.Imm);
|
||||
Context.EmitLdc_I4(Op.Size - 2);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Fcvtzu_V_64),
|
||||
nameof(ASoftFallback.Fcvtzu_V_128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fmla_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rd);
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdvec(Op.Rm);
|
||||
Context.EmitLdc_I4(Op.SizeF);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Fmla64),
|
||||
nameof(ASoftFallback.Fmla128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fmla_Vs(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rd);
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdvec(Op.Rm);
|
||||
Context.EmitLdc_I4(Op.Index);
|
||||
Context.EmitLdc_I4(Op.SizeF);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Fmla_Ve64),
|
||||
nameof(ASoftFallback.Fmla_Ve128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fmov_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdImm Op = (AOpCodeSimdImm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdc_I8(Op.Imm);
|
||||
Context.EmitLdc_I4(Op.Size + 2);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Dup_Gp64),
|
||||
nameof(ASoftFallback.Dup_Gp128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fmul_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdvec(Op.Rm);
|
||||
Context.EmitLdc_I4(Op.SizeF);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Fmul64),
|
||||
nameof(ASoftFallback.Fmul128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fmul_Vs(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdvec(Op.Rm);
|
||||
Context.EmitLdc_I4(Op.Index);
|
||||
Context.EmitLdc_I4(Op.SizeF);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Fmul_Ve64),
|
||||
nameof(ASoftFallback.Fmul_Ve128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Fsub_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdvec(Op.Rm);
|
||||
Context.EmitLdc_I4(Op.SizeF);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Fsub64),
|
||||
nameof(ASoftFallback.Fsub128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Ins_Gp(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rd);
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.DstIndex);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Ins_Gp));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Ins_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rd);
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.SrcIndex);
|
||||
Context.EmitLdc_I4(Op.DstIndex);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Ins_V));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Ld__V(AILEmitterCtx Context) => EmitSimdMultLdSt(Context, IsLoad: true);
|
||||
|
||||
public static void Mla_V(AILEmitterCtx Context) => EmitVectorMla(Context);
|
||||
|
||||
public static void Movi_V(AILEmitterCtx Context) => EmitMovi_V(Context, false);
|
||||
|
||||
public static void Mul_V(AILEmitterCtx Context) => EmitVectorBinaryZx(Context, OpCodes.Mul);
|
||||
|
||||
public static void Mvni_V(AILEmitterCtx Context) => EmitMovi_V(Context, true);
|
||||
|
||||
private static void EmitMovi_V(AILEmitterCtx Context, bool Not)
|
||||
{
|
||||
AOpCodeSimdImm Op = (AOpCodeSimdImm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdc_I8(Not ? ~Op.Imm : Op.Imm);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Dup_Gp64),
|
||||
nameof(ASoftFallback.Dup_Gp128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Neg_V(AILEmitterCtx Context) => EmitVectorUnarySx(Context, OpCodes.Neg);
|
||||
|
||||
public static void Not_V(AILEmitterCtx Context) => EmitVectorUnaryZx(Context, OpCodes.Not);
|
||||
|
||||
public static void Orr_V(AILEmitterCtx Context) => EmitVectorBinaryZx(Context, OpCodes.Or);
|
||||
|
||||
public static void Orr_Vi(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdImm Op = (AOpCodeSimdImm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rd);
|
||||
Context.EmitLdc_I8(Op.Imm);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Orr_Vi64),
|
||||
nameof(ASoftFallback.Orr_Vi128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Saddw_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdvec(Op.Rm);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Saddw),
|
||||
nameof(ASoftFallback.Saddw2));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Scvtf_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.SizeF);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Scvtf_V64),
|
||||
nameof(ASoftFallback.Scvtf_V128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Shl_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.Imm - (8 << Op.Size));
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Shl64),
|
||||
nameof(ASoftFallback.Shl128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Smax_V(AILEmitterCtx Context) => EmitVectorSmax(Context);
|
||||
public static void Smin_V(AILEmitterCtx Context) => EmitVectorSmin(Context);
|
||||
|
||||
public static void Sshll_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.Imm - (8 << Op.Size));
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Sshll),
|
||||
nameof(ASoftFallback.Sshll2));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Sshr_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4((8 << (Op.Size + 1)) - Op.Imm);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Sshr64),
|
||||
nameof(ASoftFallback.Sshr128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void St__V(AILEmitterCtx Context) => EmitSimdMultLdSt(Context, IsLoad: false);
|
||||
|
||||
public static void Sub_V(AILEmitterCtx Context) => EmitVectorBinaryZx(Context, OpCodes.Sub);
|
||||
|
||||
public static void Tbl_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdTbl Op = (AOpCodeSimdTbl)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rm);
|
||||
|
||||
for (int Index = 0; Index < Op.Size; Index++)
|
||||
{
|
||||
Context.EmitLdvec((Op.Rn + Index) & 0x1f);
|
||||
}
|
||||
|
||||
switch (Op.Size)
|
||||
{
|
||||
case 1: ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Tbl1_V64),
|
||||
nameof(ASoftFallback.Tbl1_V128)); break;
|
||||
|
||||
case 2: ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Tbl2_V64),
|
||||
nameof(ASoftFallback.Tbl2_V128)); break;
|
||||
|
||||
case 3: ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Tbl3_V64),
|
||||
nameof(ASoftFallback.Tbl3_V128)); break;
|
||||
|
||||
case 4: ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Tbl4_V64),
|
||||
nameof(ASoftFallback.Tbl4_V128)); break;
|
||||
|
||||
default: throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Uaddlv_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Uaddlv64),
|
||||
nameof(ASoftFallback.Uaddlv128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Uaddw_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdvec(Op.Rm);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Uaddw),
|
||||
nameof(ASoftFallback.Uaddw2));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Ucvtf_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
|
||||
if (Op.Size == 0)
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Ucvtf_V_F));
|
||||
}
|
||||
else if (Op.Size == 1)
|
||||
{
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Ucvtf_V_D));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Umov_S(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.DstIndex);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.ExtractVec));
|
||||
|
||||
Context.EmitStintzr(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Ushl_V(AILEmitterCtx Context) => EmitVectorUshl(Context);
|
||||
|
||||
public static void Ushll_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.Imm - (8 << Op.Size));
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Ushll),
|
||||
nameof(ASoftFallback.Ushll2));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Ushr_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4((8 << (Op.Size + 1)) - Op.Imm);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Ushr64),
|
||||
nameof(ASoftFallback.Ushr128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Usra_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rd);
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4((8 << (Op.Size + 1)) - Op.Imm);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Usra64),
|
||||
nameof(ASoftFallback.Usra128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Uzp1_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdvec(Op.Rm);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Uzp1_V64),
|
||||
nameof(ASoftFallback.Uzp1_V128));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
public static void Xtn_V(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Op.Rn);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context,
|
||||
nameof(ASoftFallback.Xtn),
|
||||
nameof(ASoftFallback.Xtn2));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
private static void EmitSimdMultLdSt(AILEmitterCtx Context, bool IsLoad)
|
||||
{
|
||||
AOpCodeSimdMemMult Op = (AOpCodeSimdMemMult)Context.CurrOp;
|
||||
|
||||
int Offset = 0;
|
||||
|
||||
for (int Rep = 0; Rep < Op.Reps; Rep++)
|
||||
for (int Elem = 0; Elem < Op.Elems; Elem++)
|
||||
for (int SElem = 0; SElem < Op.SElems; SElem++)
|
||||
{
|
||||
int Rtt = (Op.Rt + Rep + SElem) & 0x1f;
|
||||
|
||||
if (IsLoad)
|
||||
{
|
||||
Context.EmitLdvec(Rtt);
|
||||
Context.EmitLdc_I4(Elem);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
Context.EmitLdint(Op.Rn);
|
||||
Context.EmitLdc_I8(Offset);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
EmitReadZxCall(Context, Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.InsertVec));
|
||||
|
||||
Context.EmitStvec(Rtt);
|
||||
|
||||
if (Op.RegisterSize == ARegisterSize.SIMD64 && Elem == Op.Elems - 1)
|
||||
{
|
||||
EmitVectorZeroUpper(Context, Rtt);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
Context.EmitLdint(Op.Rn);
|
||||
Context.EmitLdc_I8(Offset);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
Context.EmitLdvec(Rtt);
|
||||
Context.EmitLdc_I4(Elem);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.ExtractVec));
|
||||
|
||||
EmitWriteCall(Context, Op.Size);
|
||||
}
|
||||
|
||||
Offset += 1 << Op.Size;
|
||||
}
|
||||
|
||||
if (Op.WBack)
|
||||
{
|
||||
Context.EmitLdint(Op.Rn);
|
||||
|
||||
if (Op.Rm != ARegisters.ZRIndex)
|
||||
{
|
||||
Context.EmitLdint(Op.Rm);
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.EmitLdc_I8(Offset);
|
||||
}
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
Context.EmitStint(Op.Rn);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitVectorAddv(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
|
||||
|
||||
EmitVectorZeroLower(Context, Op.Rd);
|
||||
EmitVectorZeroUpper(Context, Op.Rd);
|
||||
|
||||
Context.EmitLdvec(Op.Rd);
|
||||
Context.EmitLdc_I4(0);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
EmitVectorExtractZx(Context, Op.Rn, 0);
|
||||
|
||||
for (int Index = 1; Index < (Bytes >> Op.Size); Index++)
|
||||
{
|
||||
EmitVectorExtractZx(Context, Op.Rn, Index);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
}
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.InsertVec));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
private static void EmitVectorBic(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorBinaryZx(Context, () =>
|
||||
{
|
||||
Context.Emit(OpCodes.Not);
|
||||
Context.Emit(OpCodes.And);
|
||||
});
|
||||
}
|
||||
|
||||
private static void EmitVectorBsl(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorTernaryZx(Context, () =>
|
||||
{
|
||||
Context.EmitSttmp();
|
||||
Context.EmitLdtmp();
|
||||
|
||||
Context.Emit(OpCodes.Xor);
|
||||
Context.Emit(OpCodes.And);
|
||||
|
||||
Context.EmitLdtmp();
|
||||
|
||||
Context.Emit(OpCodes.Xor);
|
||||
});
|
||||
}
|
||||
|
||||
private static void EmitVectorMla(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorTernaryZx(Context, () =>
|
||||
{
|
||||
Context.Emit(OpCodes.Mul);
|
||||
Context.Emit(OpCodes.Add);
|
||||
});
|
||||
}
|
||||
|
||||
private static void EmitVectorSmax(AILEmitterCtx Context)
|
||||
{
|
||||
Type[] Types = new Type[] { typeof(long), typeof(long) };
|
||||
|
||||
MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Max), Types);
|
||||
|
||||
EmitVectorBinarySx(Context, () => Context.EmitCall(MthdInfo));
|
||||
}
|
||||
|
||||
private static void EmitVectorSmin(AILEmitterCtx Context)
|
||||
{
|
||||
Type[] Types = new Type[] { typeof(long), typeof(long) };
|
||||
|
||||
MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Min), Types);
|
||||
|
||||
EmitVectorBinarySx(Context, () => Context.EmitCall(MthdInfo));
|
||||
}
|
||||
|
||||
private static void EmitVectorUshl(AILEmitterCtx Context)
|
||||
{
|
||||
//This instruction shifts the value on vector A by the number of bits
|
||||
//specified on the signed, lower 8 bits of vector B. If the shift value
|
||||
//is greater or equal to the data size of each lane, then the result is zero.
|
||||
//Additionally, negative shifts produces right shifts by the negated shift value.
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
int MaxShift = 8 << Op.Size;
|
||||
|
||||
EmitVectorBinaryZx(Context, () =>
|
||||
{
|
||||
AILLabel LblShl = new AILLabel();
|
||||
AILLabel LblZero = new AILLabel();
|
||||
AILLabel LblEnd = new AILLabel();
|
||||
|
||||
void EmitShift(OpCode ILOp)
|
||||
{
|
||||
Context.Emit(OpCodes.Dup);
|
||||
|
||||
Context.EmitLdc_I4(MaxShift);
|
||||
|
||||
Context.Emit(OpCodes.Bge_S, LblZero);
|
||||
Context.Emit(ILOp);
|
||||
Context.Emit(OpCodes.Br_S, LblEnd);
|
||||
}
|
||||
|
||||
Context.Emit(OpCodes.Conv_I1);
|
||||
Context.Emit(OpCodes.Dup);
|
||||
|
||||
Context.EmitLdc_I4(0);
|
||||
|
||||
Context.Emit(OpCodes.Bge_S, LblShl);
|
||||
Context.Emit(OpCodes.Neg);
|
||||
|
||||
EmitShift(OpCodes.Shr_Un);
|
||||
|
||||
Context.MarkLabel(LblShl);
|
||||
|
||||
EmitShift(OpCodes.Shl);
|
||||
|
||||
Context.MarkLabel(LblZero);
|
||||
|
||||
Context.Emit(OpCodes.Pop);
|
||||
Context.Emit(OpCodes.Pop);
|
||||
|
||||
Context.EmitLdc_I8(0);
|
||||
|
||||
Context.MarkLabel(LblEnd);
|
||||
});
|
||||
}
|
||||
|
||||
private static void EmitVectorUnarySx(AILEmitterCtx Context, OpCode ILOp)
|
||||
{
|
||||
EmitVectorUnarySx(Context, () => Context.Emit(ILOp));
|
||||
}
|
||||
|
||||
private static void EmitVectorUnaryZx(AILEmitterCtx Context, OpCode ILOp)
|
||||
{
|
||||
EmitVectorUnaryZx(Context, () => Context.Emit(ILOp));
|
||||
}
|
||||
|
||||
private static void EmitVectorBinaryZx(AILEmitterCtx Context, OpCode ILOp)
|
||||
{
|
||||
EmitVectorBinaryZx(Context, () => Context.Emit(ILOp));
|
||||
}
|
||||
|
||||
private static void EmitVectorUnarySx(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
EmitVectorOp(Context, Emit, 1, true);
|
||||
}
|
||||
|
||||
private static void EmitVectorBinarySx(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
EmitVectorOp(Context, Emit, 2, true);
|
||||
}
|
||||
|
||||
private static void EmitVectorUnaryZx(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
EmitVectorOp(Context, Emit, 1, false);
|
||||
}
|
||||
|
||||
private static void EmitVectorBinaryZx(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
EmitVectorOp(Context, Emit, 2, false);
|
||||
}
|
||||
|
||||
private static void EmitVectorTernaryZx(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
EmitVectorOp(Context, Emit, 3, false);
|
||||
}
|
||||
|
||||
private static void EmitVectorOp(AILEmitterCtx Context, Action Emit, int Opers, bool Signed)
|
||||
{
|
||||
if (Opers < 1 || Opers > 3)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Opers));
|
||||
}
|
||||
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
|
||||
|
||||
for (int Index = 0; Index < (Bytes >> Op.Size); Index++)
|
||||
{
|
||||
Context.EmitLdvec(Op.Rd);
|
||||
Context.EmitLdc_I4(Index);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
if (Opers == 3)
|
||||
{
|
||||
EmitVectorExtract(Context, Op.Rd, Index, Signed);
|
||||
}
|
||||
|
||||
if (Opers >= 1)
|
||||
{
|
||||
EmitVectorExtract(Context, Op.Rn, Index, Signed);
|
||||
}
|
||||
|
||||
if (Opers >= 2)
|
||||
{
|
||||
EmitVectorExtract(Context, ((AOpCodeSimdReg)Op).Rm, Index, Signed);
|
||||
}
|
||||
|
||||
Emit();
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.InsertVec));
|
||||
|
||||
Context.EmitStvec(Op.Rd);
|
||||
}
|
||||
|
||||
if (Op.RegisterSize == ARegisterSize.SIMD64)
|
||||
{
|
||||
EmitVectorZeroUpper(Context, Op.Rd);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitVectorCmp(AILEmitterCtx Context, OpCode ILOp)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
|
||||
|
||||
ulong SzMask = ulong.MaxValue >> (64 - (8 << Op.Size));
|
||||
|
||||
for (int Index = 0; Index < (Bytes >> Op.Size); Index++)
|
||||
{
|
||||
EmitVectorExtractSx(Context, Op.Rn, Index);
|
||||
|
||||
if (Op is AOpCodeSimdReg BinOp)
|
||||
{
|
||||
EmitVectorExtractSx(Context, BinOp.Rm, Index);
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.EmitLdc_I8(0);
|
||||
}
|
||||
|
||||
AILLabel LblTrue = new AILLabel();
|
||||
AILLabel LblEnd = new AILLabel();
|
||||
|
||||
Context.Emit(ILOp, LblTrue);
|
||||
|
||||
EmitVectorInsert(Context, Op.Rd, Index, Op.Size, 0);
|
||||
|
||||
Context.Emit(OpCodes.Br_S, LblEnd);
|
||||
|
||||
Context.MarkLabel(LblTrue);
|
||||
|
||||
EmitVectorInsert(Context, Op.Rd, Index, Op.Size, (long)SzMask);
|
||||
|
||||
Context.MarkLabel(LblEnd);
|
||||
}
|
||||
|
||||
if (Op.RegisterSize == ARegisterSize.SIMD64)
|
||||
{
|
||||
EmitVectorZeroUpper(Context, Op.Rd);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitVectorExtractSx(AILEmitterCtx Context, int Reg, int Index)
|
||||
{
|
||||
EmitVectorExtract(Context, Reg, Index, true);
|
||||
}
|
||||
|
||||
private static void EmitVectorExtractZx(AILEmitterCtx Context, int Reg, int Index)
|
||||
{
|
||||
EmitVectorExtract(Context, Reg, Index, false);
|
||||
}
|
||||
|
||||
private static void EmitVectorExtract(AILEmitterCtx Context, int Reg, int Index, bool Signed)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
Context.EmitLdvec(Reg);
|
||||
Context.EmitLdc_I4(Index);
|
||||
Context.EmitLdc_I4(Op.Size);
|
||||
|
||||
ASoftFallback.EmitCall(Context, Signed
|
||||
? nameof(ASoftFallback.ExtractSVec)
|
||||
: nameof(ASoftFallback.ExtractVec));
|
||||
}
|
||||
|
||||
private static void EmitVectorZeroLower(AILEmitterCtx Context, int Rd)
|
||||
{
|
||||
EmitVectorInsert(Context, Rd, 0, 3, 0);
|
||||
}
|
||||
|
||||
private static void EmitVectorZeroUpper(AILEmitterCtx Context, int Rd)
|
||||
{
|
||||
EmitVectorInsert(Context, Rd, 1, 3, 0);
|
||||
}
|
||||
|
||||
private static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size, long Value)
|
||||
{
|
||||
Context.EmitLdvec(Reg);
|
||||
Context.EmitLdc_I4(Index);
|
||||
Context.EmitLdc_I4(Size);
|
||||
Context.EmitLdc_I8(Value);
|
||||
|
||||
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.InsertVec));
|
||||
|
||||
Context.EmitStvec(Reg);
|
||||
}
|
||||
}
|
||||
}
|
84
Ryujinx/Cpu/Instruction/AInstEmitSystem.cs
Normal file
84
Ryujinx/Cpu/Instruction/AInstEmitSystem.cs
Normal file
|
@ -0,0 +1,84 @@
|
|||
using ChocolArm64.Decoder;
|
||||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
public static void Mrs(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSystem Op = (AOpCodeSystem)Context.CurrOp;
|
||||
|
||||
Context.EmitLdarg(ATranslatedSub.RegistersArgIdx);
|
||||
|
||||
Context.EmitLdc_I4(Op.Op0);
|
||||
Context.EmitLdc_I4(Op.Op1);
|
||||
Context.EmitLdc_I4(Op.CRn);
|
||||
Context.EmitLdc_I4(Op.CRm);
|
||||
Context.EmitLdc_I4(Op.Op2);
|
||||
|
||||
Context.EmitCall(typeof(ARegisters), nameof(ARegisters.GetSystemReg));
|
||||
|
||||
Context.EmitStintzr(Op.Rt);
|
||||
}
|
||||
|
||||
public static void Msr(AILEmitterCtx Context)
|
||||
{
|
||||
AOpCodeSystem Op = (AOpCodeSystem)Context.CurrOp;
|
||||
|
||||
Context.EmitLdarg(ATranslatedSub.RegistersArgIdx);
|
||||
|
||||
Context.EmitLdc_I4(Op.Op0);
|
||||
Context.EmitLdc_I4(Op.Op1);
|
||||
Context.EmitLdc_I4(Op.CRn);
|
||||
Context.EmitLdc_I4(Op.CRm);
|
||||
Context.EmitLdc_I4(Op.Op2);
|
||||
Context.EmitLdintzr(Op.Rt);
|
||||
|
||||
Context.EmitCall(typeof(ARegisters), nameof(ARegisters.SetSystemReg));
|
||||
}
|
||||
|
||||
public static void Nop(AILEmitterCtx Context)
|
||||
{
|
||||
//Do nothing.
|
||||
}
|
||||
|
||||
public static void Sys(AILEmitterCtx Context)
|
||||
{
|
||||
//This instruction is used to do some operations on the CPU like cache invalidation,
|
||||
//address translation and the like.
|
||||
//We treat it as no-op here since we don't have any cache being emulated anyway.
|
||||
AOpCodeSystem Op = (AOpCodeSystem)Context.CurrOp;
|
||||
|
||||
int Id;
|
||||
|
||||
Id = Op.Op2 << 0;
|
||||
Id |= Op.CRm << 3;
|
||||
Id |= Op.CRn << 7;
|
||||
Id |= Op.Op1 << 11;
|
||||
|
||||
switch (Id)
|
||||
{
|
||||
case 0b011_0111_0100_001:
|
||||
{
|
||||
//DC ZVA
|
||||
for (int Offs = 0; Offs < 64; Offs += 8)
|
||||
{
|
||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||
Context.EmitLdint(Op.Rt);
|
||||
Context.EmitLdc_I(Offs);
|
||||
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
Context.EmitLdc_I8(0);
|
||||
|
||||
AInstEmitMemoryHelper.EmitWriteCall(Context, 3);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
6
Ryujinx/Cpu/Instruction/AInstEmitter.cs
Normal file
6
Ryujinx/Cpu/Instruction/AInstEmitter.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
using ChocolArm64.Translation;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
delegate void AInstEmitter(AILEmitterCtx Context);
|
||||
}
|
1207
Ryujinx/Cpu/Instruction/ASoftFallback.cs
Normal file
1207
Ryujinx/Cpu/Instruction/ASoftFallback.cs
Normal file
File diff suppressed because it is too large
Load diff
237
Ryujinx/Cpu/Memory/AMemory.cs
Normal file
237
Ryujinx/Cpu/Memory/AMemory.cs
Normal file
|
@ -0,0 +1,237 @@
|
|||
using ChocolArm64.State;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ChocolArm64.Memory
|
||||
{
|
||||
public unsafe class AMemory
|
||||
{
|
||||
public AMemoryMgr Manager { get; private set; }
|
||||
|
||||
private struct ExMonitor
|
||||
{
|
||||
public long Position { get; private set; }
|
||||
|
||||
private bool ExState;
|
||||
|
||||
public ExMonitor(long Position, bool ExState)
|
||||
{
|
||||
this.Position = Position;
|
||||
this.ExState = ExState;
|
||||
}
|
||||
|
||||
public bool HasExclusiveAccess(long Position)
|
||||
{
|
||||
return this.Position == Position && ExState;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
ExState = false;
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<int, ExMonitor> Monitors;
|
||||
|
||||
private HashSet<long> ExAddrs;
|
||||
|
||||
private byte* RamPtr;
|
||||
|
||||
public AMemory(IntPtr Ram, AMemoryAlloc Allocator)
|
||||
{
|
||||
Manager = new AMemoryMgr(Allocator);
|
||||
|
||||
Monitors = new Dictionary<int, ExMonitor>();
|
||||
|
||||
ExAddrs = new HashSet<long>();
|
||||
|
||||
RamPtr = (byte*)Ram;
|
||||
}
|
||||
|
||||
public void RemoveMonitor(int ThreadId)
|
||||
{
|
||||
lock (Monitors)
|
||||
{
|
||||
Monitors.Remove(ThreadId);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetExclusive(ARegisters Registers, long Position)
|
||||
{
|
||||
lock (Monitors)
|
||||
{
|
||||
bool ExState = !ExAddrs.Contains(Position);
|
||||
|
||||
if (ExState)
|
||||
{
|
||||
ExAddrs.Add(Position);
|
||||
}
|
||||
|
||||
ExMonitor Monitor = new ExMonitor(Position, ExState);
|
||||
|
||||
if (!Monitors.TryAdd(Registers.ThreadId, Monitor))
|
||||
{
|
||||
Monitors[Registers.ThreadId] = Monitor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool TestExclusive(ARegisters Registers, long Position)
|
||||
{
|
||||
lock (Monitors)
|
||||
{
|
||||
if (!Monitors.TryGetValue(Registers.ThreadId, out ExMonitor Monitor))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Monitor.HasExclusiveAccess(Position);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearExclusive(ARegisters Registers)
|
||||
{
|
||||
lock (Monitors)
|
||||
{
|
||||
if (Monitors.TryGetValue(Registers.ThreadId, out ExMonitor Monitor))
|
||||
{
|
||||
Monitor.Reset();
|
||||
ExAddrs.Remove(Monitor.Position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sbyte ReadSByte(long Position) => (sbyte)ReadByte (Position);
|
||||
public short ReadInt16(long Position) => (short)ReadUInt16(Position);
|
||||
public int ReadInt32(long Position) => (int)ReadUInt32(Position);
|
||||
public long ReadInt64(long Position) => (long)ReadUInt64(Position);
|
||||
|
||||
public byte ReadByte(long Position)
|
||||
{
|
||||
return *((byte*)(RamPtr + Manager.GetPhys(Position, AMemoryPerm.Read)));
|
||||
}
|
||||
|
||||
public ushort ReadUInt16(long Position)
|
||||
{
|
||||
long PhysPos = Manager.GetPhys(Position, AMemoryPerm.Read);
|
||||
|
||||
if (BitConverter.IsLittleEndian && !IsPageCrossed(Position, 2))
|
||||
{
|
||||
return *((ushort*)(RamPtr + PhysPos));
|
||||
}
|
||||
else
|
||||
{
|
||||
return (ushort)(
|
||||
ReadByte(Position + 0) << 0 |
|
||||
ReadByte(Position + 1) << 8);
|
||||
}
|
||||
}
|
||||
|
||||
public uint ReadUInt32(long Position)
|
||||
{
|
||||
long PhysPos = Manager.GetPhys(Position, AMemoryPerm.Read);
|
||||
|
||||
if (BitConverter.IsLittleEndian && !IsPageCrossed(Position, 4))
|
||||
{
|
||||
return *((uint*)(RamPtr + PhysPos));
|
||||
}
|
||||
else
|
||||
{
|
||||
return (uint)(
|
||||
ReadUInt16(Position + 0) << 0 |
|
||||
ReadUInt16(Position + 2) << 16);
|
||||
}
|
||||
}
|
||||
|
||||
public ulong ReadUInt64(long Position)
|
||||
{
|
||||
long PhysPos = Manager.GetPhys(Position, AMemoryPerm.Read);
|
||||
|
||||
if (BitConverter.IsLittleEndian && !IsPageCrossed(Position, 8))
|
||||
{
|
||||
return *((ulong*)(RamPtr + PhysPos));
|
||||
}
|
||||
else
|
||||
{
|
||||
return
|
||||
(ulong)ReadUInt32(Position + 0) << 0 |
|
||||
(ulong)ReadUInt32(Position + 4) << 32;
|
||||
}
|
||||
}
|
||||
|
||||
public AVec ReadVector128(long Position)
|
||||
{
|
||||
return new AVec()
|
||||
{
|
||||
X0 = ReadUInt64(Position + 0),
|
||||
X1 = ReadUInt64(Position + 8)
|
||||
};
|
||||
}
|
||||
|
||||
public void WriteSByte(long Position, sbyte Value) => WriteByte (Position, (byte)Value);
|
||||
public void WriteInt16(long Position, short Value) => WriteUInt16(Position, (ushort)Value);
|
||||
public void WriteInt32(long Position, int Value) => WriteUInt32(Position, (uint)Value);
|
||||
public void WriteInt64(long Position, long Value) => WriteUInt64(Position, (ulong)Value);
|
||||
|
||||
public void WriteByte(long Position, byte Value)
|
||||
{
|
||||
*((byte*)(RamPtr + Manager.GetPhys(Position, AMemoryPerm.Write))) = Value;
|
||||
}
|
||||
|
||||
public void WriteUInt16(long Position, ushort Value)
|
||||
{
|
||||
long PhysPos = Manager.GetPhys(Position, AMemoryPerm.Write);
|
||||
|
||||
if (BitConverter.IsLittleEndian && !IsPageCrossed(Position, 2))
|
||||
{
|
||||
*((ushort*)(RamPtr + PhysPos)) = Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteByte(Position + 0, (byte)(Value >> 0));
|
||||
WriteByte(Position + 1, (byte)(Value >> 8));
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteUInt32(long Position, uint Value)
|
||||
{
|
||||
long PhysPos = Manager.GetPhys(Position, AMemoryPerm.Write);
|
||||
|
||||
if (BitConverter.IsLittleEndian && !IsPageCrossed(Position, 4))
|
||||
{
|
||||
*((uint*)(RamPtr + PhysPos)) = Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteUInt16(Position + 0, (ushort)(Value >> 0));
|
||||
WriteUInt16(Position + 2, (ushort)(Value >> 16));
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteUInt64(long Position, ulong Value)
|
||||
{
|
||||
long PhysPos = Manager.GetPhys(Position, AMemoryPerm.Write);
|
||||
|
||||
if (BitConverter.IsLittleEndian && !IsPageCrossed(Position, 8))
|
||||
{
|
||||
*((ulong*)(RamPtr + PhysPos)) = Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteUInt32(Position + 0, (uint)(Value >> 0));
|
||||
WriteUInt32(Position + 4, (uint)(Value >> 32));
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteVector128(long Position, AVec Value)
|
||||
{
|
||||
WriteUInt64(Position + 0, Value.X0);
|
||||
WriteUInt64(Position + 8, Value.X1);
|
||||
}
|
||||
|
||||
private bool IsPageCrossed(long Position, int Size)
|
||||
{
|
||||
return (Position & AMemoryMgr.PageMask) + Size > AMemoryMgr.PageSize;
|
||||
}
|
||||
}
|
||||
}
|
35
Ryujinx/Cpu/Memory/AMemoryAlloc.cs
Normal file
35
Ryujinx/Cpu/Memory/AMemoryAlloc.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
using ChocolArm64.Exceptions;
|
||||
|
||||
namespace ChocolArm64.Memory
|
||||
{
|
||||
public class AMemoryAlloc
|
||||
{
|
||||
private long PhysPos;
|
||||
|
||||
public long Alloc(long Size)
|
||||
{
|
||||
long Position = PhysPos;
|
||||
|
||||
Size = AMemoryHelper.PageRoundUp(Size);
|
||||
|
||||
PhysPos += Size;
|
||||
|
||||
if (PhysPos > AMemoryMgr.RamSize || PhysPos < 0)
|
||||
{
|
||||
throw new VmmOutOfMemoryException(Size);
|
||||
}
|
||||
|
||||
return Position;
|
||||
}
|
||||
|
||||
public void Free(long Position)
|
||||
{
|
||||
//TODO
|
||||
}
|
||||
|
||||
public long GetFreeMem()
|
||||
{
|
||||
return AMemoryMgr.RamSize - PhysPos;
|
||||
}
|
||||
}
|
||||
}
|
73
Ryujinx/Cpu/Memory/AMemoryHelper.cs
Normal file
73
Ryujinx/Cpu/Memory/AMemoryHelper.cs
Normal file
|
@ -0,0 +1,73 @@
|
|||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace ChocolArm64.Memory
|
||||
{
|
||||
public static class AMemoryHelper
|
||||
{
|
||||
public static void FillWithZeros(AMemory Memory, long Position, int Size)
|
||||
{
|
||||
int Size8 = Size & ~(8 - 1);
|
||||
|
||||
for (int Offs = 0; Offs < Size8; Offs += 8)
|
||||
{
|
||||
Memory.WriteInt64(Position + Offs, 0);
|
||||
}
|
||||
|
||||
for (int Offs = Size8; Offs < (Size - Size8); Offs++)
|
||||
{
|
||||
Memory.WriteByte(Position + Offs, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] ReadBytes(AMemory Memory, long Position, int Size)
|
||||
{
|
||||
byte[] Data = new byte[Size];
|
||||
|
||||
for (int Offs = 0; Offs < Size; Offs++)
|
||||
{
|
||||
Data[Offs] = (byte)Memory.ReadByte(Position + Offs);
|
||||
}
|
||||
|
||||
return Data;
|
||||
}
|
||||
|
||||
public static void WriteBytes(AMemory Memory, long Position, byte[] Data)
|
||||
{
|
||||
for (int Offs = 0; Offs < Data.Length; Offs++)
|
||||
{
|
||||
Memory.WriteByte(Position + Offs, Data[Offs]);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ReadAsciiString(AMemory Memory, long Position, int MaxSize = -1)
|
||||
{
|
||||
using (MemoryStream MS = new MemoryStream())
|
||||
{
|
||||
for (int Offs = 0; Offs < MaxSize || MaxSize == -1; Offs++)
|
||||
{
|
||||
byte Value = (byte)Memory.ReadByte(Position + Offs);
|
||||
|
||||
if (Value == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
MS.WriteByte(Value);
|
||||
}
|
||||
|
||||
return Encoding.ASCII.GetString(MS.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
public static long PageRoundUp(long Value)
|
||||
{
|
||||
return (Value + AMemoryMgr.PageMask) & ~AMemoryMgr.PageMask;
|
||||
}
|
||||
|
||||
public static long PageRoundDown(long Value)
|
||||
{
|
||||
return Value & ~AMemoryMgr.PageMask;
|
||||
}
|
||||
}
|
||||
}
|
19
Ryujinx/Cpu/Memory/AMemoryMapInfo.cs
Normal file
19
Ryujinx/Cpu/Memory/AMemoryMapInfo.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
namespace ChocolArm64.Memory
|
||||
{
|
||||
public struct AMemoryMapInfo
|
||||
{
|
||||
public long Position { get; private set; }
|
||||
public long Size { get; private set; }
|
||||
public int Type { get; private set; }
|
||||
|
||||
public AMemoryPerm Perm { get; private set; }
|
||||
|
||||
public AMemoryMapInfo(long Position, long Size, int Type, AMemoryPerm Perm)
|
||||
{
|
||||
this.Position = Position;
|
||||
this.Size = Size;
|
||||
this.Type = Type;
|
||||
this.Perm = Perm;
|
||||
}
|
||||
}
|
||||
}
|
336
Ryujinx/Cpu/Memory/AMemoryMgr.cs
Normal file
336
Ryujinx/Cpu/Memory/AMemoryMgr.cs
Normal file
|
@ -0,0 +1,336 @@
|
|||
using ChocolArm64.Exceptions;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace ChocolArm64.Memory
|
||||
{
|
||||
public class AMemoryMgr
|
||||
{
|
||||
public const long AddrSize = 1L << 36;
|
||||
public const long RamSize = 2L * 1024 * 1024 * 1024;
|
||||
|
||||
private const int PTLvl0Bits = 11;
|
||||
private const int PTLvl1Bits = 13;
|
||||
private const int PTPageBits = 12;
|
||||
|
||||
private const int PTLvl0Size = 1 << PTLvl0Bits;
|
||||
private const int PTLvl1Size = 1 << PTLvl1Bits;
|
||||
public const int PageSize = 1 << PTPageBits;
|
||||
|
||||
private const int PTLvl0Mask = PTLvl0Size - 1;
|
||||
private const int PTLvl1Mask = PTLvl1Size - 1;
|
||||
public const int PageMask = PageSize - 1;
|
||||
|
||||
private const int PTLvl0Bit = PTPageBits + PTLvl0Bits;
|
||||
private const int PTLvl1Bit = PTPageBits;
|
||||
|
||||
private AMemoryAlloc Allocator;
|
||||
|
||||
private enum PTMap
|
||||
{
|
||||
Unmapped,
|
||||
Physical,
|
||||
Mirror
|
||||
}
|
||||
|
||||
private struct PTEntry
|
||||
{
|
||||
public long Position;
|
||||
public int Type;
|
||||
|
||||
public PTMap Map;
|
||||
public AMemoryPerm Perm;
|
||||
|
||||
public PTEntry(long Position, int Type, PTMap Map, AMemoryPerm Perm)
|
||||
{
|
||||
this.Position = Position;
|
||||
this.Type = Type;
|
||||
this.Map = Map;
|
||||
this.Perm = Perm;
|
||||
}
|
||||
}
|
||||
|
||||
private PTEntry[][] PageTable;
|
||||
|
||||
private bool IsHeapInitialized;
|
||||
|
||||
public long HeapAddr { get; private set; }
|
||||
public int HeapSize { get; private set; }
|
||||
|
||||
public AMemoryMgr(AMemoryAlloc Allocator)
|
||||
{
|
||||
this.Allocator = Allocator;
|
||||
|
||||
PageTable = new PTEntry[PTLvl0Size][];
|
||||
}
|
||||
|
||||
public long GetTotalMemorySize()
|
||||
{
|
||||
return Allocator.GetFreeMem() + GetUsedMemorySize();
|
||||
}
|
||||
|
||||
public long GetUsedMemorySize()
|
||||
{
|
||||
long Size = 0;
|
||||
|
||||
for (int L0 = 0; L0 < PageTable.Length; L0++)
|
||||
{
|
||||
if (PageTable[L0] == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int L1 = 0; L1 < PageTable[L0].Length; L1++)
|
||||
{
|
||||
Size += PageTable[L0][L1].Map != PTMap.Unmapped ? PageSize : 0;
|
||||
}
|
||||
}
|
||||
|
||||
return Size;
|
||||
}
|
||||
|
||||
public bool SetHeapAddr(long Position)
|
||||
{
|
||||
if (!IsHeapInitialized)
|
||||
{
|
||||
HeapAddr = Position;
|
||||
|
||||
IsHeapInitialized = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void SetHeapSize(int Size, int Type)
|
||||
{
|
||||
//TODO: Return error when theres no enough space to allocate heap.
|
||||
Size = (int)AMemoryHelper.PageRoundUp(Size);
|
||||
|
||||
long Position = HeapAddr;
|
||||
|
||||
if ((ulong)Size < (ulong)HeapSize)
|
||||
{
|
||||
//Try to free now free area if size is smaller than old size.
|
||||
Position += Size;
|
||||
|
||||
while ((ulong)Size < (ulong)HeapSize)
|
||||
{
|
||||
Allocator.Free(GetPhys(Position, AMemoryPerm.None));
|
||||
|
||||
Position += PageSize;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Allocate extra needed size.
|
||||
Position += HeapSize;
|
||||
Size -= HeapSize;
|
||||
|
||||
MapPhys(Position, Size, Type, AMemoryPerm.RW);
|
||||
}
|
||||
|
||||
HeapSize = Size;
|
||||
}
|
||||
|
||||
public bool MapPhys(long Src, long Dst, long Size, int Type, AMemoryPerm Perm)
|
||||
{
|
||||
Src = AMemoryHelper.PageRoundDown(Src);
|
||||
Dst = AMemoryHelper.PageRoundDown(Dst);
|
||||
|
||||
Size = AMemoryHelper.PageRoundUp(Size);
|
||||
|
||||
if (Dst < 0 || Dst + Size >= RamSize)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
long PagesCount = Size / PageSize;
|
||||
|
||||
while (PagesCount-- > 0)
|
||||
{
|
||||
SetPTEntry(Src, new PTEntry(Dst, Type, PTMap.Physical, Perm));
|
||||
|
||||
Src += PageSize;
|
||||
Dst += PageSize;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void MapPhys(long Position, long Size, int Type, AMemoryPerm Perm)
|
||||
{
|
||||
while (Size > 0)
|
||||
{
|
||||
if (!HasPTEntry(Position))
|
||||
{
|
||||
long PhysPos = Allocator.Alloc(PageSize);
|
||||
|
||||
SetPTEntry(Position, new PTEntry(PhysPos, Type, PTMap.Physical, Perm));
|
||||
}
|
||||
|
||||
long CPgSize = PageSize - (Position & PageMask);
|
||||
|
||||
Position += CPgSize;
|
||||
Size -= CPgSize;
|
||||
}
|
||||
}
|
||||
|
||||
public void MapMirror(long Src, long Dst, long Size, int Type)
|
||||
{
|
||||
Src = AMemoryHelper.PageRoundDown(Src);
|
||||
Dst = AMemoryHelper.PageRoundDown(Dst);
|
||||
|
||||
Size = AMemoryHelper.PageRoundUp(Size);
|
||||
|
||||
long PagesCount = Size / PageSize;
|
||||
|
||||
while (PagesCount-- > 0)
|
||||
{
|
||||
PTEntry Entry = GetPTEntry(Src);
|
||||
|
||||
Entry.Type = Type;
|
||||
Entry.Map = PTMap.Mirror;
|
||||
Entry.Position = Dst;
|
||||
|
||||
SetPTEntry(Src, Entry);
|
||||
|
||||
Src += PageSize;
|
||||
Dst += PageSize;
|
||||
}
|
||||
}
|
||||
|
||||
public void Reprotect(long Position, long Size, AMemoryPerm Perm)
|
||||
{
|
||||
Position = AMemoryHelper.PageRoundDown(Position);
|
||||
|
||||
Size = AMemoryHelper.PageRoundUp(Size);
|
||||
|
||||
long PagesCount = Size / PageSize;
|
||||
|
||||
while (PagesCount-- > 0)
|
||||
{
|
||||
PTEntry Entry = GetPTEntry(Position);
|
||||
|
||||
Entry.Perm = Perm;
|
||||
|
||||
SetPTEntry(Position, Entry);
|
||||
|
||||
Position += PageSize;
|
||||
}
|
||||
}
|
||||
|
||||
public AMemoryMapInfo GetMapInfo(long Position)
|
||||
{
|
||||
Position = AMemoryHelper.PageRoundDown(Position);
|
||||
|
||||
PTEntry BaseEntry = GetPTEntry(Position);
|
||||
|
||||
bool IsSameSegment(long Pos)
|
||||
{
|
||||
PTEntry Entry = GetPTEntry(Pos);
|
||||
|
||||
return Entry.Type == BaseEntry.Type &&
|
||||
Entry.Map == BaseEntry.Map &&
|
||||
Entry.Perm == BaseEntry.Perm;
|
||||
}
|
||||
|
||||
long Start = Position;
|
||||
long End = Position + PageSize;
|
||||
|
||||
while (Start > 0 && IsSameSegment(Start - PageSize))
|
||||
{
|
||||
Start -= PageSize;
|
||||
}
|
||||
|
||||
while (End < AddrSize && IsSameSegment(End))
|
||||
{
|
||||
End += PageSize;
|
||||
}
|
||||
|
||||
long Size = End - Start;
|
||||
|
||||
return new AMemoryMapInfo(Start, Size, BaseEntry.Type, BaseEntry.Perm);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public long GetPhys(long Position, AMemoryPerm Perm)
|
||||
{
|
||||
if (!HasPTEntry(Position))
|
||||
{
|
||||
if (Position < 0x08000000)
|
||||
{
|
||||
Console.WriteLine($"HACK: Ignoring bad access at {Position:x16}");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
throw new VmmPageFaultException(Position);
|
||||
}
|
||||
|
||||
PTEntry Entry = GetPTEntry(Position);
|
||||
|
||||
long AbsPos = Entry.Position + (Position & PageMask);
|
||||
|
||||
if (Entry.Map == PTMap.Mirror)
|
||||
{
|
||||
return GetPhys(AbsPos, Perm);
|
||||
}
|
||||
|
||||
if (Entry.Map == PTMap.Unmapped)
|
||||
{
|
||||
throw new VmmPageFaultException(Position);
|
||||
}
|
||||
|
||||
return AbsPos;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private bool HasPTEntry(long Position)
|
||||
{
|
||||
if (Position >> PTLvl0Bits + PTLvl1Bits + PTPageBits != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
|
||||
long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
|
||||
|
||||
if (PageTable[L0] == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return PageTable[L0][L1].Map != PTMap.Unmapped;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private PTEntry GetPTEntry(long Position)
|
||||
{
|
||||
long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
|
||||
long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
|
||||
|
||||
if (PageTable[L0] == null)
|
||||
{
|
||||
return default(PTEntry);
|
||||
}
|
||||
|
||||
return PageTable[L0][L1];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void SetPTEntry(long Position, PTEntry Entry)
|
||||
{
|
||||
long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
|
||||
long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
|
||||
|
||||
if (PageTable[L0] == null)
|
||||
{
|
||||
PageTable[L0] = new PTEntry[PTLvl1Size];
|
||||
}
|
||||
|
||||
PageTable[L0][L1] = Entry;
|
||||
}
|
||||
}
|
||||
}
|
15
Ryujinx/Cpu/Memory/AMemoryPerm.cs
Normal file
15
Ryujinx/Cpu/Memory/AMemoryPerm.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
|
||||
namespace ChocolArm64.Memory
|
||||
{
|
||||
[Flags]
|
||||
public enum AMemoryPerm
|
||||
{
|
||||
None = 0,
|
||||
Read = 1 << 0,
|
||||
Write = 1 << 1,
|
||||
Execute = 1 << 2,
|
||||
RW = Read | Write,
|
||||
RX = Read | Execute
|
||||
}
|
||||
}
|
8
Ryujinx/Cpu/State/ACoreType.cs
Normal file
8
Ryujinx/Cpu/State/ACoreType.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace ChocolArm64.State
|
||||
{
|
||||
public enum ACoreType
|
||||
{
|
||||
CortexA53,
|
||||
CortexA57
|
||||
}
|
||||
}
|
23
Ryujinx/Cpu/State/APState.cs
Normal file
23
Ryujinx/Cpu/State/APState.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
|
||||
namespace ChocolArm64.State
|
||||
{
|
||||
[Flags]
|
||||
public enum APState
|
||||
{
|
||||
VBit = 28,
|
||||
CBit = 29,
|
||||
ZBit = 30,
|
||||
NBit = 31,
|
||||
|
||||
V = 1 << VBit,
|
||||
C = 1 << CBit,
|
||||
Z = 1 << ZBit,
|
||||
N = 1 << NBit,
|
||||
|
||||
NZ = N | Z,
|
||||
CV = C | V,
|
||||
|
||||
NZCV = NZ | CV
|
||||
}
|
||||
}
|
142
Ryujinx/Cpu/State/ARegister.cs
Normal file
142
Ryujinx/Cpu/State/ARegister.cs
Normal file
|
@ -0,0 +1,142 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ChocolArm64.State
|
||||
{
|
||||
struct ARegister
|
||||
{
|
||||
public int Index;
|
||||
|
||||
public ARegisterType Type;
|
||||
|
||||
public ARegister(int Index, ARegisterType Type)
|
||||
{
|
||||
this.Index = Index;
|
||||
this.Type = Type;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (ushort)Index | ((ushort)Type << 16);
|
||||
}
|
||||
|
||||
public override bool Equals(object Obj)
|
||||
{
|
||||
return Obj is ARegister Reg &&
|
||||
Reg.Index == Index &&
|
||||
Reg.Type == Type;
|
||||
}
|
||||
|
||||
public FieldInfo GetField()
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case ARegisterType.Flag: return GetFieldFlag();
|
||||
case ARegisterType.Int: return GetFieldInt();
|
||||
case ARegisterType.Vector: return GetFieldVector();
|
||||
}
|
||||
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
private FieldInfo GetFieldFlag()
|
||||
{
|
||||
switch ((APState)Index)
|
||||
{
|
||||
case APState.VBit: return GetField(nameof(ARegisters.Overflow));
|
||||
case APState.CBit: return GetField(nameof(ARegisters.Carry));
|
||||
case APState.ZBit: return GetField(nameof(ARegisters.Zero));
|
||||
case APState.NBit: return GetField(nameof(ARegisters.Negative));
|
||||
}
|
||||
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
private FieldInfo GetFieldInt()
|
||||
{
|
||||
switch (Index)
|
||||
{
|
||||
case 0: return GetField(nameof(ARegisters.X0));
|
||||
case 1: return GetField(nameof(ARegisters.X1));
|
||||
case 2: return GetField(nameof(ARegisters.X2));
|
||||
case 3: return GetField(nameof(ARegisters.X3));
|
||||
case 4: return GetField(nameof(ARegisters.X4));
|
||||
case 5: return GetField(nameof(ARegisters.X5));
|
||||
case 6: return GetField(nameof(ARegisters.X6));
|
||||
case 7: return GetField(nameof(ARegisters.X7));
|
||||
case 8: return GetField(nameof(ARegisters.X8));
|
||||
case 9: return GetField(nameof(ARegisters.X9));
|
||||
case 10: return GetField(nameof(ARegisters.X10));
|
||||
case 11: return GetField(nameof(ARegisters.X11));
|
||||
case 12: return GetField(nameof(ARegisters.X12));
|
||||
case 13: return GetField(nameof(ARegisters.X13));
|
||||
case 14: return GetField(nameof(ARegisters.X14));
|
||||
case 15: return GetField(nameof(ARegisters.X15));
|
||||
case 16: return GetField(nameof(ARegisters.X16));
|
||||
case 17: return GetField(nameof(ARegisters.X17));
|
||||
case 18: return GetField(nameof(ARegisters.X18));
|
||||
case 19: return GetField(nameof(ARegisters.X19));
|
||||
case 20: return GetField(nameof(ARegisters.X20));
|
||||
case 21: return GetField(nameof(ARegisters.X21));
|
||||
case 22: return GetField(nameof(ARegisters.X22));
|
||||
case 23: return GetField(nameof(ARegisters.X23));
|
||||
case 24: return GetField(nameof(ARegisters.X24));
|
||||
case 25: return GetField(nameof(ARegisters.X25));
|
||||
case 26: return GetField(nameof(ARegisters.X26));
|
||||
case 27: return GetField(nameof(ARegisters.X27));
|
||||
case 28: return GetField(nameof(ARegisters.X28));
|
||||
case 29: return GetField(nameof(ARegisters.X29));
|
||||
case 30: return GetField(nameof(ARegisters.X30));
|
||||
case 31: return GetField(nameof(ARegisters.X31));
|
||||
}
|
||||
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
private FieldInfo GetFieldVector()
|
||||
{
|
||||
switch (Index)
|
||||
{
|
||||
case 0: return GetField(nameof(ARegisters.V0));
|
||||
case 1: return GetField(nameof(ARegisters.V1));
|
||||
case 2: return GetField(nameof(ARegisters.V2));
|
||||
case 3: return GetField(nameof(ARegisters.V3));
|
||||
case 4: return GetField(nameof(ARegisters.V4));
|
||||
case 5: return GetField(nameof(ARegisters.V5));
|
||||
case 6: return GetField(nameof(ARegisters.V6));
|
||||
case 7: return GetField(nameof(ARegisters.V7));
|
||||
case 8: return GetField(nameof(ARegisters.V8));
|
||||
case 9: return GetField(nameof(ARegisters.V9));
|
||||
case 10: return GetField(nameof(ARegisters.V10));
|
||||
case 11: return GetField(nameof(ARegisters.V11));
|
||||
case 12: return GetField(nameof(ARegisters.V12));
|
||||
case 13: return GetField(nameof(ARegisters.V13));
|
||||
case 14: return GetField(nameof(ARegisters.V14));
|
||||
case 15: return GetField(nameof(ARegisters.V15));
|
||||
case 16: return GetField(nameof(ARegisters.V16));
|
||||
case 17: return GetField(nameof(ARegisters.V17));
|
||||
case 18: return GetField(nameof(ARegisters.V18));
|
||||
case 19: return GetField(nameof(ARegisters.V19));
|
||||
case 20: return GetField(nameof(ARegisters.V20));
|
||||
case 21: return GetField(nameof(ARegisters.V21));
|
||||
case 22: return GetField(nameof(ARegisters.V22));
|
||||
case 23: return GetField(nameof(ARegisters.V23));
|
||||
case 24: return GetField(nameof(ARegisters.V24));
|
||||
case 25: return GetField(nameof(ARegisters.V25));
|
||||
case 26: return GetField(nameof(ARegisters.V26));
|
||||
case 27: return GetField(nameof(ARegisters.V27));
|
||||
case 28: return GetField(nameof(ARegisters.V28));
|
||||
case 29: return GetField(nameof(ARegisters.V29));
|
||||
case 30: return GetField(nameof(ARegisters.V30));
|
||||
case 31: return GetField(nameof(ARegisters.V31));
|
||||
}
|
||||
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
private FieldInfo GetField(string Name)
|
||||
{
|
||||
return typeof(ARegisters).GetField(Name);
|
||||
}
|
||||
}
|
||||
}
|
10
Ryujinx/Cpu/State/ARegisterSize.cs
Normal file
10
Ryujinx/Cpu/State/ARegisterSize.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace ChocolArm64.State
|
||||
{
|
||||
enum ARegisterSize
|
||||
{
|
||||
Int32,
|
||||
Int64,
|
||||
SIMD64,
|
||||
SIMD128
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue