1
0
Fork 0
mirror of https://github.com/Ryujinx/Ryujinx.git synced 2024-12-18 04:32:01 +00:00
This commit is contained in:
gdkchan 2018-02-04 20:08:20 -03:00
commit b7e1d9930d
230 changed files with 17548 additions and 0 deletions

63
.gitattributes vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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();
}
}
}

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

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

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

View file

@ -0,0 +1,10 @@
namespace ChocolArm64.Decoder
{
enum ADataOp
{
Adr = 0,
Arithmetic = 1,
Logical = 2,
BitField = 3
}
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

View file

@ -0,0 +1,10 @@
namespace ChocolArm64.Decoder
{
enum AShiftType
{
Lsl,
Lsr,
Asr,
Ror
}
}

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

View file

@ -0,0 +1,10 @@
namespace ChocolArm64.Decoder
{
interface IAOpCodeAlu : IAOpCode
{
int Rd { get; }
int Rn { get; }
ADataOp DataOp { get; }
}
}

View file

@ -0,0 +1,7 @@
namespace ChocolArm64.Decoder
{
interface IAOpCodeAluImm : IAOpCodeAlu
{
long Imm { get; }
}
}

View file

@ -0,0 +1,10 @@
namespace ChocolArm64.Decoder
{
interface IAOpCodeAluRs : IAOpCodeAlu
{
int Shift { get; }
int Rm { get; }
AShiftType ShiftType { get; }
}
}

View file

@ -0,0 +1,10 @@
namespace ChocolArm64.Decoder
{
interface IAOpCodeAluRx : IAOpCodeAlu
{
int Shift { get; }
int Rm { get; }
AIntType IntType { get; }
}
}

View file

@ -0,0 +1,7 @@
namespace ChocolArm64.Decoder
{
interface IAOpCodeCond : IAOpCode
{
ACond Cond { get; }
}
}

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

View file

@ -0,0 +1,7 @@
namespace ChocolArm64.Decoder
{
interface IAOpCodeSimd : IAOpCode
{
int Size { get; }
}
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

View file

@ -0,0 +1,6 @@
using ChocolArm64.Translation;
namespace ChocolArm64.Instruction
{
delegate void AInstEmitter(AILEmitterCtx Context);
}

File diff suppressed because it is too large Load diff

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

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

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

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

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

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

View file

@ -0,0 +1,8 @@
namespace ChocolArm64.State
{
public enum ACoreType
{
CortexA53,
CortexA57
}
}

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

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

View 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