commit b7e1d9930db6d80fcb1f7c5c6b0aa627e42e6595
Author: gdkchan <gab.dark.100@gmail.com>
Date:   Sun Feb 4 20:08:20 2018 -0300

    aloha

diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000000..1ff0c42304
--- /dev/null
+++ b/.gitattributes
@@ -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
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000..82d9719b59
--- /dev/null
+++ b/.gitignore
@@ -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
diff --git a/GLScreen.cs b/GLScreen.cs
new file mode 100644
index 0000000000..d757db8fce
--- /dev/null
+++ b/GLScreen.cs
@@ -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);
+        }
+    }
+}
\ No newline at end of file
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000000..00d2e135a7
--- /dev/null
+++ b/LICENSE.txt
@@ -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/>
\ No newline at end of file
diff --git a/Program.cs b/Program.cs
new file mode 100644
index 0000000000..44dc67c1fd
--- /dev/null
+++ b/Program.cs
@@ -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();
+        }
+    }
+}
diff --git a/Ryujinx.csproj b/Ryujinx.csproj
new file mode 100644
index 0000000000..2c25afc0ff
--- /dev/null
+++ b/Ryujinx.csproj
@@ -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>
\ No newline at end of file
diff --git a/Ryujinx.sln b/Ryujinx.sln
new file mode 100644
index 0000000000..c75364f712
--- /dev/null
+++ b/Ryujinx.sln
@@ -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
diff --git a/Ryujinx/Cpu/ABitUtils.cs b/Ryujinx/Cpu/ABitUtils.cs
new file mode 100644
index 0000000000..357dd45d15
--- /dev/null
+++ b/Ryujinx/Cpu/ABitUtils.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/AOpCodeTable.cs b/Ryujinx/Cpu/AOpCodeTable.cs
new file mode 100644
index 0000000000..d15d2e1230
--- /dev/null
+++ b/Ryujinx/Cpu/AOpCodeTable.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/AThread.cs b/Ryujinx/Cpu/AThread.cs
new file mode 100644
index 0000000000..2fafcdd53c
--- /dev/null
+++ b/Ryujinx/Cpu/AThread.cs
@@ -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();
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/ATranslatedSub.cs b/Ryujinx/Cpu/ATranslatedSub.cs
new file mode 100644
index 0000000000..1a821f479f
--- /dev/null
+++ b/Ryujinx/Cpu/ATranslatedSub.cs
@@ -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;
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/ATranslator.cs b/Ryujinx/Cpu/ATranslator.cs
new file mode 100644
index 0000000000..ba7f3df6fa
--- /dev/null
+++ b/Ryujinx/Cpu/ATranslator.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/ABlock.cs b/Ryujinx/Cpu/Decoder/ABlock.cs
new file mode 100644
index 0000000000..32974c1abf
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/ABlock.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/ACond.cs b/Ryujinx/Cpu/Decoder/ACond.cs
new file mode 100644
index 0000000000..f2da8bd29f
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/ACond.cs
@@ -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
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/ADataOp.cs b/Ryujinx/Cpu/Decoder/ADataOp.cs
new file mode 100644
index 0000000000..a5601a3abc
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/ADataOp.cs
@@ -0,0 +1,10 @@
+namespace ChocolArm64.Decoder
+{
+    enum ADataOp
+    {
+        Adr        = 0,
+        Arithmetic = 1,
+        Logical    = 2,
+        BitField   = 3
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/ADecoder.cs b/Ryujinx/Cpu/Decoder/ADecoder.cs
new file mode 100644
index 0000000000..1d34003965
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/ADecoder.cs
@@ -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);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/ADecoderHelper.cs b/Ryujinx/Cpu/Decoder/ADecoderHelper.cs
new file mode 100644
index 0000000000..a2179f49e3
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/ADecoderHelper.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AIntType.cs b/Ryujinx/Cpu/Decoder/AIntType.cs
new file mode 100644
index 0000000000..242fdada14
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AIntType.cs
@@ -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
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCode.cs b/Ryujinx/Cpu/Decoder/AOpCode.cs
new file mode 100644
index 0000000000..4e5a807007
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCode.cs
@@ -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();
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeAdr.cs b/Ryujinx/Cpu/Decoder/AOpCodeAdr.cs
new file mode 100644
index 0000000000..49f756e721
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeAdr.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeAlu.cs b/Ryujinx/Cpu/Decoder/AOpCodeAlu.cs
new file mode 100644
index 0000000000..981af800e5
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeAlu.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeAluImm.cs b/Ryujinx/Cpu/Decoder/AOpCodeAluImm.cs
new file mode 100644
index 0000000000..6b0adbc25e
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeAluImm.cs
@@ -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));
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeAluRs.cs b/Ryujinx/Cpu/Decoder/AOpCodeAluRs.cs
new file mode 100644
index 0000000000..8439df6f28
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeAluRs.cs
@@ -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
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeAluRx.cs b/Ryujinx/Cpu/Decoder/AOpCodeAluRx.cs
new file mode 100644
index 0000000000..7dd72a6842
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeAluRx.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeBImm.cs b/Ryujinx/Cpu/Decoder/AOpCodeBImm.cs
new file mode 100644
index 0000000000..2a56d4af49
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeBImm.cs
@@ -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) { }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeBImmAl.cs b/Ryujinx/Cpu/Decoder/AOpCodeBImmAl.cs
new file mode 100644
index 0000000000..1b6a98e7c8
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeBImmAl.cs
@@ -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);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeBImmCmp.cs b/Ryujinx/Cpu/Decoder/AOpCodeBImmCmp.cs
new file mode 100644
index 0000000000..e0ce57e386
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeBImmCmp.cs
@@ -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);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeBImmCond.cs b/Ryujinx/Cpu/Decoder/AOpCodeBImmCond.cs
new file mode 100644
index 0000000000..e4ae845fe0
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeBImmCond.cs
@@ -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);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeBImmTest.cs b/Ryujinx/Cpu/Decoder/AOpCodeBImmTest.cs
new file mode 100644
index 0000000000..6b8b966db8
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeBImmTest.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeBReg.cs b/Ryujinx/Cpu/Decoder/AOpCodeBReg.cs
new file mode 100644
index 0000000000..a71fc338ae
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeBReg.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeBfm.cs b/Ryujinx/Cpu/Decoder/AOpCodeBfm.cs
new file mode 100644
index 0000000000..6498d8ec69
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeBfm.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeCcmp.cs b/Ryujinx/Cpu/Decoder/AOpCodeCcmp.cs
new file mode 100644
index 0000000000..ab7aa75456
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeCcmp.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeCcmpImm.cs b/Ryujinx/Cpu/Decoder/AOpCodeCcmpImm.cs
new file mode 100644
index 0000000000..803eefc249
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeCcmpImm.cs
@@ -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) { }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeCcmpReg.cs b/Ryujinx/Cpu/Decoder/AOpCodeCcmpReg.cs
new file mode 100644
index 0000000000..c364ae68b4
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeCcmpReg.cs
@@ -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) { }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeCsel.cs b/Ryujinx/Cpu/Decoder/AOpCodeCsel.cs
new file mode 100644
index 0000000000..cdef3e745c
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeCsel.cs
@@ -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);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeException.cs b/Ryujinx/Cpu/Decoder/AOpCodeException.cs
new file mode 100644
index 0000000000..6d4a03861b
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeException.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeMem.cs b/Ryujinx/Cpu/Decoder/AOpCodeMem.cs
new file mode 100644
index 0000000000..1950b28677
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeMem.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeMemEx.cs b/Ryujinx/Cpu/Decoder/AOpCodeMemEx.cs
new file mode 100644
index 0000000000..3a28cfd73a
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeMemEx.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeMemImm.cs b/Ryujinx/Cpu/Decoder/AOpCodeMemImm.cs
new file mode 100644
index 0000000000..14edc51487
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeMemImm.cs
@@ -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;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeMemLit.cs b/Ryujinx/Cpu/Decoder/AOpCodeMemLit.cs
new file mode 100644
index 0000000000..b942943531
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeMemLit.cs
@@ -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;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeMemPair.cs b/Ryujinx/Cpu/Decoder/AOpCodeMemPair.cs
new file mode 100644
index 0000000000..ec866c84e5
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeMemPair.cs
@@ -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);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeMemReg.cs b/Ryujinx/Cpu/Decoder/AOpCodeMemReg.cs
new file mode 100644
index 0000000000..989271282f
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeMemReg.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeMov.cs b/Ryujinx/Cpu/Decoder/AOpCodeMov.cs
new file mode 100644
index 0000000000..3d1431fb1d
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeMov.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeMul.cs b/Ryujinx/Cpu/Decoder/AOpCodeMul.cs
new file mode 100644
index 0000000000..ca2b0cdb38
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeMul.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimd.cs b/Ryujinx/Cpu/Decoder/AOpCodeSimd.cs
new file mode 100644
index 0000000000..7961998451
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeSimd.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdCvt.cs b/Ryujinx/Cpu/Decoder/AOpCodeSimdCvt.cs
new file mode 100644
index 0000000000..41f4d3b143
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeSimdCvt.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdFcond.cs b/Ryujinx/Cpu/Decoder/AOpCodeSimdFcond.cs
new file mode 100644
index 0000000000..e38e742473
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeSimdFcond.cs
@@ -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);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdFmov.cs b/Ryujinx/Cpu/Decoder/AOpCodeSimdFmov.cs
new file mode 100644
index 0000000000..1047beffca
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeSimdFmov.cs
@@ -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);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdImm.cs b/Ryujinx/Cpu/Decoder/AOpCodeSimdImm.cs
new file mode 100644
index 0000000000..3a08ce63ad
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeSimdImm.cs
@@ -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));
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdIns.cs b/Ryujinx/Cpu/Decoder/AOpCodeSimdIns.cs
new file mode 100644
index 0000000000..0b60bbe837
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeSimdIns.cs
@@ -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);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdMemImm.cs b/Ryujinx/Cpu/Decoder/AOpCodeSimdMemImm.cs
new file mode 100644
index 0000000000..1ef19a5d67
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeSimdMemImm.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdMemLit.cs b/Ryujinx/Cpu/Decoder/AOpCodeSimdMemLit.cs
new file mode 100644
index 0000000000..cf6915f566
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeSimdMemLit.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdMemMult.cs b/Ryujinx/Cpu/Decoder/AOpCodeSimdMemMult.cs
new file mode 100644
index 0000000000..9731c7e745
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeSimdMemMult.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdMemPair.cs b/Ryujinx/Cpu/Decoder/AOpCodeSimdMemPair.cs
new file mode 100644
index 0000000000..db99e3d44d
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeSimdMemPair.cs
@@ -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);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdMemReg.cs b/Ryujinx/Cpu/Decoder/AOpCodeSimdMemReg.cs
new file mode 100644
index 0000000000..aabf484611
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeSimdMemReg.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdReg.cs b/Ryujinx/Cpu/Decoder/AOpCodeSimdReg.cs
new file mode 100644
index 0000000000..d3a8b76aae
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeSimdReg.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdRegElem.cs b/Ryujinx/Cpu/Decoder/AOpCodeSimdRegElem.cs
new file mode 100644
index 0000000000..828fe7884d
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeSimdRegElem.cs
@@ -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;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdShImm.cs b/Ryujinx/Cpu/Decoder/AOpCodeSimdShImm.cs
new file mode 100644
index 0000000000..a677e579b5
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeSimdShImm.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdTbl.cs b/Ryujinx/Cpu/Decoder/AOpCodeSimdTbl.cs
new file mode 100644
index 0000000000..c8ae5bac74
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeSimdTbl.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSystem.cs b/Ryujinx/Cpu/Decoder/AOpCodeSystem.cs
new file mode 100644
index 0000000000..95b291001d
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AOpCodeSystem.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/AShiftType.cs b/Ryujinx/Cpu/Decoder/AShiftType.cs
new file mode 100644
index 0000000000..34ceea2087
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/AShiftType.cs
@@ -0,0 +1,10 @@
+namespace ChocolArm64.Decoder
+{
+    enum AShiftType
+    {
+        Lsl,
+        Lsr,
+        Asr,
+        Ror
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/IAOpCode.cs b/Ryujinx/Cpu/Decoder/IAOpCode.cs
new file mode 100644
index 0000000000..44bf9cb2f1
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/IAOpCode.cs
@@ -0,0 +1,13 @@
+using ChocolArm64.Instruction;
+using ChocolArm64.State;
+
+namespace ChocolArm64.Decoder
+{
+    interface IAOpCode
+    {
+        long Position { get; }
+
+        AInstEmitter  Emitter      { get; }
+        ARegisterSize RegisterSize { get; }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/IAOpCodeAlu.cs b/Ryujinx/Cpu/Decoder/IAOpCodeAlu.cs
new file mode 100644
index 0000000000..22af4c82d6
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/IAOpCodeAlu.cs
@@ -0,0 +1,10 @@
+namespace ChocolArm64.Decoder
+{
+    interface IAOpCodeAlu : IAOpCode
+    {
+        int Rd { get; }
+        int Rn { get; }
+
+        ADataOp DataOp { get; }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/IAOpCodeAluImm.cs b/Ryujinx/Cpu/Decoder/IAOpCodeAluImm.cs
new file mode 100644
index 0000000000..04b5c5f7d1
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/IAOpCodeAluImm.cs
@@ -0,0 +1,7 @@
+namespace ChocolArm64.Decoder
+{
+    interface IAOpCodeAluImm : IAOpCodeAlu
+    {
+        long Imm { get; }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/IAOpCodeAluRs.cs b/Ryujinx/Cpu/Decoder/IAOpCodeAluRs.cs
new file mode 100644
index 0000000000..5ca9de4032
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/IAOpCodeAluRs.cs
@@ -0,0 +1,10 @@
+namespace ChocolArm64.Decoder
+{
+    interface IAOpCodeAluRs : IAOpCodeAlu
+    {
+        int Shift { get; }
+        int Rm    { get; }
+
+        AShiftType ShiftType { get; }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/IAOpCodeAluRx.cs b/Ryujinx/Cpu/Decoder/IAOpCodeAluRx.cs
new file mode 100644
index 0000000000..b49d5325a9
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/IAOpCodeAluRx.cs
@@ -0,0 +1,10 @@
+namespace ChocolArm64.Decoder
+{
+    interface IAOpCodeAluRx : IAOpCodeAlu
+    {
+        int Shift { get; }
+        int Rm    { get; }
+
+        AIntType IntType { get; }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/IAOpCodeCond.cs b/Ryujinx/Cpu/Decoder/IAOpCodeCond.cs
new file mode 100644
index 0000000000..1655abaac0
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/IAOpCodeCond.cs
@@ -0,0 +1,7 @@
+namespace ChocolArm64.Decoder
+{
+    interface IAOpCodeCond : IAOpCode
+    {
+        ACond Cond { get; }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/IAOpCodeLit.cs b/Ryujinx/Cpu/Decoder/IAOpCodeLit.cs
new file mode 100644
index 0000000000..0f5092d076
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/IAOpCodeLit.cs
@@ -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; }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Decoder/IAOpCodeSimd.cs b/Ryujinx/Cpu/Decoder/IAOpCodeSimd.cs
new file mode 100644
index 0000000000..19032ad940
--- /dev/null
+++ b/Ryujinx/Cpu/Decoder/IAOpCodeSimd.cs
@@ -0,0 +1,7 @@
+namespace ChocolArm64.Decoder
+{
+    interface IAOpCodeSimd : IAOpCode
+    {
+        int Size { get; }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Exceptions/VmmAccessViolationException.cs b/Ryujinx/Cpu/Exceptions/VmmAccessViolationException.cs
new file mode 100644
index 0000000000..dd9d7a646b
--- /dev/null
+++ b/Ryujinx/Cpu/Exceptions/VmmAccessViolationException.cs
@@ -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)) { }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Exceptions/VmmOutOfMemoryException.cs b/Ryujinx/Cpu/Exceptions/VmmOutOfMemoryException.cs
new file mode 100644
index 0000000000..c11384dae8
--- /dev/null
+++ b/Ryujinx/Cpu/Exceptions/VmmOutOfMemoryException.cs
@@ -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)) { }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Exceptions/VmmPageFaultException.cs b/Ryujinx/Cpu/Exceptions/VmmPageFaultException.cs
new file mode 100644
index 0000000000..d55c2c1ca9
--- /dev/null
+++ b/Ryujinx/Cpu/Exceptions/VmmPageFaultException.cs
@@ -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)) { }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Instruction/AInst.cs b/Ryujinx/Cpu/Instruction/AInst.cs
new file mode 100644
index 0000000000..cab597d656
--- /dev/null
+++ b/Ryujinx/Cpu/Instruction/AInst.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Instruction/AInstEmitAlu.cs b/Ryujinx/Cpu/Instruction/AInstEmitAlu.cs
new file mode 100644
index 0000000000..c09b186322
--- /dev/null
+++ b/Ryujinx/Cpu/Instruction/AInstEmitAlu.cs
@@ -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);
+            }
+        }        
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Instruction/AInstEmitAluHelper.cs b/Ryujinx/Cpu/Instruction/AInstEmitAluHelper.cs
new file mode 100644
index 0000000000..03355ebad7
--- /dev/null
+++ b/Ryujinx/Cpu/Instruction/AInstEmitAluHelper.cs
@@ -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);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Instruction/AInstEmitBfm.cs b/Ryujinx/Cpu/Instruction/AInstEmitBfm.cs
new file mode 100644
index 0000000000..4eff013d16
--- /dev/null
+++ b/Ryujinx/Cpu/Instruction/AInstEmitBfm.cs
@@ -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);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Instruction/AInstEmitCcmp.cs b/Ryujinx/Cpu/Instruction/AInstEmitCcmp.cs
new file mode 100644
index 0000000000..7153a6a0d1
--- /dev/null
+++ b/Ryujinx/Cpu/Instruction/AInstEmitCcmp.cs
@@ -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);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Instruction/AInstEmitCsel.cs b/Ryujinx/Cpu/Instruction/AInstEmitCsel.cs
new file mode 100644
index 0000000000..330809806a
--- /dev/null
+++ b/Ryujinx/Cpu/Instruction/AInstEmitCsel.cs
@@ -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);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Instruction/AInstEmitException.cs b/Ryujinx/Cpu/Instruction/AInstEmitException.cs
new file mode 100644
index 0000000000..6e5665fbca
--- /dev/null
+++ b/Ryujinx/Cpu/Instruction/AInstEmitException.cs
@@ -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"));
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Instruction/AInstEmitFlow.cs b/Ryujinx/Cpu/Instruction/AInstEmitFlow.cs
new file mode 100644
index 0000000000..6fa19c9fc0
--- /dev/null
+++ b/Ryujinx/Cpu/Instruction/AInstEmitFlow.cs
@@ -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));
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Instruction/AInstEmitMemory.cs b/Ryujinx/Cpu/Instruction/AInstEmitMemory.cs
new file mode 100644
index 0000000000..ca0c82a377
--- /dev/null
+++ b/Ryujinx/Cpu/Instruction/AInstEmitMemory.cs
@@ -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);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Instruction/AInstEmitMemoryEx.cs b/Ryujinx/Cpu/Instruction/AInstEmitMemoryEx.cs
new file mode 100644
index 0000000000..ebb6e93284
--- /dev/null
+++ b/Ryujinx/Cpu/Instruction/AInstEmitMemoryEx.cs
@@ -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));
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Instruction/AInstEmitMemoryHelper.cs b/Ryujinx/Cpu/Instruction/AInstEmitMemoryHelper.cs
new file mode 100644
index 0000000000..e05153d998
--- /dev/null
+++ b/Ryujinx/Cpu/Instruction/AInstEmitMemoryHelper.cs
@@ -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);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Instruction/AInstEmitMove.cs b/Ryujinx/Cpu/Instruction/AInstEmitMove.cs
new file mode 100644
index 0000000000..719b53d5d5
--- /dev/null
+++ b/Ryujinx/Cpu/Instruction/AInstEmitMove.cs
@@ -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);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Instruction/AInstEmitMul.cs b/Ryujinx/Cpu/Instruction/AInstEmitMul.cs
new file mode 100644
index 0000000000..3713c81f6c
--- /dev/null
+++ b/Ryujinx/Cpu/Instruction/AInstEmitMul.cs
@@ -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);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Instruction/AInstEmitScalar.cs b/Ryujinx/Cpu/Instruction/AInstEmitScalar.cs
new file mode 100644
index 0000000000..fcddb0c983
--- /dev/null
+++ b/Ryujinx/Cpu/Instruction/AInstEmitScalar.cs
@@ -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();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Instruction/AInstEmitSimd.cs b/Ryujinx/Cpu/Instruction/AInstEmitSimd.cs
new file mode 100644
index 0000000000..0801716ad2
--- /dev/null
+++ b/Ryujinx/Cpu/Instruction/AInstEmitSimd.cs
@@ -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);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Instruction/AInstEmitSystem.cs b/Ryujinx/Cpu/Instruction/AInstEmitSystem.cs
new file mode 100644
index 0000000000..23a0b6b2d3
--- /dev/null
+++ b/Ryujinx/Cpu/Instruction/AInstEmitSystem.cs
@@ -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;
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Instruction/AInstEmitter.cs b/Ryujinx/Cpu/Instruction/AInstEmitter.cs
new file mode 100644
index 0000000000..8712a7367c
--- /dev/null
+++ b/Ryujinx/Cpu/Instruction/AInstEmitter.cs
@@ -0,0 +1,6 @@
+using ChocolArm64.Translation;
+
+namespace ChocolArm64.Instruction
+{
+    delegate void AInstEmitter(AILEmitterCtx Context);
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Instruction/ASoftFallback.cs b/Ryujinx/Cpu/Instruction/ASoftFallback.cs
new file mode 100644
index 0000000000..9101231486
--- /dev/null
+++ b/Ryujinx/Cpu/Instruction/ASoftFallback.cs
@@ -0,0 +1,1207 @@
+using ChocolArm64.State;
+using ChocolArm64.Translation;
+using System;
+
+namespace ChocolArm64.Instruction
+{
+    static class ASoftFallback
+    {
+        public static void EmitCall(AILEmitterCtx Context, string Name64, string Name128)
+        {
+            bool IsSimd64 = Context.CurrOp.RegisterSize == ARegisterSize.SIMD64;
+
+            Context.EmitCall(typeof(ASoftFallback), IsSimd64 ? Name64 : Name128);
+        }
+
+        public static void EmitCall(AILEmitterCtx Context, string MthdName)
+        {
+            Context.EmitCall(typeof(ASoftFallback), MthdName);
+        }
+
+        public static uint  CountLeadingZeros32(uint Value)  => (uint)CountLeadingZeros(Value, 32);
+        public static ulong CountLeadingZeros64(ulong Value) => (ulong)CountLeadingZeros(Value, 64);
+
+        private static ulong CountLeadingZeros(ulong Value, int Size)
+        {
+            int HighBit = Size - 1;
+
+            for (int Bit = HighBit; Bit >= 0; Bit--)
+            {
+                if (((Value >> Bit) & 1) != 0)
+                {
+                    return (ulong)(HighBit - Bit);
+                }
+            }
+
+            return (ulong)Size;
+        }
+
+        public static uint ReverseBits32(uint Value)
+        {
+            Value = ((Value & 0xaaaaaaaa) >> 1) | ((Value & 0x55555555) << 1);
+            Value = ((Value & 0xcccccccc) >> 2) | ((Value & 0x33333333) << 2);
+            Value = ((Value & 0xf0f0f0f0) >> 4) | ((Value & 0x0f0f0f0f) << 4);
+            Value = ((Value & 0xff00ff00) >> 8) | ((Value & 0x00ff00ff) << 8);
+
+            return (Value >> 16) | (Value << 16);
+        }
+
+        public static ulong ReverseBits64(ulong Value)
+        {
+            Value = ((Value & 0xaaaaaaaaaaaaaaaa) >>  1) | ((Value & 0x5555555555555555) <<  1);
+            Value = ((Value & 0xcccccccccccccccc) >>  2) | ((Value & 0x3333333333333333) <<  2);
+            Value = ((Value & 0xf0f0f0f0f0f0f0f0) >>  4) | ((Value & 0x0f0f0f0f0f0f0f0f) <<  4);
+            Value = ((Value & 0xff00ff00ff00ff00) >>  8) | ((Value & 0x00ff00ff00ff00ff) <<  8);
+            Value = ((Value & 0xffff0000ffff0000) >> 16) | ((Value & 0x0000ffff0000ffff) << 16);           
+
+            return (Value >> 32) | (Value << 32);
+        }
+
+        public static uint ReverseBytes16_32(uint Value) => (uint)ReverseBytes16_64(Value);
+        public static uint ReverseBytes32_32(uint Value) => (uint)ReverseBytes32_64(Value);
+
+        public static ulong ReverseBytes16_64(ulong Value) => ReverseBytes(Value, RevSize.Rev16);
+        public static ulong ReverseBytes32_64(ulong Value) => ReverseBytes(Value, RevSize.Rev32);
+        public static ulong ReverseBytes64(ulong Value)    => ReverseBytes(Value, RevSize.Rev64);
+
+        private enum RevSize
+        {
+            Rev16,
+            Rev32,
+            Rev64
+        }
+
+        private static ulong ReverseBytes(ulong Value, RevSize Size)
+        {
+            Value = ((Value & 0xff00ff00ff00ff00) >>  8) | ((Value & 0x00ff00ff00ff00ff) <<  8);
+
+            if (Size == RevSize.Rev16)
+            {
+                return Value;
+            }
+
+            Value = ((Value & 0xffff0000ffff0000) >> 16) | ((Value & 0x0000ffff0000ffff) << 16);
+
+            if (Size == RevSize.Rev32)
+            {
+                return Value;
+            }
+
+            Value = ((Value & 0xffffffff00000000) >> 32) | ((Value & 0x00000000ffffffff) << 32);
+
+            if (Size == RevSize.Rev64)
+            {
+                return Value;
+            }
+
+            throw new ArgumentException(nameof(Size));
+        }
+
+        public static int SatDoubleToInt32(double Value, int FBits = 0)
+        {
+            if (FBits != 0) Value *= Math.Pow(2, FBits);
+
+            return Value > int.MaxValue ? int.MaxValue :
+                   Value < int.MinValue ? int.MinValue : (int)Value;
+        }
+
+        public static long SatDoubleToInt64(double Value, int FBits = 0)
+        {
+            if (FBits != 0) Value *= Math.Pow(2, FBits);
+
+            return Value > long.MaxValue ? long.MaxValue :
+                   Value < long.MinValue ? long.MinValue : (long)Value;
+        }
+
+        public static uint SatDoubleToUInt32(double Value, int FBits = 0)
+        {
+            if (FBits != 0) Value *= Math.Pow(2, FBits);
+
+            return Value > uint.MaxValue ? uint.MaxValue :
+                   Value < uint.MinValue ? uint.MinValue : (uint)Value;
+        }
+
+        public static ulong SatDoubleToUInt64(double Value, int FBits = 0)
+        {
+            if (FBits != 0) Value *= Math.Pow(2, FBits);
+
+            return Value > ulong.MaxValue ? ulong.MaxValue :
+                   Value < ulong.MinValue ? ulong.MinValue : (ulong)Value;
+        }
+
+        public static int SatSingleToInt32(float Value, int FBits = 0)
+        {
+            if (FBits != 0) Value *= MathF.Pow(2, FBits);
+
+            return Value > int.MaxValue ? int.MaxValue :
+                   Value < int.MinValue ? int.MinValue : (int)Value;
+        }
+
+        public static long SatSingleToInt64(float Value, int FBits = 0)
+        {
+            if (FBits != 0) Value *= MathF.Pow(2, FBits);
+
+            return Value > long.MaxValue ? long.MaxValue :
+                   Value < long.MinValue ? long.MinValue : (long)Value;
+        }
+
+        public static uint SatSingleToUInt32(float Value, int FBits = 0)
+        {
+            if (FBits != 0) Value *= MathF.Pow(2, FBits);
+
+            return Value > uint.MaxValue ? uint.MaxValue :
+                   Value < uint.MinValue ? uint.MinValue : (uint)Value;
+        }
+
+        public static ulong SatSingleToUInt64(float Value, int FBits = 0)
+        {
+            if (FBits != 0) Value *= MathF.Pow(2, FBits);
+
+            return Value > ulong.MaxValue ? ulong.MaxValue :
+                   Value < ulong.MinValue ? ulong.MinValue : (ulong)Value;
+        }
+
+        public static ulong SMulHi128(ulong LHS, ulong RHS)
+        {
+            long LLo = (uint)(LHS >>  0);
+            long LHi =  (int)(LHS >> 32);
+            long RLo = (uint)(RHS >>  0);
+            long RHi =  (int)(RHS >> 32);
+
+            long LHiRHi = LHi * RHi;
+            long LHiRLo = LHi * RLo;
+            long LLoRHi = LLo * RHi;
+            long LLoRLo = LLo * RLo;
+
+            long Carry = ((uint)LHiRLo + ((uint)LLoRHi + (LLoRLo >> 32))) >> 32;
+
+            long ResHi = LHiRHi + (LHiRLo >> 32) + (LLoRHi >> 32) + Carry;
+
+            return (ulong)ResHi;
+        }
+
+        public static ulong UMulHi128(ulong LHS, ulong RHS)
+        {
+            ulong LLo = (uint)(LHS >>  0);
+            ulong LHi = (uint)(LHS >> 32);
+            ulong RLo = (uint)(RHS >>  0);
+            ulong RHi = (uint)(RHS >> 32);
+
+            ulong LHiRHi = LHi * RHi;
+            ulong LHiRLo = LHi * RLo;
+            ulong LLoRHi = LLo * RHi;
+            ulong LLoRLo = LLo * RLo;
+
+            ulong Carry = ((uint)LHiRLo + ((uint)LLoRHi + (LLoRLo >> 32))) >> 32;
+
+            ulong ResHi = LHiRHi + (LHiRLo >> 32) + (LLoRHi >> 32) + Carry;
+
+            return ResHi;
+        }    
+
+        public static AVec Addp_S(AVec Vector, int Size)
+        {
+            ulong Low  = ExtractVec(Vector, 0, Size);
+            ulong High = ExtractVec(Vector, 1, Size);
+
+            return InsertVec(new AVec(), 0, Size, Low + High);
+        }
+
+        public static AVec Addp64(AVec LHS, AVec RHS, int Size)
+        {
+            return Addp(LHS, RHS, Size, 8);
+        }
+
+        public static AVec Addp128(AVec LHS, AVec RHS, int Size)
+        {
+            return Addp(LHS, RHS, Size, 16);
+        }
+
+        private static AVec Addp(AVec LHS, AVec RHS, int Size, int Bytes)
+        {
+            AVec Res = new AVec();
+
+            int Elems = Bytes >> Size;
+            int Half  = Elems >> 1;
+
+            for (int Index = 0; Index < Elems; Index++)
+            {
+                int Elem = (Index & (Half - 1)) << 1;
+
+                ulong L = Index < Half
+                    ? ExtractVec(LHS, Elem + 0, Size)
+                    : ExtractVec(RHS, Elem + 0, Size);
+                
+                ulong R = Index < Half
+                    ? ExtractVec(LHS, Elem + 1, Size)
+                    : ExtractVec(RHS, Elem + 1, Size);
+
+                Res = InsertVec(Res, Index, Size, L + R);
+            }
+
+            return Res;
+        }
+
+        public static AVec Bic_Vi64(AVec Res, ulong Imm, int Size)
+        {
+            return Bic_Vi(Res, Imm, Size, 8);
+        }
+
+        public static AVec Bic_Vi128(AVec Res, ulong Imm, int Size)
+        {
+            return Bic_Vi(Res, Imm, Size, 16);
+        }
+
+        private static AVec Bic_Vi(AVec Res, ulong Imm, int Size, int Bytes)
+        {
+            int Elems = Bytes >> Size;
+
+            for (int Index = 0; Index < Elems; Index++)
+            {
+                ulong Value = ExtractVec(Res, Index, Size);
+
+                Res = InsertVec(Res, Index, Size, Value & ~Imm);
+            }
+
+            return Res;
+        }
+
+        public static AVec Cnt64(AVec Vector)
+        {
+            AVec Res = new AVec();
+
+            Res.B0 = (byte)CountSetBits8(Vector.B0);
+            Res.B1 = (byte)CountSetBits8(Vector.B1);
+            Res.B2 = (byte)CountSetBits8(Vector.B2);
+            Res.B3 = (byte)CountSetBits8(Vector.B3);
+            Res.B4 = (byte)CountSetBits8(Vector.B4);
+            Res.B5 = (byte)CountSetBits8(Vector.B5);
+            Res.B6 = (byte)CountSetBits8(Vector.B6);
+            Res.B7 = (byte)CountSetBits8(Vector.B7);
+
+            return Res;
+        }
+
+        public static AVec Cnt128(AVec Vector)
+        {
+            AVec Res = new AVec();
+
+            Res.B0  = (byte)CountSetBits8(Vector.B0);
+            Res.B1  = (byte)CountSetBits8(Vector.B1);
+            Res.B2  = (byte)CountSetBits8(Vector.B2);
+            Res.B3  = (byte)CountSetBits8(Vector.B3);
+            Res.B4  = (byte)CountSetBits8(Vector.B4);
+            Res.B5  = (byte)CountSetBits8(Vector.B5);
+            Res.B6  = (byte)CountSetBits8(Vector.B6);
+            Res.B7  = (byte)CountSetBits8(Vector.B7);
+            Res.B8  = (byte)CountSetBits8(Vector.B8);
+            Res.B9  = (byte)CountSetBits8(Vector.B9);
+            Res.B10 = (byte)CountSetBits8(Vector.B10);
+            Res.B11 = (byte)CountSetBits8(Vector.B11);
+            Res.B12 = (byte)CountSetBits8(Vector.B12);
+            Res.B13 = (byte)CountSetBits8(Vector.B13);
+            Res.B14 = (byte)CountSetBits8(Vector.B14);
+            Res.B15 = (byte)CountSetBits8(Vector.B15);
+
+            return Res;
+        }
+
+        private static int CountSetBits8(byte Value)
+        {
+            return (Value >> 0) & 1 + (Value >> 1) & 1 +
+                   (Value >> 2) & 1 + (Value >> 3) & 1 +
+                   (Value >> 4) & 1 + (Value >> 5) & 1 +
+                   (Value >> 6) & 1 + (Value >> 7);
+        }
+
+        public static AVec Dup_Gp64(ulong Value, int Size)
+        {
+            return Dup_Gp(Value, Size, 8);
+        }
+
+        public static AVec Dup_Gp128(ulong Value, int Size)
+        {
+            return Dup_Gp(Value, Size, 16);
+        }
+
+        private static AVec Dup_Gp(ulong Value, int Size, int Bytes)
+        {
+            AVec Res = new AVec();
+
+            for (int Index = 0; Index < (Bytes >> Size); Index++)
+            {
+                Res = InsertVec(Res, Index, Size, Value);
+            }
+
+            return Res;
+        }
+
+        public static AVec Dup_S(AVec Vector, int Elem, int Size)
+        {
+            return InsertVec(new AVec(), 0, Size, ExtractVec(Vector, Elem, Size));
+        }
+
+        public static AVec Dup_V64(AVec Vector, int Elem, int Size)
+        {
+            return Dup_V(Vector, Elem, Size, 8);
+        }
+
+        public static AVec Dup_V128(AVec Vector, int Elem, int Size)
+        {
+            return Dup_V(Vector, Elem, Size, 16);
+        }
+
+        private static AVec Dup_V(AVec Vector, int Elem, int Size, int Bytes)
+        {
+            AVec Res = new AVec();
+
+            ulong Value = ExtractVec(Vector, Elem, Size);
+
+            for (Elem = 0; Elem < (Bytes >> Size); Elem++)
+            {
+                Res = InsertVec(Res, Elem, Size, Value);
+            }
+
+            return Res;
+        }
+
+        public static AVec Fadd64(AVec LHS, AVec RHS, int Size)
+        {
+            return Fadd(LHS, RHS, Size, 2);
+        }
+
+        public static AVec Fadd128(AVec LHS, AVec RHS, int Size)
+        {
+            return Fadd(LHS, RHS, Size, 4);
+        }
+
+        private static AVec Fadd(AVec LHS, AVec RHS, int Size, int Bytes)
+        {
+            AVec Res = new AVec();
+
+            int Elems = Bytes >> Size;
+
+            if (Size == 0)
+            {
+                for (int Index = 0; Index < Elems; Index++)
+                {
+                    float L = LHS.ExtractSingle(Index);
+                    float R = RHS.ExtractSingle(Index);
+
+                    Res = AVec.InsertSingle(Res, Index, L + R);
+                }
+            }
+            else
+            {
+                for (int Index = 0; Index < Elems; Index++)
+                {
+                    double L = LHS.ExtractDouble(Index);
+                    double R = RHS.ExtractDouble(Index);
+
+                    Res = AVec.InsertDouble(Res, Index, L + R);
+                }
+            }
+
+            return Res;
+        }
+
+        public static AVec Fcvtzs_V64(AVec Vector, int Size)
+        {
+            return Fcvtzs_V(Vector, Size, 2);
+        }
+
+        public static AVec Fcvtzs_V128(AVec Vector, int Size)
+        {
+            return Fcvtzs_V(Vector, Size, 4);
+        }
+
+        private static AVec Fcvtzs_V(AVec Vector, int Size, int Bytes)
+        {
+            AVec Res = new AVec();
+
+            int Elems = Bytes >> Size;
+
+            if (Size == 0)
+            {
+                for (int Index = 0; Index < Elems; Index++)
+                {
+                    float Value = Vector.ExtractSingle(Index);
+
+                    Res = InsertSVec(Res, Index, Size + 2, SatSingleToInt32(Value));
+                }
+            }
+            else
+            {
+                for (int Index = 0; Index < Elems; Index++)
+                {
+                    double Value = Vector.ExtractDouble(Index);
+
+                    Res = InsertSVec(Res, Index, Size + 2, SatDoubleToInt64(Value));
+                }
+            }
+
+            return Res;
+        }
+
+        public static AVec Fcvtzu_V_64(AVec Vector, int FBits, int Size)
+        {
+            return Fcvtzu_V(Vector, FBits, Size, 2);
+        }
+
+        public static AVec Fcvtzu_V_128(AVec Vector, int FBits, int Size)
+        {
+            return Fcvtzu_V(Vector, FBits, Size, 4);
+        }
+
+        private static AVec Fcvtzu_V(AVec Vector, int FBits, int Size, int Bytes)
+        {
+            AVec Res = new AVec();
+
+            int Elems = Bytes >> Size;
+
+            if (Size == 0)
+            {
+                for (int Index = 0; Index < Elems; Index++)
+                {
+                    float Value = Vector.ExtractSingle(Index);
+
+                    Res = InsertVec(Res, Index, Size + 2, SatSingleToUInt32(Value, FBits));
+                }
+            }
+            else
+            {
+                for (int Index = 0; Index < Elems; Index++)
+                {
+                    double Value = Vector.ExtractDouble(Index);
+
+                    Res = InsertVec(Res, Index, Size + 2, SatDoubleToUInt64(Value, FBits));
+                }
+            }
+
+            return Res;
+        }
+
+        public static AVec Fmla64(AVec Res, AVec LHS, AVec RHS, int Size)
+        {
+            return Fmla(Res, LHS, RHS, Size, 2);
+        }
+
+        public static AVec Fmla128(AVec Res, AVec LHS, AVec RHS, int Size)
+        {
+            return Fmla(Res, LHS, RHS, Size, 4);
+        }
+
+        private static AVec Fmla(AVec Res, AVec LHS, AVec RHS, int Size, int Bytes)
+        {
+            int Elems = Bytes >> Size;
+
+            if (Size == 0)
+            {
+                for (int Index = 0; Index < Elems; Index++)
+                {
+                    float L      = LHS.ExtractSingle(Index);
+                    float R      = RHS.ExtractSingle(Index);
+                    float Addend = Res.ExtractSingle(Index);
+
+                    Res = AVec.InsertSingle(Res, Index, Addend + L * R);
+                }
+            }
+            else
+            {
+                for (int Index = 0; Index < Elems; Index++)
+                {
+                    double L      = LHS.ExtractDouble(Index);
+                    double R      = RHS.ExtractDouble(Index);
+                    double Addend = Res.ExtractDouble(Index);
+
+                    Res = AVec.InsertDouble(Res, Index, Addend + L * R);
+                }
+            }
+
+            return Res;
+        }
+
+        public static AVec Fmla_Ve64(AVec Res, AVec LHS, AVec RHS, int SIdx, int Size)
+        {
+            return Fmla_Ve(Res, LHS, RHS, SIdx, Size, 2);
+        }
+
+        public static AVec Fmla_Ve128(AVec Res, AVec LHS, AVec RHS, int SIdx, int Size)
+        {
+            return Fmla_Ve(Res, LHS, RHS, SIdx, Size, 4);
+        }
+
+        private static AVec Fmla_Ve(AVec Res, AVec LHS, AVec RHS, int SIdx, int Size, int Bytes)
+        {
+            int Elems = Bytes >> Size;
+
+            if (Size == 0)
+            {
+                float R = RHS.ExtractSingle(SIdx);
+
+                for (int Index = 0; Index < Elems; Index++)
+                {
+                    float L      = LHS.ExtractSingle(Index);
+                    float Addend = Res.ExtractSingle(Index);
+
+                    Res = AVec.InsertSingle(Res, Index, Addend + L * R);
+                }
+            }
+            else
+            {
+                double R = RHS.ExtractDouble(SIdx);
+
+                for (int Index = 0; Index < Elems; Index++)
+                {
+                    double L      = LHS.ExtractDouble(Index);
+                    double Addend = Res.ExtractDouble(Index);
+
+                    Res = AVec.InsertDouble(Res, Index, Addend + L * R);
+                }
+            }
+
+            return Res;
+        }
+
+        public static AVec Fmov_S(ulong Value, int Elem, int Size)
+        {
+            return InsertVec(new AVec(), Elem, Size, Value);
+        }
+
+        public static AVec Fmul64(AVec LHS, AVec RHS, int Size)
+        {
+            return Fmul(LHS, RHS, Size, 2);
+        }
+
+        public static AVec Fmul128(AVec LHS, AVec RHS, int Size)
+        {
+            return Fmul(LHS, RHS, Size, 4);
+        }
+
+        private static AVec Fmul(AVec LHS, AVec RHS, int Size, int Bytes)
+        {
+            AVec Res = new AVec();
+
+            int Elems = Bytes >> Size;
+
+            if (Size == 0)
+            {
+                for (int Index = 0; Index < Elems; Index++)
+                {
+                    float L = LHS.ExtractSingle(Index);
+                    float R = RHS.ExtractSingle(Index);
+
+                    Res = AVec.InsertSingle(Res, Index, L * R);
+                }
+            }
+            else
+            {
+                for (int Index = 0; Index < Elems; Index++)
+                {
+                    double L = LHS.ExtractDouble(Index);
+                    double R = RHS.ExtractDouble(Index);
+
+                    Res = AVec.InsertDouble(Res, Index, L * R);
+                }
+            }
+
+            return Res;
+        }
+
+        public static AVec Fmul_Ve64(AVec LHS, AVec RHS, int SIdx, int Size)
+        {
+            return Fmul_Ve(LHS, RHS, SIdx, Size, 2);
+        }
+
+        public static AVec Fmul_Ve128(AVec LHS, AVec RHS, int SIdx, int Size)
+        {
+            return Fmul_Ve(LHS, RHS, SIdx, Size, 4);
+        }
+
+        private static AVec Fmul_Ve(AVec LHS, AVec RHS, int SIdx, int Size, int Bytes)
+        {
+            AVec Res = new AVec();
+
+            int Elems = Bytes >> Size;
+
+            if (Size == 0)
+            {
+                float R = RHS.ExtractSingle(SIdx);
+
+                for (int Index = 0; Index < Elems; Index++)
+                {
+                    float L = LHS.ExtractSingle(Index);
+
+                    Res = AVec.InsertSingle(Res, Index, L * R);
+                }
+            }
+            else
+            {
+                double R = RHS.ExtractDouble(SIdx);
+
+                for (int Index = 0; Index < Elems; Index++)
+                {
+                    double L = LHS.ExtractDouble(Index);
+
+                    Res = AVec.InsertDouble(Res, Index, L * R);
+                }
+            }
+
+            return Res;
+        }
+
+        public static AVec Fsub64(AVec LHS, AVec RHS, int Size)
+        {
+            return Fsub(LHS, RHS, Size, 2);
+        }
+
+        public static AVec Fsub128(AVec LHS, AVec RHS, int Size)
+        {
+            return Fsub(LHS, RHS, Size, 4);
+        }
+
+        private static AVec Fsub(AVec LHS, AVec RHS, int Size, int Bytes)
+        {
+            AVec Res = new AVec();
+
+            int Elems = Bytes >> Size;
+
+            if (Size == 0)
+            {
+                for (int Index = 0; Index < Elems; Index++)
+                {
+                    float L = LHS.ExtractSingle(Index);
+                    float R = RHS.ExtractSingle(Index);
+
+                    Res = AVec.InsertSingle(Res, Index, L - R);
+                }
+            }
+            else
+            {
+                for (int Index = 0; Index < Elems; Index++)
+                {
+                    double L = LHS.ExtractDouble(Index);
+                    double R = RHS.ExtractDouble(Index);
+
+                    Res = AVec.InsertDouble(Res, Index, L - R);
+                }
+            }
+
+            return Res;
+        }
+
+        public static AVec Ins_Gp(AVec Res, ulong Value, int Elem, int Size)
+        {
+            return InsertVec(Res, Elem, Size, Value);
+        }
+
+        public static AVec Ins_V(AVec Res, AVec Value, int Src, int Dst, int Size)
+        {
+            return InsertVec(Res, Dst, Size, ExtractVec(Value, Src, Size));;
+        }
+
+        public static AVec Orr_Vi64(AVec Res, ulong Imm, int Size)
+        {
+            return Orr_Vi(Res, Imm, Size, 8);
+        }
+
+        public static AVec Orr_Vi128(AVec Res, ulong Imm, int Size)
+        {
+            return Orr_Vi(Res, Imm, Size, 16);
+        }
+
+        private static AVec Orr_Vi(AVec Res, ulong Imm, int Size, int Bytes)
+        {
+            int Elems = Bytes >> Size;
+
+            for (int Index = 0; Index < Elems; Index++)
+            {
+                ulong Value = ExtractVec(Res, Index, Size);
+
+                Res = InsertVec(Res, Index, Size, Value | Imm);
+            }
+
+            return Res;
+        }
+
+        public static AVec Saddw(AVec LHS, AVec RHS, int Size)
+        {
+            return Saddw_(LHS, RHS, Size, false);
+        }
+
+        public static AVec Saddw2(AVec LHS, AVec RHS, int Size)
+        {
+            return Saddw_(LHS, RHS, Size, true);
+        }
+
+        private static AVec Saddw_(AVec LHS, AVec RHS, int Size, bool High)
+        {
+            AVec Res = new AVec();
+
+            int Elems = 8 >> Size;
+            int Part  = High ? Elems : 0;
+
+            for (int Index = 0; Index < Elems; Index++)
+            {
+                long L = ExtractSVec(LHS, Index,        Size + 1);
+                long R = ExtractSVec(RHS, Index + Part, Size);
+
+                Res = InsertSVec(Res, Index, Size + 1, L + R);
+            }
+
+            return Res;
+        }
+
+        public static AVec Scvtf_V64(AVec Vector, int Size)
+        {
+            return Scvtf_V(Vector, Size, 2);
+        }
+
+        public static AVec Scvtf_V128(AVec Vector, int Size)
+        {
+            return Scvtf_V(Vector, Size, 4);
+        }
+
+        private static AVec Scvtf_V(AVec Vector, int Size, int Bytes)
+        {
+            AVec Res = new AVec();
+
+            int Elems = Bytes >> Size;
+
+            if (Size == 0)
+            {
+                for (int Index = 0; Index < Elems; Index++)
+                {
+                    int Value = (int)ExtractSVec(Vector, Index, Size + 2);
+
+                    Res = AVec.InsertSingle(Res, Index, Value);
+                }
+            }
+            else
+            {
+                for (int Index = 0; Index < Elems; Index++)
+                {
+                    long Value = ExtractSVec(Vector, Index, Size + 2);
+
+                    Res = AVec.InsertDouble(Res, Index, Value);
+                }
+            }
+
+            return Res;
+        }
+
+        public static AVec Shl64(AVec Vector, int Shift, int Size)
+        {
+            return Shl(Vector, Shift, Size, 8);
+        }
+
+        public static AVec Shl128(AVec Vector, int Shift, int Size)
+        {
+            return Shl(Vector, Shift, Size, 16);
+        }
+
+        private static AVec Shl(AVec Vector, int Shift, int Size, int Bytes)
+        {
+            AVec Res = new AVec();
+
+            int Elems = Bytes >> Size;
+
+            for (int Index = 0; Index < Elems; Index++)
+            {
+                ulong Value = ExtractVec(Vector, Index, Size);
+
+                Res = InsertVec(Res, Index, Size, Value << Shift);
+            }
+
+            return Res;
+        }
+
+        public static AVec Sshll(AVec Vector, int Shift, int Size)
+        {
+            return Sshll_(Vector, Shift, Size, false);
+        }
+
+        public static AVec Sshll2(AVec Vector, int Shift, int Size)
+        {
+            return Sshll_(Vector, Shift, Size, true);
+        }
+
+        private static AVec Sshll_(AVec Vector, int Shift, int Size, bool High)
+        {
+            AVec Res = new AVec();
+
+            int Elems = 8 >> Size;
+            int Part  = High ? Elems : 0;
+
+            for (int Index = 0; Index < Elems; Index++)
+            {
+                long Value = ExtractSVec(Vector, Index + Part, Size);
+
+                Res = InsertSVec(Res, Index, Size + 1, Value << Shift);
+            }
+
+            return Res;
+        }
+
+        public static AVec Sshr64(AVec Vector, int Shift, int Size)
+        {
+            return Sshr(Vector, Shift, Size, 8);
+        }
+
+        public static AVec Sshr128(AVec Vector, int Shift, int Size)
+        {
+            return Sshr(Vector, Shift, Size, 16);
+        }
+
+        private static AVec Sshr(AVec Vector, int Shift, int Size, int Bytes)
+        {
+            AVec Res = new AVec();
+
+            int Elems = Bytes >> Size;
+
+            for (int Index = 0; Index < Elems; Index++)
+            {
+                long Value = ExtractSVec(Vector, Index, Size);
+
+                Res = InsertSVec(Res, Index, Size, Value >> Shift);
+            }
+
+            return Res;
+        }
+
+        public static AVec Tbl1_V64(AVec Vector, AVec Tb0)
+        {
+            return Tbl(Vector, 8, Tb0);
+        }
+
+        public static AVec Tbl1_V128(AVec Vector, AVec Tb0)
+        {
+            return Tbl(Vector, 16, Tb0);
+        }
+
+        public static AVec Tbl2_V64(AVec Vector, AVec Tb0, AVec Tb1)
+        {
+            return Tbl(Vector, 8, Tb0, Tb1);
+        }
+
+        public static AVec Tbl2_V128(AVec Vector, AVec Tb0, AVec Tb1)
+        {
+            return Tbl(Vector, 16, Tb0, Tb1);
+        }
+
+        public static AVec Tbl3_V64(AVec Vector, AVec Tb0, AVec Tb1, AVec Tb2)
+        {
+            return Tbl(Vector, 8, Tb0, Tb1, Tb2);
+        }
+
+        public static AVec Tbl3_V128(AVec Vector, AVec Tb0, AVec Tb1, AVec Tb2)
+        {
+            return Tbl(Vector, 16, Tb0, Tb1, Tb2);
+        }
+
+        public static AVec Tbl4_V64(AVec Vector, AVec Tb0, AVec Tb1, AVec Tb2, AVec Tb3)
+        {
+            return Tbl(Vector, 8, Tb0, Tb1, Tb2, Tb3);
+        }
+
+        public static AVec Tbl4_V128(AVec Vector, AVec Tb0, AVec Tb1, AVec Tb2, AVec Tb3)
+        {
+            return Tbl(Vector, 16, Tb0, Tb1, Tb2, Tb3);
+        }
+
+        private static AVec Tbl(AVec Vector, int Bytes, params AVec[] Tb)
+        {
+            AVec Res = new AVec();
+
+            byte[] Table = new byte[Tb.Length * 16];
+
+            for (int Index  = 0; Index  < Tb.Length; Index++)
+            for (int Index2 = 0; Index2 < 16;        Index2++)
+            {
+                Table[Index * 16 + Index2] = (byte)ExtractVec(Tb[Index], Index2, 0);
+            }
+
+            for (int Index = 0; Index < Bytes; Index++)
+            {
+                byte TblIdx = (byte)ExtractVec(Vector, Index, 0);
+
+                if (TblIdx < Table.Length)
+                {
+                    Res = InsertVec(Res, Index, 0, Table[TblIdx]);
+                }
+            }
+
+            return Res;
+        }
+
+        public static AVec Uaddlv64(AVec Vector, int Size)
+        {
+            return Uaddlv(Vector, Size, 8);
+        }
+
+        public static AVec Uaddlv128(AVec Vector, int Size)
+        {
+            return Uaddlv(Vector, Size, 16);
+        }
+
+        private static AVec Uaddlv(AVec Vector, int Size, int Bytes)
+        {
+            int Elems = Bytes >> Size;
+
+            ulong Sum = 0;
+
+            for (int Index = 0; Index < Elems; Index++)
+            {
+                Sum += ExtractVec(Vector, Index, Size);
+            }
+
+            return InsertVec(new AVec(), 0, 3, Sum);
+        }
+
+        public static AVec Uaddw(AVec LHS, AVec RHS, int Size)
+        {
+            return Uaddw_(LHS, RHS, Size, false);
+        }
+
+        public static AVec Uaddw2(AVec LHS, AVec RHS, int Size)
+        {
+            return Uaddw_(LHS, RHS, Size, true);
+        }
+
+        private static AVec Uaddw_(AVec LHS, AVec RHS, int Size, bool High)
+        {
+            AVec Res = new AVec();
+
+            int Elems = 8 >> Size;
+            int Part  = High ? Elems : 0;
+
+            for (int Index = 0; Index < Elems; Index++)
+            {
+                ulong L = ExtractVec(LHS, Index,        Size + 1);
+                ulong R = ExtractVec(RHS, Index + Part, Size);
+
+                Res = InsertVec(Res, Index, Size + 1, L + R);
+            }
+
+            return Res;
+        }
+
+        public static AVec Ucvtf_V_F(AVec Vector)
+        {
+            return new AVec()
+            {
+                S0 = (uint)Vector.W0,
+                S1 = (uint)Vector.W1,
+                S2 = (uint)Vector.W2,
+                S3 = (uint)Vector.W3
+            };
+        }
+
+        public static AVec Ucvtf_V_D(AVec Vector)
+        {
+            return new AVec()
+            {
+                D0 = (ulong)Vector.X0,
+                D1 = (ulong)Vector.X1
+            };
+        }
+
+        public static AVec Ushll(AVec Vector, int Shift, int Size)
+        {
+            return Ushll_(Vector, Shift, Size, false);
+        }
+
+        public static AVec Ushll2(AVec Vector, int Shift, int Size)
+        {
+            return Ushll_(Vector, Shift, Size, true);
+        }
+
+        private static AVec Ushll_(AVec Vector, int Shift, int Size, bool High)
+        {
+            AVec Res = new AVec();
+
+            int Elems = 8 >> Size;
+            int Part  = High ? Elems : 0;
+
+            for (int Index = 0; Index < Elems; Index++)
+            {
+                ulong Value = ExtractVec(Vector, Index + Part, Size);
+
+                Res = InsertVec(Res, Index, Size + 1, Value << Shift);
+            }
+
+            return Res;
+        }
+
+        public static AVec Ushr64(AVec Vector, int Shift, int Size)
+        {
+            return Ushr(Vector, Shift, Size, 8);
+        }
+
+        public static AVec Ushr128(AVec Vector, int Shift, int Size)
+        {
+            return Ushr(Vector, Shift, Size, 16);
+        }
+
+        private static AVec Ushr(AVec Vector, int Shift, int Size, int Bytes)
+        {
+            AVec Res = new AVec();
+
+            int Elems = Bytes >> Size;
+
+            for (int Index = 0; Index < Elems; Index++)
+            {
+                ulong Value = ExtractVec(Vector, Index, Size);
+
+                Res = InsertVec(Res, Index, Size, Value >> Shift);
+            }
+
+            return Res;
+        }
+
+        public static AVec Usra64(AVec Res, AVec Vector, int Shift, int Size)
+        {
+            return Usra(Res, Vector, Shift, Size, 8);
+        }
+
+        public static AVec Usra128(AVec Res, AVec Vector, int Shift, int Size)
+        {
+            return Usra(Res, Vector, Shift, Size, 16);
+        }
+
+        private static AVec Usra(AVec Res, AVec Vector, int Shift, int Size, int Bytes)
+        {
+            int Elems = Bytes >> Size;
+
+            for (int Index = 0; Index < Elems; Index++)
+            {
+                ulong Value  = ExtractVec(Vector, Index, Size);
+                ulong Addend = ExtractVec(Res,    Index, Size);
+
+                Res = InsertVec(Res, Index, Size, Addend + (Value >> Shift));
+            }
+
+            return Res;
+        }
+
+        public static AVec Uzp1_V64(AVec LHS, AVec RHS, int Size)
+        {
+            return Uzp(LHS, RHS, Size, 0, 8);
+        }
+
+        public static AVec Uzp1_V128(AVec LHS, AVec RHS, int Size)
+        {
+            return Uzp(LHS, RHS, Size, 0, 16);
+        }
+
+        public static AVec Uzp2_V64(AVec LHS, AVec RHS, int Size)
+        {
+            return Uzp(LHS, RHS, Size, 1, 8);
+        }
+
+        public static AVec Uzp2_V128(AVec LHS, AVec RHS, int Size)
+        {
+            return Uzp(LHS, RHS, Size, 1, 16);
+        }
+
+        private static AVec Uzp(AVec LHS, AVec RHS, int Size, int Part, int Bytes)
+        {
+            AVec Res = new AVec();
+
+            int Elems = Bytes >> Size;
+            int Half  = Elems >> 1;
+
+            for (int Index = 0; Index < Elems; Index++)
+            {
+                int Elem = (Index & (Half - 1)) << 1;
+
+                ulong Value = Index < Half
+                    ? ExtractVec(LHS, Elem + Part, Size)
+                    : ExtractVec(RHS, Elem + Part, Size);
+ 
+                Res = InsertVec(Res, Index, Size, Value);
+            }
+
+            return Res;
+        }
+
+        public static AVec Xtn(AVec Vector, int Size)
+        {
+            return Xtn_(Vector, Size, false);
+        }
+
+        public static AVec Xtn2(AVec Vector, int Size)
+        {
+            return Xtn_(Vector, Size, true);
+        }
+
+        private static AVec Xtn_(AVec Vector, int Size, bool High)
+        {
+            AVec Res = new AVec();
+
+            int Elems = 8 >> Size;
+            int Part  = High ? Elems : 0;
+
+            for (int Index = 0; Index < Elems; Index++)
+            {
+                ulong Value = ExtractVec(Vector, Index, Size + 1);
+
+                Res = InsertVec(Res, Index + Part, Size, Value);
+            }
+
+            return Res;
+        }
+
+        public static ulong ExtractVec(AVec Vector, int Index, int Size)
+        {
+            switch (Size)
+            {
+                case 0: return Vector.ExtractByte(Index);
+                case 1: return Vector.ExtractUInt16(Index);
+                case 2: return Vector.ExtractUInt32(Index);
+                case 3: return Vector.ExtractUInt64(Index);
+            }
+
+            throw new ArgumentOutOfRangeException(nameof(Size));
+        }
+
+        public static long ExtractSVec(AVec Vector, int Index, int Size)
+        {
+            switch (Size)
+            {
+                case 0: return (sbyte)Vector.ExtractByte(Index);
+                case 1: return (short)Vector.ExtractUInt16(Index);
+                case 2: return (int)Vector.ExtractUInt32(Index);
+                case 3: return (long)Vector.ExtractUInt64(Index);
+            }
+
+            throw new ArgumentOutOfRangeException(nameof(Size));
+        }
+
+        public static AVec InsertVec(AVec Vector, int Index, int Size, ulong Value)
+        {
+            switch (Size)
+            {
+                case 0: return AVec.InsertByte(Vector, Index, (byte)Value);
+                case 1: return AVec.InsertUInt16(Vector, Index, (ushort)Value);
+                case 2: return AVec.InsertUInt32(Vector, Index, (uint)Value);
+                case 3: return AVec.InsertUInt64(Vector, Index, (ulong)Value);
+            }
+
+            throw new ArgumentOutOfRangeException(nameof(Size));
+        }
+
+        public static AVec InsertSVec(AVec Vector, int Index, int Size, long Value)
+        {
+            switch (Size)
+            {
+                case 0: return AVec.InsertByte(Vector, Index, (byte)Value);
+                case 1: return AVec.InsertUInt16(Vector, Index, (ushort)Value);
+                case 2: return AVec.InsertUInt32(Vector, Index, (uint)Value);
+                case 3: return AVec.InsertUInt64(Vector, Index, (ulong)Value);
+            }
+
+            throw new ArgumentOutOfRangeException(nameof(Size));
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Memory/AMemory.cs b/Ryujinx/Cpu/Memory/AMemory.cs
new file mode 100644
index 0000000000..4e9bd53fcc
--- /dev/null
+++ b/Ryujinx/Cpu/Memory/AMemory.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Memory/AMemoryAlloc.cs b/Ryujinx/Cpu/Memory/AMemoryAlloc.cs
new file mode 100644
index 0000000000..b11e779314
--- /dev/null
+++ b/Ryujinx/Cpu/Memory/AMemoryAlloc.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Memory/AMemoryHelper.cs b/Ryujinx/Cpu/Memory/AMemoryHelper.cs
new file mode 100644
index 0000000000..219aeebf9e
--- /dev/null
+++ b/Ryujinx/Cpu/Memory/AMemoryHelper.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Memory/AMemoryMapInfo.cs b/Ryujinx/Cpu/Memory/AMemoryMapInfo.cs
new file mode 100644
index 0000000000..8ba6c25e60
--- /dev/null
+++ b/Ryujinx/Cpu/Memory/AMemoryMapInfo.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Memory/AMemoryMgr.cs b/Ryujinx/Cpu/Memory/AMemoryMgr.cs
new file mode 100644
index 0000000000..4d995469fe
--- /dev/null
+++ b/Ryujinx/Cpu/Memory/AMemoryMgr.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Memory/AMemoryPerm.cs b/Ryujinx/Cpu/Memory/AMemoryPerm.cs
new file mode 100644
index 0000000000..b425eb94b2
--- /dev/null
+++ b/Ryujinx/Cpu/Memory/AMemoryPerm.cs
@@ -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
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/State/ACoreType.cs b/Ryujinx/Cpu/State/ACoreType.cs
new file mode 100644
index 0000000000..3fed78cf6e
--- /dev/null
+++ b/Ryujinx/Cpu/State/ACoreType.cs
@@ -0,0 +1,8 @@
+namespace ChocolArm64.State
+{
+    public enum ACoreType
+    {
+        CortexA53,
+        CortexA57
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/State/APState.cs b/Ryujinx/Cpu/State/APState.cs
new file mode 100644
index 0000000000..f55431a661
--- /dev/null
+++ b/Ryujinx/Cpu/State/APState.cs
@@ -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
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/State/ARegister.cs b/Ryujinx/Cpu/State/ARegister.cs
new file mode 100644
index 0000000000..5146bc313d
--- /dev/null
+++ b/Ryujinx/Cpu/State/ARegister.cs
@@ -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);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/State/ARegisterSize.cs b/Ryujinx/Cpu/State/ARegisterSize.cs
new file mode 100644
index 0000000000..144f36b929
--- /dev/null
+++ b/Ryujinx/Cpu/State/ARegisterSize.cs
@@ -0,0 +1,10 @@
+namespace ChocolArm64.State
+{
+    enum ARegisterSize
+    {
+        Int32,
+        Int64,
+        SIMD64,
+        SIMD128
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/State/ARegisterType.cs b/Ryujinx/Cpu/State/ARegisterType.cs
new file mode 100644
index 0000000000..f9776bb7dd
--- /dev/null
+++ b/Ryujinx/Cpu/State/ARegisterType.cs
@@ -0,0 +1,9 @@
+namespace ChocolArm64.State
+{
+    enum ARegisterType
+    {
+        Flag,
+        Int,
+        Vector
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/State/ARegisters.cs b/Ryujinx/Cpu/State/ARegisters.cs
new file mode 100644
index 0000000000..3a424a7f93
--- /dev/null
+++ b/Ryujinx/Cpu/State/ARegisters.cs
@@ -0,0 +1,126 @@
+using System;
+
+namespace ChocolArm64.State
+{
+    public class ARegisters
+    {
+        internal const int LRIndex = 30;
+        internal const int ZRIndex = 31;
+
+        public ulong X0,  X1,  X2,  X3,  X4,  X5,  X6,  X7,
+                     X8,  X9,  X10, X11, X12, X13, X14, X15,
+                     X16, X17, X18, X19, X20, X21, X22, X23,
+                     X24, X25, X26, X27, X28, X29, X30, X31;
+
+        public AVec V0,  V1,  V2,  V3,  V4,  V5,  V6,  V7,
+                    V8,  V9,  V10, V11, V12, V13, V14, V15,
+                    V16, V17, V18, V19, V20, V21, V22, V23,
+                    V24, V25, V26, V27, V28, V29, V30, V31;
+
+        public bool Overflow;
+        public bool Carry;
+        public bool Zero;
+        public bool Negative;
+
+        public int  ProcessId;
+        public int  ThreadId;
+        public long TlsAddrEl0;
+        public long TlsAddr;
+
+        private int FPCR;
+        private int FPSR;
+
+        public ACoreType CoreType;
+
+        private const ulong A53DczidEl0 = 4;
+        private const ulong A53CtrEl0  = 0x84448004;
+        private const ulong A57CtrEl0  = 0x8444c004;
+
+        private const ulong TicksPerS  = 19_200_000;
+        private const ulong TicksPerMS = TicksPerS / 1_000;
+
+        public event EventHandler<SvcEventArgs> SvcCall;
+        public event EventHandler<EventArgs>    Undefined;
+
+        public ulong GetSystemReg(int Op0, int Op1, int CRn, int CRm, int Op2)
+        {
+            switch (PackRegId(Op0, Op1, CRn, CRm, Op2))
+            {
+                case 0b11_011_0000_0000_001: return GetCtrEl0();
+                case 0b11_011_0000_0000_111: return GetDczidEl0();
+                case 0b11_011_0100_0100_000: return (ulong)PackFPCR();
+                case 0b11_011_0100_0100_001: return (ulong)PackFPSR();
+                case 0b11_011_1101_0000_010: return (ulong)TlsAddrEl0;
+                case 0b11_011_1101_0000_011: return (ulong)TlsAddr;
+                case 0b11_011_1110_0000_001: return (ulong)Environment.TickCount * TicksPerMS;
+
+                default: throw new ArgumentException();
+            }
+        }
+
+        public void SetSystemReg(int Op0, int Op1, int CRn, int CRm, int Op2, ulong Value)
+        {
+            switch (PackRegId(Op0, Op1, CRn, CRm, Op2))
+            {
+                case 0b11_011_0100_0100_000: UnpackFPCR((int)Value);   break;
+                case 0b11_011_0100_0100_001: UnpackFPSR((int)Value);   break;
+                case 0b11_011_1101_0000_010: TlsAddrEl0 = (long)Value; break;
+
+                default: throw new ArgumentException();
+            }
+        }
+
+        private int PackRegId(int Op0, int Op1, int CRn, int CRm, int Op2)
+        {
+            int Id;
+
+            Id  = Op2 << 0;
+            Id |= CRm << 3;
+            Id |= CRn << 7;
+            Id |= Op1 << 11;
+            Id |= Op0 << 14;
+
+            return Id;
+        }
+
+        public ulong GetCtrEl0()
+        {
+            return CoreType == ACoreType.CortexA53 ? A53CtrEl0 : A57CtrEl0;
+        }
+
+        public ulong GetDczidEl0()
+        {
+            return A53DczidEl0;
+        }
+
+        public int PackFPCR()
+        {
+            return FPCR; //TODO
+        }
+
+        public int PackFPSR()
+        {
+            return FPSR; //TODO
+        }
+
+        public void UnpackFPCR(int Value)
+        {
+            FPCR = Value;
+        }
+
+        public void UnpackFPSR(int Value)
+        {
+            FPSR = Value;
+        }
+
+        public void OnSvcCall(int Imm)
+        {
+            SvcCall?.Invoke(this, new SvcEventArgs(Imm));
+        }
+
+        public void OnUndefined()
+        {
+            Undefined?.Invoke(this, EventArgs.Empty);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/State/AVec.cs b/Ryujinx/Cpu/State/AVec.cs
new file mode 100644
index 0000000000..f7eb2e221b
--- /dev/null
+++ b/Ryujinx/Cpu/State/AVec.cs
@@ -0,0 +1,243 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace ChocolArm64.State
+{
+    [StructLayout(LayoutKind.Explicit, Size = 16)]
+    public struct AVec
+    {
+        [FieldOffset(0x0)] public byte B0;
+        [FieldOffset(0x1)] public byte B1;
+        [FieldOffset(0x2)] public byte B2;
+        [FieldOffset(0x3)] public byte B3;
+        [FieldOffset(0x4)] public byte B4;
+        [FieldOffset(0x5)] public byte B5;
+        [FieldOffset(0x6)] public byte B6;
+        [FieldOffset(0x7)] public byte B7;
+        [FieldOffset(0x8)] public byte B8;
+        [FieldOffset(0x9)] public byte B9;
+        [FieldOffset(0xa)] public byte B10;
+        [FieldOffset(0xb)] public byte B11;
+        [FieldOffset(0xc)] public byte B12;
+        [FieldOffset(0xd)] public byte B13;
+        [FieldOffset(0xe)] public byte B14;
+        [FieldOffset(0xf)] public byte B15;
+
+        [FieldOffset(0x0)] public ushort H0;
+        [FieldOffset(0x2)] public ushort H1;
+        [FieldOffset(0x4)] public ushort H2;
+        [FieldOffset(0x6)] public ushort H3;
+        [FieldOffset(0x8)] public ushort H4;
+        [FieldOffset(0xa)] public ushort H5;
+        [FieldOffset(0xc)] public ushort H6;
+        [FieldOffset(0xe)] public ushort H7;
+
+        [FieldOffset(0x0)] public uint W0;
+        [FieldOffset(0x4)] public uint W1;
+        [FieldOffset(0x8)] public uint W2;
+        [FieldOffset(0xc)] public uint W3;
+
+        [FieldOffset(0x0)] public float S0;
+        [FieldOffset(0x4)] public float S1;
+        [FieldOffset(0x8)] public float S2;
+        [FieldOffset(0xc)] public float S3;
+
+        [FieldOffset(0x0)] public ulong X0;
+        [FieldOffset(0x8)] public ulong X1;
+
+        [FieldOffset(0x0)] public double D0;
+        [FieldOffset(0x8)] public double D1;
+
+        public byte ExtractByte(int Index)
+        {
+            switch (Index)
+            {
+                case 0:  return B0;
+                case 1:  return B1;
+                case 2:  return B2;
+                case 3:  return B3;
+                case 4:  return B4;
+                case 5:  return B5;
+                case 6:  return B6;
+                case 7:  return B7;
+                case 8:  return B8;
+                case 9:  return B9;
+                case 10: return B10;
+                case 11: return B11;
+                case 12: return B12;
+                case 13: return B13;
+                case 14: return B14;
+                case 15: return B15;
+            }
+
+            throw new ArgumentOutOfRangeException(nameof(Index));
+        }
+
+        public ushort ExtractUInt16(int Index)
+        {
+            switch (Index)
+            {
+                case 0: return H0;
+                case 1: return H1;
+                case 2: return H2;
+                case 3: return H3;
+                case 4: return H4;
+                case 5: return H5;
+                case 6: return H6;
+                case 7: return H7;
+            }
+
+            throw new ArgumentOutOfRangeException(nameof(Index));
+        }
+
+        public uint ExtractUInt32(int Index)
+        {
+            switch (Index)
+            {
+                case 0: return W0;
+                case 1: return W1;
+                case 2: return W2;
+                case 3: return W3;
+            }
+
+            throw new ArgumentOutOfRangeException(nameof(Index));
+        }
+
+        public float ExtractSingle(int Index)
+        {
+            switch (Index)
+            {
+                case 0: return S0;
+                case 1: return S1;
+                case 2: return S2;
+                case 3: return S3;
+            }
+
+            throw new ArgumentOutOfRangeException(nameof(Index));
+        }
+
+        public ulong ExtractUInt64(int Index)
+        {
+            switch (Index)
+            {
+                case 0: return X0;
+                case 1: return X1;
+            }
+
+            throw new ArgumentOutOfRangeException(nameof(Index));
+        }
+
+        public double ExtractDouble(int Index)
+        {
+            switch (Index)
+            {
+                case 0: return D0;
+                case 1: return D1;
+            }
+
+            throw new ArgumentOutOfRangeException(nameof(Index));
+        }
+
+        public static AVec InsertByte(AVec Vec, int Index, byte Value)
+        {
+            switch (Index)
+            {
+                case 0:  Vec.B0  = Value; break;
+                case 1:  Vec.B1  = Value; break;
+                case 2:  Vec.B2  = Value; break;
+                case 3:  Vec.B3  = Value; break;
+                case 4:  Vec.B4  = Value; break;
+                case 5:  Vec.B5  = Value; break;
+                case 6:  Vec.B6  = Value; break;
+                case 7:  Vec.B7  = Value; break;
+                case 8:  Vec.B8  = Value; break;
+                case 9:  Vec.B9  = Value; break;
+                case 10: Vec.B10 = Value; break;
+                case 11: Vec.B11 = Value; break;
+                case 12: Vec.B12 = Value; break;
+                case 13: Vec.B13 = Value; break;
+                case 14: Vec.B14 = Value; break;
+                case 15: Vec.B15 = Value; break;
+
+                default: throw new ArgumentOutOfRangeException(nameof(Index));
+            }
+
+            return Vec;
+        }
+
+        public static AVec InsertUInt16(AVec Vec, int Index, ushort Value)
+        {
+            switch (Index)
+            {
+                case 0: Vec.H0 = Value; break;
+                case 1: Vec.H1 = Value; break;
+                case 2: Vec.H2 = Value; break;
+                case 3: Vec.H3 = Value; break;
+                case 4: Vec.H4 = Value; break;
+                case 5: Vec.H5 = Value; break;
+                case 6: Vec.H6 = Value; break;
+                case 7: Vec.H7 = Value; break;
+
+                default: throw new ArgumentOutOfRangeException(nameof(Index));
+            }
+
+            return Vec;
+        }
+
+        public static AVec InsertUInt32(AVec Vec, int Index, uint Value)
+        {
+            switch (Index)
+            {
+                case 0: Vec.W0 = Value; break;
+                case 1: Vec.W1 = Value; break;
+                case 2: Vec.W2 = Value; break;
+                case 3: Vec.W3 = Value; break;
+
+                default: throw new ArgumentOutOfRangeException(nameof(Index));
+            }
+
+            return Vec;
+        }
+
+        public static AVec InsertSingle(AVec Vec, int Index, float Value)
+        {
+            switch (Index)
+            {
+                case 0: Vec.S0 = Value; break;
+                case 1: Vec.S1 = Value; break;
+                case 2: Vec.S2 = Value; break;
+                case 3: Vec.S3 = Value; break;
+
+                default: throw new ArgumentOutOfRangeException(nameof(Index));
+            }
+
+            return Vec;
+        }
+
+        public static AVec InsertUInt64(AVec Vec, int Index, ulong Value)
+        {
+            switch (Index)
+            {
+                case 0: Vec.X0 = Value; break;
+                case 1: Vec.X1 = Value; break;
+
+                default: throw new ArgumentOutOfRangeException(nameof(Index));
+            }
+
+            return Vec;
+        }
+
+        public static AVec InsertDouble(AVec Vec, int Index, double Value)
+        {
+            switch (Index)
+            {
+                case 0: Vec.D0 = Value; break;
+                case 1: Vec.D1 = Value; break;
+
+                default: throw new ArgumentOutOfRangeException(nameof(Index));
+            }
+
+            return Vec;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/State/SvcEventArgs.cs b/Ryujinx/Cpu/State/SvcEventArgs.cs
new file mode 100644
index 0000000000..3a43241a64
--- /dev/null
+++ b/Ryujinx/Cpu/State/SvcEventArgs.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace ChocolArm64.State
+{
+    public class SvcEventArgs : EventArgs
+    {
+        public int Id { get; private set; }
+
+        public SvcEventArgs(int Id)
+        {
+            this.Id = Id;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Translation/AILBlock.cs b/Ryujinx/Cpu/Translation/AILBlock.cs
new file mode 100644
index 0000000000..2746e4288d
--- /dev/null
+++ b/Ryujinx/Cpu/Translation/AILBlock.cs
@@ -0,0 +1,65 @@
+using System.Collections.Generic;
+
+namespace ChocolArm64.Translation
+{
+    class AILBlock : IAILEmit
+    {
+        public long IntInputs  { get; private set; }
+        public long IntOutputs { get; private set; }
+
+        public long VecInputs  { get; private set; }
+        public long VecOutputs { get; private set; }
+
+        public bool HasStateStore { get; private set; }
+
+        public List<IAILEmit> ILEmitters { get; private set; }
+
+        public AILBlock Next   { get; set; }
+        public AILBlock Branch { get; set; }
+
+        public AILBlock()
+        {
+            ILEmitters = new List<IAILEmit>();
+        }
+
+        public void Add(IAILEmit ILEmitter)
+        {
+            if (ILEmitter is AILOpCodeLoad Ld && AILEmitter.IsRegIndex(Ld.Index))
+            {
+                switch (Ld.IoType & AIoType.Mask)
+                {
+                    case AIoType.Flag:   IntInputs |= ((1L << Ld.Index) << 32) & ~IntOutputs; break;
+                    case AIoType.Int:    IntInputs |=  (1L << Ld.Index)        & ~IntOutputs; break;
+                    case AIoType.Vector: VecInputs |=  (1L << Ld.Index)        & ~VecOutputs; break;
+                }
+            }
+            else if (ILEmitter is AILOpCodeStore St)
+            {
+                if (AILEmitter.IsRegIndex(St.Index))
+                {
+                    switch (St.IoType & AIoType.Mask)
+                    {
+                        case AIoType.Flag:   IntOutputs |= (1L << St.Index) << 32; break;
+                        case AIoType.Int:    IntOutputs |=  1L << St.Index;        break;
+                        case AIoType.Vector: VecOutputs |=  1L << St.Index;        break;
+                    }
+                }
+
+                if (St.IoType == AIoType.Fields)
+                {
+                    HasStateStore = true;
+                }
+            }
+
+            ILEmitters.Add(ILEmitter);
+        }
+
+        public void Emit(AILEmitter Context)
+        {
+            foreach (IAILEmit ILEmitter in ILEmitters)
+            {
+                ILEmitter.Emit(Context);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Translation/AILConv.cs b/Ryujinx/Cpu/Translation/AILConv.cs
new file mode 100644
index 0000000000..8969dc4e94
--- /dev/null
+++ b/Ryujinx/Cpu/Translation/AILConv.cs
@@ -0,0 +1,113 @@
+using ChocolArm64.State;
+using System;
+using System.Reflection;
+using System.Reflection.Emit;
+
+namespace ChocolArm64.Translation
+{
+    static class AILConv
+    {
+        public static void EmitConv(AILEmitter Context, Type SrcType, Type TgtType)
+        {
+            if (SrcType == TgtType)
+            {
+                //If both types are equal we don't need to cast anything.
+                return;
+            }
+
+            if (SrcType.IsPrimitive)
+            {
+                if (TgtType == typeof(byte))
+                {
+                    Context.Generator.Emit(OpCodes.Conv_U1);
+                }
+                else if (TgtType == typeof(ushort))
+                {
+                    Context.Generator.Emit(OpCodes.Conv_U2);
+                }
+                else if (TgtType == typeof(uint))
+                {
+                    Context.Generator.Emit(OpCodes.Conv_U4);
+                }
+                else if (TgtType == typeof(ulong))
+                {
+                    Context.Generator.Emit(OpCodes.Conv_U8);
+                }
+                else if (TgtType == typeof(float))
+                {
+                    Context.Generator.Emit(OpCodes.Conv_R4);
+                }
+                else if (TgtType == typeof(double))
+                {
+                    Context.Generator.Emit(OpCodes.Conv_R8);
+                }
+                else if (TgtType == typeof(AVec))
+                {
+                    EmitMakeVec(Context, SrcType);
+                }
+                else
+                {
+                    throw new ArgumentException(nameof(TgtType));
+                }
+            }
+            else if (SrcType == typeof(AVec))
+            {
+                if (TgtType == typeof(float))
+                {
+                    EmitScalarLdfld(Context, nameof(AVec.S0));
+                }
+                else if (TgtType == typeof(double))
+                {
+                    EmitScalarLdfld(Context, nameof(AVec.D0));
+                }
+                else if (TgtType == typeof(byte))
+                {
+                    EmitScalarLdfld(Context, nameof(AVec.B0));
+                }
+                else if (TgtType == typeof(ushort))
+                {
+                    EmitScalarLdfld(Context, nameof(AVec.H0));
+                }
+                else if (TgtType == typeof(uint))
+                {
+                    EmitScalarLdfld(Context, nameof(AVec.W0));
+                }
+                else if (TgtType == typeof(ulong))
+                {
+                    EmitScalarLdfld(Context, nameof(AVec.X0));
+                }
+                else
+                {
+                    throw new ArgumentException(nameof(TgtType));
+                }
+            }
+            else
+            {
+                throw new ArgumentException(nameof(SrcType));
+            }
+        }
+
+        private static void EmitScalarLdfld(AILEmitter Context,string FldName)
+        {
+            Context.Generator.Emit(OpCodes.Ldfld, typeof(AVec).GetField(FldName));
+        }
+
+        private static void EmitMakeVec(AILEmitter Context, Type SrcType)
+        {
+            string MthdName = nameof(MakeScalar);
+
+            Type[] MthdTypes = new Type[] { SrcType };
+
+            MethodInfo MthdInfo = typeof(AILConv).GetMethod(MthdName, MthdTypes);
+
+            Context.Generator.Emit(OpCodes.Call, MthdInfo);
+        }
+
+        public static AVec MakeScalar(byte   Value) => new AVec { B0 = Value };
+        public static AVec MakeScalar(ushort Value) => new AVec { H0 = Value };
+        public static AVec MakeScalar(uint   Value) => new AVec { W0 = Value };
+        public static AVec MakeScalar(float  Value) => new AVec { S0 = Value };
+        public static AVec MakeScalar(ulong  Value) => new AVec { X0 = Value };
+        public static AVec MakeScalar(double Value) => new AVec { D0 = Value };
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Translation/AILEmitter.cs b/Ryujinx/Cpu/Translation/AILEmitter.cs
new file mode 100644
index 0000000000..0619149c3d
--- /dev/null
+++ b/Ryujinx/Cpu/Translation/AILEmitter.cs
@@ -0,0 +1,190 @@
+using ChocolArm64.Decoder;
+using ChocolArm64.State;
+using System;
+using System.Collections.Generic;
+using System.Reflection.Emit;
+
+namespace ChocolArm64.Translation
+{
+    class AILEmitter
+    {
+        public ALocalAlloc LocalAlloc { get; private set; }
+
+        public ILGenerator Generator { get; private set; }
+
+        private Dictionary<ARegister, int> Locals;
+
+        private AILBlock[] ILBlocks;
+
+        private AILBlock Root;
+
+        private ATranslatedSub Subroutine;
+
+        private string SubName;
+
+        private int LocalsCount;
+
+        public AILEmitter(ABlock[] Graph, ABlock Root, string SubName)
+        {
+            this.SubName = SubName;
+
+            Locals = new Dictionary<ARegister, int>();
+
+            ILBlocks = new AILBlock[Graph.Length];
+
+            AILBlock GetBlock(int Index)
+            {
+                if (Index < 0 || Index >= ILBlocks.Length)
+                {
+                    return null;
+                }
+
+                if (ILBlocks[Index] == null)
+                {
+                    ILBlocks[Index] = new AILBlock();
+                }
+
+                return ILBlocks[Index];
+            }
+
+            for (int Index = 0; Index < ILBlocks.Length; Index++)
+            {
+                AILBlock Block = GetBlock(Index);
+
+                Block.Next   = GetBlock(Array.IndexOf(Graph, Graph[Index].Next));
+                Block.Branch = GetBlock(Array.IndexOf(Graph, Graph[Index].Branch));
+            }
+
+            this.Root = ILBlocks[Array.IndexOf(Graph, Root)];
+        }
+
+        public ATranslatedSub GetSubroutine()
+        {
+            LocalAlloc = new ALocalAlloc(ILBlocks, Root);
+
+            InitSubroutine();
+            InitLocals();
+
+            foreach (AILBlock ILBlock in ILBlocks)
+            {
+                ILBlock.Emit(this);
+            }
+
+            return Subroutine;
+        }
+
+        public AILBlock GetILBlock(int Index) => ILBlocks[Index];
+
+        private void InitLocals()
+        {
+            int ParamsStart = ATranslatedSub.FixedArgTypes.Length;
+
+            Locals = new Dictionary<ARegister, int>();
+
+            for (int Index = 0; Index < Subroutine.Params.Count; Index++)
+            {
+                ARegister Reg = Subroutine.Params[Index];
+
+                Generator.EmitLdarg(Index + ParamsStart);
+
+                AILConv.EmitConv(this, GetFieldType(Reg.Type), GetLocalType(Reg));
+
+                Generator.EmitStloc(GetLocalIndex(Reg));
+            }
+        }
+
+        private void InitSubroutine()
+        {
+            List<ARegister> Params = new List<ARegister>();
+
+            void SetParams(long Inputs, ARegisterType BaseType)
+            {
+                for (int Bit = 0; Bit < 64; Bit++)
+                {
+                    long Mask = 1L << Bit;
+
+                    if ((Inputs & Mask) != 0)
+                    {
+                        Params.Add(GetRegFromBit(Bit, BaseType));
+                    }
+                }
+            }
+
+            SetParams(LocalAlloc.GetIntInputs(Root), ARegisterType.Int);
+            SetParams(LocalAlloc.GetVecInputs(Root), ARegisterType.Vector);
+
+            DynamicMethod Mthd = new DynamicMethod(SubName, typeof(long), GetParamTypes(Params));
+
+            Generator = Mthd.GetILGenerator();
+
+            Subroutine = new ATranslatedSub(Mthd, Params);
+        }
+
+        private Type[] GetParamTypes(IList<ARegister> Params)
+        {
+            Type[] FixedArgs = ATranslatedSub.FixedArgTypes;
+
+            Type[] Output = new Type[Params.Count + FixedArgs.Length];
+
+            FixedArgs.CopyTo(Output, 0);
+
+            int TypeIdx = FixedArgs.Length;
+
+            for (int Index = 0; Index < Params.Count; Index++)
+            {
+                Output[TypeIdx++] = GetFieldType(Params[Index].Type);
+            }
+
+            return Output;
+        }
+
+        public int GetLocalIndex(ARegister Reg)
+        {
+            if (!Locals.TryGetValue(Reg, out int Index))
+            {
+                Generator.DeclareLocal(GetLocalType(Reg));
+
+                Index = LocalsCount++;
+
+                Locals.Add(Reg, Index);
+            }
+
+            return Index;
+        }
+
+        public Type GetLocalType(ARegister Reg) => GetFieldType(Reg.Type);
+
+        public Type GetFieldType(ARegisterType RegType)
+        {
+            switch (RegType)
+            {
+                case ARegisterType.Flag:   return typeof(bool);
+                case ARegisterType.Int:    return typeof(ulong);
+                case ARegisterType.Vector: return typeof(AVec);
+            }
+
+            throw new ArgumentException(nameof(RegType));
+        }
+
+        public static ARegister GetRegFromBit(int Bit, ARegisterType BaseType)
+        {
+            if (Bit < 32)
+            {
+                return new ARegister(Bit, BaseType);
+            }
+            else if (BaseType == ARegisterType.Int)
+            {
+                return new ARegister(Bit & 0x1f, ARegisterType.Flag);
+            }
+            else
+            {
+                throw new ArgumentOutOfRangeException(nameof(Bit));
+            }
+        }
+
+        public static bool IsRegIndex(int Index)
+        {
+            return Index >= 0 && Index < 32;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Translation/AILEmitterCtx.cs b/Ryujinx/Cpu/Translation/AILEmitterCtx.cs
new file mode 100644
index 0000000000..88841db77c
--- /dev/null
+++ b/Ryujinx/Cpu/Translation/AILEmitterCtx.cs
@@ -0,0 +1,548 @@
+using ChocolArm64.Decoder;
+using ChocolArm64.Instruction;
+using ChocolArm64.State;
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Reflection.Emit;
+
+namespace ChocolArm64.Translation
+{
+    class AILEmitterCtx
+    {
+        private ATranslator Translator;
+
+        private Dictionary<long, AILLabel> Labels;        
+
+        private AILEmitter Emitter;
+
+        private AILBlock ILBlock;
+
+        private AOpCode LastCmpOp;
+        private AOpCode LastFlagOp;
+
+        private int BlkIndex;
+        private int OpcIndex;
+
+        private ABlock[] Graph;
+        private ABlock   Root;
+        public  ABlock   CurrBlock => Graph[BlkIndex];
+        public  AOpCode  CurrOp    => Graph[BlkIndex].OpCodes[OpcIndex];
+
+        //This is the index of the temporary register, used to store temporary
+        //values needed by some functions, since IL doesn't have a swap instruction.
+        //You can use any value here as long it doesn't conflict with the indices
+        //for the other registers. Any value >= 64 or < 0 will do.
+        private const int Tmp1Index = -1;
+        private const int Tmp2Index = -2;
+        private const int Tmp3Index = -3;
+        private const int Tmp4Index = -4;
+
+        public AILEmitterCtx(ATranslator Translator, ABlock[] Graph, ABlock Root)
+        {
+            this.Translator = Translator;
+            this.Graph      = Graph;
+            this.Root       = Root;
+
+            string SubName = $"Sub{Root.Position:X16}";
+
+            Labels = new Dictionary<long, AILLabel>();
+
+            Emitter = new AILEmitter(Graph, Root, SubName);
+
+            ILBlock = Emitter.GetILBlock(0);          
+
+            OpcIndex = -1;
+
+            if (!AdvanceOpCode())
+            {
+                throw new ArgumentException(nameof(Graph));
+            }
+        }
+
+        public ATranslatedSub GetSubroutine() => Emitter.GetSubroutine();
+
+        public bool AdvanceOpCode()
+        {
+            while (++OpcIndex >= (CurrBlock?.OpCodes.Count ?? 0))
+            {
+                if (BlkIndex + 1 >= Graph.Length)
+                {
+                    return false;
+                }
+
+                BlkIndex++;
+                OpcIndex = -1;
+
+                ILBlock = Emitter.GetILBlock(BlkIndex);
+            }
+
+            return true;
+        }
+
+        public void EmitOpCode()
+        {
+            if (OpcIndex == 0)
+            {
+                MarkLabel(GetLabel(CurrBlock.Position));
+            }
+
+            CurrOp.Emitter(this);
+        }
+
+        public bool TryOptEmitSubroutineCall()
+        {           
+            if (!Translator.TryGetCachedSub(CurrOp, out ATranslatedSub Sub))
+            {
+                return false;
+            }
+
+            for (int Index = 0; Index < ATranslatedSub.FixedArgTypes.Length; Index++)
+            {
+                EmitLdarg(Index);
+            }
+
+            foreach (ARegister Reg in Sub.Params)
+            {
+                switch (Reg.Type)
+                {
+                    case ARegisterType.Flag:   Ldloc(Reg.Index, AIoType.Flag);   break;
+                    case ARegisterType.Int:    Ldloc(Reg.Index, AIoType.Int);    break;
+                    case ARegisterType.Vector: Ldloc(Reg.Index, AIoType.Vector); break;
+                }
+            }
+
+            EmitCall(Sub.Method);
+
+            return true;
+        }
+
+        public void TryOptMarkCondWithoutCmp()
+        {
+            LastCmpOp = CurrOp;
+
+            AInstEmitAluHelper.EmitDataLoadOpers(this);
+
+            Stloc(Tmp4Index, AIoType.Int);
+            Stloc(Tmp3Index, AIoType.Int);
+        }
+
+        private Dictionary<ACond, OpCode> BranchOps = new Dictionary<ACond, OpCode>()
+        {
+            { ACond.Eq,    OpCodes.Beq    },
+            { ACond.Ne,    OpCodes.Bne_Un },
+            { ACond.Ge_Un, OpCodes.Bge_Un },
+            { ACond.Lt_Un, OpCodes.Blt_Un },
+            { ACond.Gt_Un, OpCodes.Bgt_Un },
+            { ACond.Le_Un, OpCodes.Ble_Un },
+            { ACond.Ge,    OpCodes.Bge    },
+            { ACond.Lt,    OpCodes.Blt    },
+            { ACond.Gt,    OpCodes.Bgt    },
+            { ACond.Le,    OpCodes.Ble    }
+        };
+
+        public void EmitCondBranch(AILLabel Target, ACond Cond)
+        {
+            OpCode ILOp;
+
+            int IntCond = (int)Cond;            
+
+            if (LastFlagOp == LastCmpOp && BranchOps.ContainsKey(Cond))
+            {
+                Ldloc(Tmp3Index, AIoType.Int, GetIntType(LastCmpOp));
+                Ldloc(Tmp4Index, AIoType.Int, GetIntType(LastCmpOp));
+
+                if (LastCmpOp.Emitter == AInstEmit.Adds)
+                {
+                    Emit(OpCodes.Neg);
+                }
+
+                ILOp = BranchOps[Cond];
+            }
+            else if (IntCond < 14)
+            {
+                int CondTrue = IntCond >> 1;
+
+                switch (CondTrue)
+                {
+                    case 0: EmitLdflg((int)APState.ZBit); break;
+                    case 1: EmitLdflg((int)APState.CBit); break;
+                    case 2: EmitLdflg((int)APState.NBit); break;
+                    case 3: EmitLdflg((int)APState.VBit); break;
+
+                    case 4:
+                        EmitLdflg((int)APState.CBit);
+                        EmitLdflg((int)APState.ZBit);
+
+                        Emit(OpCodes.Not);
+                        Emit(OpCodes.And);
+                        break;
+
+                    case 5:
+                    case 6:
+                        EmitLdflg((int)APState.NBit);
+                        EmitLdflg((int)APState.VBit);
+
+                        Emit(OpCodes.Ceq);
+
+                        if (CondTrue == 6)
+                        {
+                            EmitLdflg((int)APState.ZBit);
+
+                            Emit(OpCodes.Not);
+                            Emit(OpCodes.And);
+                        }
+                        break;
+                }
+
+                ILOp = (IntCond & 1) != 0
+                    ? OpCodes.Brfalse
+                    : OpCodes.Brtrue;
+            }
+            else
+            {
+                ILOp = OpCodes.Br;
+            }
+
+            Emit(ILOp, Target);
+        }
+
+        public void EmitCast(AIntType IntType)
+        {
+            switch (IntType)
+            {
+                case AIntType.UInt8:  Emit(OpCodes.Conv_U1); break;
+                case AIntType.UInt16: Emit(OpCodes.Conv_U2); break;
+                case AIntType.UInt32: Emit(OpCodes.Conv_U4); break;
+                case AIntType.UInt64: Emit(OpCodes.Conv_U8); break;
+                case AIntType.Int8:   Emit(OpCodes.Conv_I1); break;
+                case AIntType.Int16:  Emit(OpCodes.Conv_I2); break;
+                case AIntType.Int32:  Emit(OpCodes.Conv_I4); break;
+                case AIntType.Int64:  Emit(OpCodes.Conv_I8); break;
+            }
+
+            if (IntType == AIntType.UInt64 ||
+                IntType == AIntType.Int64)
+            {
+                return;
+            }
+
+            if (CurrOp.RegisterSize != ARegisterSize.Int32)
+            {
+                Emit(IntType >= AIntType.Int8
+                    ? OpCodes.Conv_I8
+                    : OpCodes.Conv_U8);
+            }
+        }
+
+        public void EmitLsl(int Amount) => EmitILShift(Amount, OpCodes.Shl);
+        public void EmitLsr(int Amount) => EmitILShift(Amount, OpCodes.Shr_Un);
+        public void EmitAsr(int Amount) => EmitILShift(Amount, OpCodes.Shr);
+
+        private void EmitILShift(int Amount, OpCode ILOp)
+        {
+            if (Amount > 0)
+            {
+                EmitLdc_I4(Amount);
+
+                Emit(ILOp);
+            }
+        }
+
+        public void EmitRor(int Amount)
+        {
+            if (Amount > 0)
+            {
+                Stloc(Tmp2Index, AIoType.Int);
+                Ldloc(Tmp2Index, AIoType.Int);
+
+                EmitLdc_I4(Amount);
+
+                Emit(OpCodes.Shr_Un);
+                
+                Ldloc(Tmp2Index, AIoType.Int);
+
+                EmitLdc_I4(CurrOp.GetBitsCount() - Amount);
+
+                Emit(OpCodes.Shl);
+                Emit(OpCodes.Or);
+            }
+        }
+
+        public AILLabel GetLabel(long Position)
+        {
+            if (!Labels.TryGetValue(Position, out AILLabel Output))
+            {
+                Output = new AILLabel();
+
+                Labels.Add(Position, Output);
+            }
+
+            return Output;
+        }
+
+        public void MarkLabel(AILLabel Label)
+        {
+            ILBlock.Add(Label);
+        }
+
+        public void Emit(OpCode ILOp)
+        {
+            ILBlock.Add(new AILOpCode(ILOp));
+        }
+
+        public void Emit(OpCode ILOp, AILLabel Label)
+        {
+            ILBlock.Add(new AILOpCodeBranch(ILOp, Label));
+        }
+
+        public void Emit(string Text)
+        {
+            ILBlock.Add(new AILOpCodeLog(Text));
+        }
+
+        public void EmitLdarg(int Index)
+        {
+            ILBlock.Add(new AILOpCodeLoad(Index, AIoType.Arg));
+        }
+
+        public void EmitLdintzr(int Index)
+        {
+            if (Index != ARegisters.ZRIndex)
+            {
+                EmitLdint(Index);
+            }
+            else
+            {
+                EmitLdc_I(0);
+            }
+        }
+
+        public void EmitStintzr(int Index)
+        {
+            if (Index != ARegisters.ZRIndex)
+            {
+                EmitStint(Index);
+            }
+            else
+            {
+                Emit(OpCodes.Pop);
+            }
+        }
+
+        public void EmitLoadState(ABlock RetBlk)
+        {
+            ILBlock.Add(new AILOpCodeLoad(Array.IndexOf(Graph, RetBlk), AIoType.Fields));
+        }
+
+        public void EmitStoreState()
+        {
+            ILBlock.Add(new AILOpCodeStore(Array.IndexOf(Graph, CurrBlock), AIoType.Fields));
+        }
+
+        public void EmitLdtmp() => EmitLdint(Tmp1Index);
+        public void EmitSttmp() => EmitStint(Tmp1Index);
+
+        public void EmitLdint(int Index) => Ldloc(Index, AIoType.Int);
+        public void EmitStint(int Index) => Stloc(Index, AIoType.Int);
+
+        public void EmitLdvec(int Index) => Ldloc(Index, AIoType.Vector);
+        public void EmitStvec(int Index) => Stloc(Index, AIoType.Vector);
+
+        public void EmitLdvecsi(int Index) => Ldloc(Index, AIoType.VectorI);
+        public void EmitStvecsi(int Index) => Stloc(Index, AIoType.VectorI);
+
+        public void EmitLdvecsf(int Index) => Ldloc(Index, AIoType.VectorF);
+        public void EmitStvecsf(int Index) => Stloc(Index, AIoType.VectorF);
+
+        public void EmitLdflg(int Index) => Ldloc(Index, AIoType.Flag);
+        public void EmitStflg(int Index)
+        {
+            LastFlagOp = CurrOp;
+
+            Stloc(Index, AIoType.Flag);
+        }
+
+        private void Ldloc(int Index, AIoType IoType)
+        {
+            ILBlock.Add(new AILOpCodeLoad(Index, IoType, GetOperType(IoType)));
+        }
+
+        private void Ldloc(int Index, AIoType IoType, Type Type)
+        {
+            ILBlock.Add(new AILOpCodeLoad(Index, IoType, Type));
+        }
+
+        private void Stloc(int Index, AIoType IoType)
+        {
+            ILBlock.Add(new AILOpCodeStore(Index, IoType, GetOutOperType(IoType)));
+        }
+
+        private Type GetOutOperType(AIoType IoType)
+        {
+            //This instruction is used to convert between floating point
+            //types, so the input and output types are different.
+            if (CurrOp.Emitter == AInstEmit.Fcvt_S)
+            {
+                return GetFloatType(((AOpCodeSimd)CurrOp).Opc);
+            }
+            else
+            {
+                return GetOperType(IoType);
+            }
+        }
+
+        private Type GetOperType(AIoType IoType)
+        {
+            switch (IoType & AIoType.Mask)
+            {
+                case AIoType.Flag:   return typeof(bool);
+                case AIoType.Int:    return GetIntType(CurrOp);
+                case AIoType.Vector: return GetVecType(CurrOp, IoType);
+            }
+
+            throw new ArgumentException(nameof(IoType));
+        }
+
+        private Type GetIntType(AOpCode OpCode)
+        {
+            //Always default to 64-bits.
+            return OpCode.RegisterSize == ARegisterSize.Int32
+                ? typeof(uint)
+                : typeof(ulong);
+        }
+
+        private Type GetVecType(AOpCode OpCode, AIoType IoType)
+        {
+            if (!(OpCode is IAOpCodeSimd Op))
+            {
+                return typeof(AVec);
+            }
+
+            int Size = Op.Size;
+
+            if (Op.Emitter == AInstEmit.Fmov_Ftoi ||
+                Op.Emitter == AInstEmit.Fmov_Itof)
+            {
+                Size |= 2;
+            }
+
+            if (Op is AOpCodeMem || Op is IAOpCodeLit)
+            {
+                return Size < 4 ? typeof(ulong) : typeof(AVec);
+            }
+            else if (IoType == AIoType.VectorI)
+            {
+                return GetIntType(Size);
+            }
+            else if (IoType == AIoType.VectorF)
+            {
+                return GetFloatType(Size);
+            }
+
+            return typeof(AVec);
+        }
+
+        private static Type GetIntType(int Size)
+        {
+            switch (Size)
+            {
+                case 0: return typeof(byte);
+                case 1: return typeof(ushort);
+                case 2: return typeof(uint);
+                case 3: return typeof(ulong);
+            }
+
+            throw new ArgumentOutOfRangeException(nameof(Size));
+        }
+
+        private static Type GetFloatType(int Size)
+        {
+            switch (Size)
+            {
+                case 0: return typeof(float);
+                case 1: return typeof(double);
+            }
+
+            throw new ArgumentOutOfRangeException(nameof(Size));
+        }
+
+        public void EmitCall(Type MthdType, string MthdName)
+        {
+            if (MthdType == null)
+            {
+                throw new ArgumentNullException(nameof(MthdType));
+            }
+
+            if (MthdName == null)
+            {
+                throw new ArgumentNullException(nameof(MthdName));
+            }
+
+            EmitCall(MthdType.GetMethod(MthdName));
+        }
+
+        public void EmitCall(MethodInfo MthdInfo)
+        {
+            if (MthdInfo == null)
+            {
+                throw new ArgumentNullException(nameof(MthdInfo));
+            }
+
+            ILBlock.Add(new AILOpCodeCall(MthdInfo));
+        }
+
+        public void EmitLdc_I(long Value)
+        {
+            if (CurrOp.RegisterSize == ARegisterSize.Int32)
+            {
+                EmitLdc_I4((int)Value);
+            }
+            else
+            {
+                EmitLdc_I8(Value);
+            }
+        }
+
+        public void EmitLdc_I4(int Value)
+        {
+            ILBlock.Add(new AILOpCodeConst(Value));
+        }
+
+        public void EmitLdc_I8(long Value)
+        {
+            ILBlock.Add(new AILOpCodeConst(Value));
+        }
+
+        public void EmitLdc_R4(float Value)
+        {
+            ILBlock.Add(new AILOpCodeConst(Value));
+        }
+
+        public void EmitLdc_R8(double Value)
+        {
+            ILBlock.Add(new AILOpCodeConst(Value));
+        }
+
+        public void EmitZNFlagCheck()
+        {
+            EmitZNCheck(OpCodes.Ceq, (int)APState.ZBit);
+            EmitZNCheck(OpCodes.Clt, (int)APState.NBit);
+        }
+
+        private void EmitZNCheck(OpCode ILCmpOp, int Flag)
+        {
+            Emit(OpCodes.Dup);
+            Emit(OpCodes.Ldc_I4_0);
+
+            if (CurrOp.RegisterSize != ARegisterSize.Int32)
+            {
+                Emit(OpCodes.Conv_I8);
+            }
+
+            Emit(ILCmpOp);
+
+            EmitStflg(Flag);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Translation/AILLabel.cs b/Ryujinx/Cpu/Translation/AILLabel.cs
new file mode 100644
index 0000000000..0ee39ad7e2
--- /dev/null
+++ b/Ryujinx/Cpu/Translation/AILLabel.cs
@@ -0,0 +1,28 @@
+using System.Reflection.Emit;
+
+namespace ChocolArm64.Translation
+{
+    class AILLabel : IAILEmit
+    {
+        private bool HasLabel;
+
+        private Label Lbl;
+
+        public void Emit(AILEmitter Context)
+        {
+            Context.Generator.MarkLabel(GetLabel(Context));
+        }
+
+        public Label GetLabel(AILEmitter Context)
+        {
+            if (!HasLabel)
+            {
+                Lbl = Context.Generator.DefineLabel();
+
+                HasLabel = true;
+            }
+
+            return Lbl;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Translation/AILOpCode.cs b/Ryujinx/Cpu/Translation/AILOpCode.cs
new file mode 100644
index 0000000000..a4bc93a065
--- /dev/null
+++ b/Ryujinx/Cpu/Translation/AILOpCode.cs
@@ -0,0 +1,19 @@
+using System.Reflection.Emit;
+
+namespace ChocolArm64.Translation
+{
+    struct AILOpCode : IAILEmit
+    {
+        private OpCode ILOp;
+
+        public AILOpCode(OpCode ILOp)
+        {
+            this.ILOp = ILOp;
+        }
+
+        public void Emit(AILEmitter Context)
+        {
+            Context.Generator.Emit(ILOp);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Translation/AILOpCodeBranch.cs b/Ryujinx/Cpu/Translation/AILOpCodeBranch.cs
new file mode 100644
index 0000000000..e4caad1ffa
--- /dev/null
+++ b/Ryujinx/Cpu/Translation/AILOpCodeBranch.cs
@@ -0,0 +1,21 @@
+using System.Reflection.Emit;
+
+namespace ChocolArm64.Translation
+{
+    struct AILOpCodeBranch : IAILEmit
+    {
+        private OpCode   ILOp;
+        private AILLabel Label;
+
+        public AILOpCodeBranch(OpCode ILOp, AILLabel Label)
+        {
+            this.ILOp  = ILOp;
+            this.Label = Label;
+        }
+
+        public void Emit(AILEmitter Context)
+        {
+            Context.Generator.Emit(ILOp, Label.GetLabel(Context));
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Translation/AILOpCodeCall.cs b/Ryujinx/Cpu/Translation/AILOpCodeCall.cs
new file mode 100644
index 0000000000..8cd944eb01
--- /dev/null
+++ b/Ryujinx/Cpu/Translation/AILOpCodeCall.cs
@@ -0,0 +1,20 @@
+using System.Reflection;
+using System.Reflection.Emit;
+
+namespace ChocolArm64.Translation
+{
+    struct AILOpCodeCall : IAILEmit
+    {
+        private MethodInfo MthdInfo;
+
+        public AILOpCodeCall(MethodInfo MthdInfo)
+        {
+            this.MthdInfo = MthdInfo;
+        }
+
+        public void Emit(AILEmitter Context)
+        {
+            Context.Generator.Emit(OpCodes.Call, MthdInfo);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Translation/AILOpCodeConst.cs b/Ryujinx/Cpu/Translation/AILOpCodeConst.cs
new file mode 100644
index 0000000000..80150ec5a1
--- /dev/null
+++ b/Ryujinx/Cpu/Translation/AILOpCodeConst.cs
@@ -0,0 +1,81 @@
+using System.Reflection.Emit;
+using System.Runtime.InteropServices;
+
+namespace ChocolArm64.Translation
+{
+    class AILOpCodeConst : IAILEmit
+    {
+        [StructLayout(LayoutKind.Explicit, Size = 8)]
+        private struct ImmVal
+        {
+            [FieldOffset(0)] public int    I4;
+            [FieldOffset(0)] public long   I8;
+            [FieldOffset(0)] public float  R4;
+            [FieldOffset(0)] public double R8;
+        }
+
+        private ImmVal Value;
+
+        private enum ConstType
+        {
+            Int32,
+            Int64,
+            Single,
+            Double
+        }
+
+        private ConstType Type;
+
+        private AILOpCodeConst(ConstType Type)
+        {
+            this.Type = Type;
+        }
+
+        public AILOpCodeConst(int Value) : this(ConstType.Int32)
+        {
+            this.Value = new ImmVal { I4 = Value };
+        }
+
+        public AILOpCodeConst(long Value) : this(ConstType.Int64)
+        {
+            this.Value = new ImmVal { I8 = Value };
+        }
+
+        public AILOpCodeConst(float Value) : this(ConstType.Single)
+        {
+            this.Value = new ImmVal { R4 = Value };
+        }
+
+        public AILOpCodeConst(double Value) : this(ConstType.Double)
+        {
+            this.Value = new ImmVal { R8 = Value };
+        }
+
+        public void Emit(AILEmitter Context)
+        {
+            switch (Type)
+            {
+                case ConstType.Int32: Context.Generator.EmitLdc_I4(Value.I4); break;
+
+                case ConstType.Int64:
+                {
+                    if (Value.I8 >= int.MinValue &&
+                        Value.I8 <= int.MaxValue)
+                    {
+                        Context.Generator.EmitLdc_I4(Value.I4);
+
+                        Context.Generator.Emit(OpCodes.Conv_I8);
+                    }
+                    else
+                    {
+                        Context.Generator.Emit(OpCodes.Ldc_I8, Value.I8);
+                    }
+                    break;
+                }
+
+                case ConstType.Single: Context.Generator.Emit(OpCodes.Ldc_R4, Value.R4); break;
+                case ConstType.Double: Context.Generator.Emit(OpCodes.Ldc_R8, Value.R8); break;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Translation/AILOpCodeLoad.cs b/Ryujinx/Cpu/Translation/AILOpCodeLoad.cs
new file mode 100644
index 0000000000..2169cc7779
--- /dev/null
+++ b/Ryujinx/Cpu/Translation/AILOpCodeLoad.cs
@@ -0,0 +1,82 @@
+using ChocolArm64.State;
+using System;
+using System.Reflection.Emit;
+
+namespace ChocolArm64.Translation
+{
+    struct AILOpCodeLoad : IAILEmit
+    {
+        public int Index { get; private set; }
+
+        public AIoType IoType { get; private set; }
+
+        public Type OperType { get; private set; }
+
+        public AILOpCodeLoad(int Index, AIoType IoType) : this(Index, IoType, null) { }
+
+        public AILOpCodeLoad(int Index, AIoType IoType, Type OperType)
+        {
+            this.IoType   = IoType;
+            this.Index    = Index;
+            this.OperType = OperType;
+        }
+
+        public void Emit(AILEmitter Context)
+        {
+            switch (IoType & AIoType.Mask)
+            {
+                case AIoType.Arg:    EmitLdarg(Context, Index);                       break;
+                case AIoType.Fields: EmitLdfld(Context, Index);                       break;
+                case AIoType.Flag:   EmitLdloc(Context, Index, ARegisterType.Flag);   break;
+                case AIoType.Int:    EmitLdloc(Context, Index, ARegisterType.Int);    break;
+                case AIoType.Vector: EmitLdloc(Context, Index, ARegisterType.Vector); break;
+            }
+        }
+
+        private void EmitLdarg(AILEmitter Context, int Index)
+        {
+            Context.Generator.EmitLdarg(Index);
+        }
+
+        private void EmitLdfld(AILEmitter Context, int Index)
+        {
+            long IntInputs = Context.LocalAlloc.GetIntInputs(Context.GetILBlock(Index));
+            long VecInputs = Context.LocalAlloc.GetVecInputs(Context.GetILBlock(Index));
+
+            LoadLocals(Context, IntInputs, ARegisterType.Int);
+            LoadLocals(Context, VecInputs, ARegisterType.Vector);
+        }
+
+        private void LoadLocals(AILEmitter Context, long Inputs, ARegisterType BaseType)
+        {
+            for (int Bit = 0; Bit < 64; Bit++)
+            {
+                long Mask = 1L << Bit;
+
+                if ((Inputs & Mask) != 0)
+                {
+                    ARegister Reg = AILEmitter.GetRegFromBit(Bit, BaseType);
+
+                    Context.Generator.EmitLdarg(ATranslatedSub.RegistersArgIdx);
+                    Context.Generator.Emit(OpCodes.Ldfld, Reg.GetField());
+
+                    AILConv.EmitConv(
+                        Context,
+                        Context.GetFieldType(Reg.Type),
+                        Context.GetLocalType(Reg));
+
+                    Context.Generator.EmitStloc(Context.GetLocalIndex(Reg));
+                }
+            }
+        }
+
+        private void EmitLdloc(AILEmitter Context, int Index, ARegisterType Type)
+        {
+            ARegister Reg = new ARegister(Index, Type);
+
+            Context.Generator.EmitLdloc(Context.GetLocalIndex(Reg));
+
+            AILConv.EmitConv(Context, Context.GetLocalType(Reg), OperType);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Translation/AILOpCodeLog.cs b/Ryujinx/Cpu/Translation/AILOpCodeLog.cs
new file mode 100644
index 0000000000..1338ca1f3a
--- /dev/null
+++ b/Ryujinx/Cpu/Translation/AILOpCodeLog.cs
@@ -0,0 +1,17 @@
+namespace ChocolArm64.Translation
+{
+    struct AILOpCodeLog : IAILEmit
+    {
+        private string Text;
+
+        public AILOpCodeLog(string Text)
+        {
+            this.Text = Text;
+        }
+
+        public void Emit(AILEmitter Context)
+        {
+            Context.Generator.EmitWriteLine(Text);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Translation/AILOpCodeStore.cs b/Ryujinx/Cpu/Translation/AILOpCodeStore.cs
new file mode 100644
index 0000000000..012e24aecf
--- /dev/null
+++ b/Ryujinx/Cpu/Translation/AILOpCodeStore.cs
@@ -0,0 +1,82 @@
+using ChocolArm64.State;
+using System;
+using System.Reflection.Emit;
+
+namespace ChocolArm64.Translation
+{
+    struct AILOpCodeStore : IAILEmit
+    {
+        public AIoType IoType { get; private set; }
+
+        public Type OperType { get; private set; }
+
+        public int Index { get; private set; }
+
+        public AILOpCodeStore(int Index, AIoType IoType) : this(Index, IoType, null) { }
+
+        public AILOpCodeStore(int Index, AIoType IoType, Type OperType)
+        {
+            this.IoType   = IoType;
+            this.Index    = Index;
+            this.OperType = OperType;
+        }
+
+        public void Emit(AILEmitter Context)
+        {
+            switch (IoType & AIoType.Mask)
+            {
+                case AIoType.Arg:    EmitStarg(Context, Index);                       break;
+                case AIoType.Fields: EmitStfld(Context, Index);                       break;
+                case AIoType.Flag:   EmitStloc(Context, Index, ARegisterType.Flag);   break;
+                case AIoType.Int:    EmitStloc(Context, Index, ARegisterType.Int);    break;
+                case AIoType.Vector: EmitStloc(Context, Index, ARegisterType.Vector); break;
+            }
+        }
+
+        private void EmitStarg(AILEmitter Context, int Index)
+        {
+            Context.Generator.EmitStarg(Index);
+        }
+
+        private void EmitStfld(AILEmitter Context, int Index)
+        {
+            long IntOutputs = Context.LocalAlloc.GetIntOutputs(Context.GetILBlock(Index));
+            long VecOutputs = Context.LocalAlloc.GetVecOutputs(Context.GetILBlock(Index));
+
+            StoreLocals(Context, IntOutputs, ARegisterType.Int);
+            StoreLocals(Context, VecOutputs, ARegisterType.Vector);
+        }
+
+        private void StoreLocals(AILEmitter Context, long Outputs, ARegisterType BaseType)
+        {
+            for (int Bit = 0; Bit < 64; Bit++)
+            {
+                long Mask = 1L << Bit;
+
+                if ((Outputs & Mask) != 0)
+                {
+                    ARegister Reg = AILEmitter.GetRegFromBit(Bit, BaseType);
+
+                    Context.Generator.EmitLdarg(ATranslatedSub.RegistersArgIdx);
+                    Context.Generator.EmitLdloc(Context.GetLocalIndex(Reg));
+
+                    AILConv.EmitConv(
+                        Context,
+                        Context.GetLocalType(Reg),
+                        Context.GetFieldType(Reg.Type));
+
+                    Context.Generator.Emit(OpCodes.Stfld, Reg.GetField());
+                }
+            }
+        }
+
+        private void EmitStloc(AILEmitter Context, int Index, ARegisterType Type)
+        {
+            ARegister Reg = new ARegister(Index, Type);
+
+            AILConv.EmitConv(Context, OperType, Context.GetLocalType(Reg));
+
+            Context.Generator.EmitStloc(Context.GetLocalIndex(Reg));
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Translation/AIoType.cs b/Ryujinx/Cpu/Translation/AIoType.cs
new file mode 100644
index 0000000000..34aa224e5f
--- /dev/null
+++ b/Ryujinx/Cpu/Translation/AIoType.cs
@@ -0,0 +1,18 @@
+using System;
+
+namespace ChocolArm64.Translation
+{
+    [Flags]
+    enum AIoType
+    {
+        Arg,
+        Fields,
+        Flag,
+        Int,
+        Float,
+        Vector,
+        Mask    = 0xff,
+        VectorI = Vector | 1 << 8,
+        VectorF = Vector | 1 << 9
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Translation/ALocalAlloc.cs b/Ryujinx/Cpu/Translation/ALocalAlloc.cs
new file mode 100644
index 0000000000..0661ddc8dd
--- /dev/null
+++ b/Ryujinx/Cpu/Translation/ALocalAlloc.cs
@@ -0,0 +1,231 @@
+using System.Collections.Generic;
+
+namespace ChocolArm64.Translation
+{
+    class ALocalAlloc
+    {
+        private class PathIo
+        {
+            private Dictionary<AILBlock, long> AllInputs;
+            private Dictionary<AILBlock, long> CmnOutputs;
+
+            private long AllOutputs;
+
+            public PathIo()
+            {
+                AllInputs  = new Dictionary<AILBlock, long>();
+                CmnOutputs = new Dictionary<AILBlock, long>();
+            }
+
+            public PathIo(AILBlock Root, long Inputs, long Outputs) : this()
+            {
+                Set(Root, Inputs, Outputs);
+            }
+
+            public void Set(AILBlock Root, long Inputs, long Outputs)
+            {
+                if (!AllInputs.TryAdd(Root, Inputs))
+                {
+                    AllInputs[Root] |= Inputs;
+                }
+
+                if (!CmnOutputs.TryAdd(Root, Outputs))
+                {
+                    CmnOutputs[Root] &= Outputs;
+                }
+
+                AllOutputs |= Outputs;
+            }
+
+            public long GetInputs(AILBlock Root)
+            {
+                if (AllInputs.TryGetValue(Root, out long Inputs))
+                {
+                    return Inputs | (AllOutputs & ~CmnOutputs[Root]);
+                }
+
+                return 0;
+            }
+
+            public long GetOutputs()
+            {
+                return AllOutputs;
+            }
+        }
+
+        private Dictionary<AILBlock, PathIo> IntPaths;
+        private Dictionary<AILBlock, PathIo> VecPaths;
+
+        private struct BlockIo
+        {
+            public AILBlock Block;
+            public AILBlock Entry;
+
+            public long IntInputs;
+            public long VecInputs;
+            public long IntOutputs;
+            public long VecOutputs;
+        }
+
+        private const int MaxOptGraphLength = 120;
+
+        public ALocalAlloc(AILBlock[] Graph, AILBlock Root)
+        {
+            IntPaths = new Dictionary<AILBlock, PathIo>();
+            VecPaths = new Dictionary<AILBlock, PathIo>();
+
+            if (Graph.Length < MaxOptGraphLength)
+            {
+                InitializeOptimal(Graph, Root);
+            }
+            else
+            {
+                InitializeFast(Graph);
+            }
+        }
+
+        private void InitializeOptimal(AILBlock[] Graph, AILBlock Root)
+        {
+            //This will go through all possible paths on the graph,
+            //and store all inputs/outputs for each block. A register
+            //that was previously written to already is not considered an input.
+            //When a block can be reached by more than one path, then the
+            //output from all paths needs to be set for this block, and
+            //only outputs present in all of the parent blocks can be considered
+            //when doing input elimination. Each block chain have a root, that's where
+            //the code starts executing. They are present on the subroutine start point,
+            //and on call return points too (address written to X30 by BL).
+            HashSet<BlockIo> Visited = new HashSet<BlockIo>();
+
+            Queue<BlockIo> Unvisited = new Queue<BlockIo>();
+
+            void Enqueue(BlockIo Block)
+            {
+                if (!Visited.Contains(Block))
+                {
+                    Unvisited.Enqueue(Block);
+
+                    Visited.Add(Block);
+                }
+            }
+
+            Enqueue(new BlockIo()
+            {
+                Block = Root,
+                Entry = Root
+            });
+
+            while (Unvisited.Count > 0)
+            {
+                BlockIo Current = Unvisited.Dequeue();
+
+                Current.IntInputs  |= Current.Block.IntInputs & ~Current.IntOutputs;
+                Current.VecInputs  |= Current.Block.VecInputs & ~Current.VecOutputs;
+                Current.IntOutputs |= Current.Block.IntOutputs;
+                Current.VecOutputs |= Current.Block.VecOutputs;
+
+                //Check if this is a exit block
+                //(a block that returns or calls another sub).
+                if ((Current.Block.Next   == null &&
+                     Current.Block.Branch == null) || Current.Block.HasStateStore)
+                {
+                    if (!IntPaths.TryGetValue(Current.Block, out PathIo IntPath))
+                    {
+                        IntPaths.Add(Current.Block, IntPath = new PathIo());
+                    }
+
+                    if (!VecPaths.TryGetValue(Current.Block, out PathIo VecPath))
+                    {
+                        VecPaths.Add(Current.Block, VecPath = new PathIo());
+                    }
+
+                    IntPath.Set(Current.Entry, Current.IntInputs, Current.IntOutputs);
+                    VecPath.Set(Current.Entry, Current.VecInputs, Current.VecOutputs);
+                }
+
+                void EnqueueFromCurrent(AILBlock Block, bool RetTarget)
+                {
+                    BlockIo BlkIO = new BlockIo() { Block = Block };
+
+                    if (RetTarget)
+                    {
+                        BlkIO.Entry      = Block;
+                        BlkIO.IntInputs  = 0;
+                        BlkIO.VecInputs  = 0;
+                        BlkIO.IntOutputs = 0;
+                        BlkIO.VecOutputs = 0;
+                    }
+                    else
+                    {
+                        BlkIO.Entry      = Current.Entry;
+                        BlkIO.IntInputs  = Current.IntInputs;
+                        BlkIO.VecInputs  = Current.VecInputs;
+                        BlkIO.IntOutputs = Current.IntOutputs;
+                        BlkIO.VecOutputs = Current.VecOutputs;
+                    }
+
+                    Enqueue(BlkIO);
+                }
+
+                if (Current.Block.Next != null)
+                {
+                    EnqueueFromCurrent(Current.Block.Next, Current.Block.HasStateStore);
+                }
+
+                if (Current.Block.Branch != null)
+                {
+                    EnqueueFromCurrent(Current.Block.Branch, false);
+                }
+            }
+        }
+
+        private void InitializeFast(AILBlock[] Graph)
+        {
+            //This is WAY faster than InitializeOptimal, but results in
+            //uneeded loads and stores, so the resulting code will be slower.
+            long IntInputs  = 0;
+            long IntOutputs = 0;
+            long VecInputs  = 0;
+            long VecOutputs = 0;
+
+            foreach (AILBlock Block in Graph)
+            {
+                IntInputs  |= Block.IntInputs;
+                IntOutputs |= Block.IntOutputs;
+                VecInputs  |= Block.VecInputs;
+                VecOutputs |= Block.VecOutputs;
+            }
+
+            //It's possible that not all code paths writes to those output registers,
+            //in those cases if we attempt to write an output registers that was
+            //not written, we will be just writing zero and messing up the old register value.
+            //So we just need to ensure that all outputs are loaded.
+            IntInputs |= IntOutputs;
+            VecInputs |= VecOutputs;
+
+            foreach (AILBlock Block in Graph)
+            {
+                IntPaths.Add(Block, new PathIo(Block, IntInputs, IntOutputs));
+                VecPaths.Add(Block, new PathIo(Block, VecInputs, VecOutputs));
+            }
+        }
+
+        public long GetIntInputs(AILBlock Root) => GetInputsImpl(Root, IntPaths.Values);
+        public long GetVecInputs(AILBlock Root) => GetInputsImpl(Root, VecPaths.Values);
+
+        private long GetInputsImpl(AILBlock Root, IEnumerable<PathIo> Values)
+        {
+            long Inputs = 0;
+
+            foreach (PathIo Path in Values)
+            {
+                Inputs |= Path.GetInputs(Root);
+            }
+
+            return Inputs;
+        }
+
+        public long GetIntOutputs(AILBlock Block) => IntPaths[Block].GetOutputs();
+        public long GetVecOutputs(AILBlock Block) => VecPaths[Block].GetOutputs();
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Translation/IAILEmit.cs b/Ryujinx/Cpu/Translation/IAILEmit.cs
new file mode 100644
index 0000000000..6e4e9a7855
--- /dev/null
+++ b/Ryujinx/Cpu/Translation/IAILEmit.cs
@@ -0,0 +1,7 @@
+namespace ChocolArm64.Translation
+{
+    interface IAILEmit
+    {
+        void Emit(AILEmitter Context);
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Cpu/Translation/ILGeneratorEx.cs b/Ryujinx/Cpu/Translation/ILGeneratorEx.cs
new file mode 100644
index 0000000000..abb35ec3f1
--- /dev/null
+++ b/Ryujinx/Cpu/Translation/ILGeneratorEx.cs
@@ -0,0 +1,129 @@
+using System;
+
+namespace ChocolArm64
+{
+    using System.Reflection.Emit;
+    
+    static class ILGeneratorEx
+    {
+        public static void EmitLdc_I4(this ILGenerator Generator,int Value)
+        {
+            switch (Value)
+            {
+                case  0: Generator.Emit(OpCodes.Ldc_I4_0);      break;
+                case  1: Generator.Emit(OpCodes.Ldc_I4_1);      break;
+                case  2: Generator.Emit(OpCodes.Ldc_I4_2);      break;
+                case  3: Generator.Emit(OpCodes.Ldc_I4_3);      break;
+                case  4: Generator.Emit(OpCodes.Ldc_I4_4);      break;
+                case  5: Generator.Emit(OpCodes.Ldc_I4_5);      break;
+                case  6: Generator.Emit(OpCodes.Ldc_I4_6);      break;
+                case  7: Generator.Emit(OpCodes.Ldc_I4_7);      break;
+                case  8: Generator.Emit(OpCodes.Ldc_I4_8);      break;
+                case -1: Generator.Emit(OpCodes.Ldc_I4_M1);     break;
+                default: Generator.Emit(OpCodes.Ldc_I4, Value); break;
+            }
+        }
+
+        public static void EmitLdarg(this ILGenerator Generator, int Index)
+        {
+            switch (Index)
+            {
+                case 0:  Generator.Emit(OpCodes.Ldarg_0); break;
+                case 1:  Generator.Emit(OpCodes.Ldarg_1); break;
+                case 2:  Generator.Emit(OpCodes.Ldarg_2); break;
+                case 3:  Generator.Emit(OpCodes.Ldarg_3); break;
+
+                default:
+                    if ((uint)Index <= byte.MaxValue)
+                    {
+                        Generator.Emit(OpCodes.Ldarg_S, (byte)Index);
+                    }
+                    else if ((uint)Index < ushort.MaxValue)
+                    {
+                        Generator.Emit(OpCodes.Ldarg, (short)Index);
+                    }
+                    else
+                    {
+                        throw new ArgumentOutOfRangeException(nameof(Index));
+                    }
+                    break;
+            }
+        }
+
+        public static void EmitStarg(this ILGenerator Generator, int Index)
+        {
+            if ((uint)Index <= byte.MaxValue)
+            {
+                Generator.Emit(OpCodes.Starg_S, (byte)Index);
+            }
+            else if ((uint)Index < ushort.MaxValue)
+            {
+                Generator.Emit(OpCodes.Starg, (short)Index);
+            }
+            else
+            {
+                throw new ArgumentOutOfRangeException(nameof(Index));
+            } 
+        }
+
+        public static void EmitLdloc(this ILGenerator Generator, int Index)
+        {
+            switch (Index)
+            {
+                case 0:  Generator.Emit(OpCodes.Ldloc_0); break;
+                case 1:  Generator.Emit(OpCodes.Ldloc_1); break;
+                case 2:  Generator.Emit(OpCodes.Ldloc_2); break;
+                case 3:  Generator.Emit(OpCodes.Ldloc_3); break;
+
+                default:
+                    if ((uint)Index <= byte.MaxValue)
+                    {
+                        Generator.Emit(OpCodes.Ldloc_S, (byte)Index);
+                    }
+                    else if ((uint)Index < ushort.MaxValue)
+                    {
+                        Generator.Emit(OpCodes.Ldloc, (short)Index);
+                    }
+                    else
+                    {
+                        throw new ArgumentOutOfRangeException(nameof(Index));
+                    }
+                    break;
+            }            
+        }
+
+        public static void EmitStloc(this ILGenerator Generator, int Index)
+        {
+            switch (Index)
+            {
+                case 0:  Generator.Emit(OpCodes.Stloc_0); break;
+                case 1:  Generator.Emit(OpCodes.Stloc_1); break;
+                case 2:  Generator.Emit(OpCodes.Stloc_2); break;
+                case 3:  Generator.Emit(OpCodes.Stloc_3); break;
+
+                default:
+                    if ((uint)Index <= byte.MaxValue)
+                    {
+                        Generator.Emit(OpCodes.Stloc_S, (byte)Index);
+                    }
+                    else if ((uint)Index < ushort.MaxValue)
+                    {
+                        Generator.Emit(OpCodes.Stloc, (short)Index);
+                    }
+                    else
+                    {
+                        throw new ArgumentOutOfRangeException(nameof(Index));
+                    }
+                    break;
+            }
+        }
+
+        public static void EmitLdargSeq(this ILGenerator Generator, int Count)
+        {
+            for (int Index = 0; Index < Count; Index++)
+            {
+                Generator.EmitLdarg(Index);
+            }         
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Gal/GalPrimitiveType.cs b/Ryujinx/Gal/GalPrimitiveType.cs
new file mode 100644
index 0000000000..7b6d99a0bd
--- /dev/null
+++ b/Ryujinx/Gal/GalPrimitiveType.cs
@@ -0,0 +1,21 @@
+namespace Gal
+{
+    public enum GalPrimitiveType
+    {
+        Points                 = 0x0,
+        Lines                  = 0x1,
+        LineLoop               = 0x2,
+        LineStrip              = 0x3,
+        Triangles              = 0x4,
+        TriangleStrip          = 0x5,
+        TriangleFan            = 0x6,
+        Quads                  = 0x7,
+        QuadStrip              = 0x8,
+        Polygon                = 0x9,
+        LinesAdjacency         = 0xa,
+        LineStripAdjacency     = 0xb,
+        TrianglesAdjacency     = 0xc,
+        TriangleStripAdjacency = 0xd,
+        Patches                = 0xe
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Gal/GalVertexAttrib.cs b/Ryujinx/Gal/GalVertexAttrib.cs
new file mode 100644
index 0000000000..bbc3263375
--- /dev/null
+++ b/Ryujinx/Gal/GalVertexAttrib.cs
@@ -0,0 +1,33 @@
+namespace Gal
+{
+    public struct GalVertexAttrib
+    {
+        public int  Index   { get; private set; }
+        public int  Buffer  { get; private set; }
+        public bool IsConst { get; private set; }
+        public int  Offset  { get; private set; }
+
+        public GalVertexAttribSize Size { get; private set; }
+        public GalVertexAttribType Type { get; private set; }
+
+        public bool IsBgra { get; private set; }
+
+        public GalVertexAttrib(
+            int                 Index,
+            int                 Buffer,
+            bool                IsConst,
+            int                 Offset,
+            GalVertexAttribSize Size,
+            GalVertexAttribType Type,
+            bool                IsBgra)
+        {
+            this.Index   = Index;
+            this.Buffer  = Buffer;
+            this.IsConst = IsConst;
+            this.Offset  = Offset;
+            this.Size    = Size;
+            this.Type    = Type;
+            this.IsBgra  = IsBgra;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Gal/GalVertexAttribSize.cs b/Ryujinx/Gal/GalVertexAttribSize.cs
new file mode 100644
index 0000000000..11f0470c28
--- /dev/null
+++ b/Ryujinx/Gal/GalVertexAttribSize.cs
@@ -0,0 +1,20 @@
+namespace Gal
+{
+    public enum GalVertexAttribSize
+    {
+        _32_32_32_32 = 0x1,
+        _32_32_32    = 0x2,
+        _16_16_16_16 = 0x3,
+        _32_32       = 0x4,
+        _16_16_16    = 0x5,
+        _8_8_8_8     = 0xa,
+        _16_16       = 0xf,
+        _32          = 0x12,
+        _8_8_8       = 0x13,
+        _8_8         = 0x18,
+        _16          = 0x1b,
+        _8           = 0x1d,
+        _10_10_10_2  = 0x30,
+        _11_11_10    = 0x31
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Gal/GalVertexAttribType.cs b/Ryujinx/Gal/GalVertexAttribType.cs
new file mode 100644
index 0000000000..c0ed59fb27
--- /dev/null
+++ b/Ryujinx/Gal/GalVertexAttribType.cs
@@ -0,0 +1,13 @@
+namespace Gal
+{
+    public enum GalVertexAttribType
+    {
+        Snorm   = 1,
+        Unorm   = 2,
+        Sint    = 3,
+        Uint    = 4,
+        Uscaled = 5,
+        Sscaled = 6,
+        Float   = 7
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Gal/IGalRenderer.cs b/Ryujinx/Gal/IGalRenderer.cs
new file mode 100644
index 0000000000..306d0d5111
--- /dev/null
+++ b/Ryujinx/Gal/IGalRenderer.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace Gal
+{
+    public interface IGalRenderer
+    {
+        long FrameBufferPtr { get; set; }
+
+        void QueueAction(Action ActionMthd);
+        void RunActions();
+
+        void Render();
+        void SendVertexBuffer(int Index, byte[] Buffer, int Stride, GalVertexAttrib[] Attribs);
+        void SendR8G8B8A8Texture(int Index, byte[] Buffer, int Width, int Height);
+        void BindTexture(int Index);
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Gal/OpenGL/OpenGLRenderer.cs b/Ryujinx/Gal/OpenGL/OpenGLRenderer.cs
new file mode 100644
index 0000000000..72ad6f706f
--- /dev/null
+++ b/Ryujinx/Gal/OpenGL/OpenGLRenderer.cs
@@ -0,0 +1,282 @@
+using OpenTK.Graphics.OpenGL;
+using System;
+using System.Collections.Generic;
+
+namespace Gal.OpenGL
+{
+    public class OpenGLRenderer : IGalRenderer
+    {
+        private struct VertexBuffer
+        {
+            public int VaoHandle;
+            public int VboHandle;
+
+            public int PrimCount;
+        }
+
+        private struct Texture
+        {
+            public int Handle;
+        }
+
+        private List<VertexBuffer> VertexBuffers;
+
+        private Texture[] Textures;
+
+        private Queue<Action> ActionsQueue;
+
+        public long FrameBufferPtr { get; set; }
+
+        public OpenGLRenderer()
+        {
+            VertexBuffers = new List<VertexBuffer>();
+
+            Textures = new Texture[8];
+
+            ActionsQueue = new Queue<Action>();
+        }
+
+        public void QueueAction(Action ActionMthd)
+        {
+            ActionsQueue.Enqueue(ActionMthd);
+        }
+
+        public void RunActions()
+        {
+            while (ActionsQueue.Count > 0)
+            {
+                ActionsQueue.Dequeue()();
+            }
+        }
+
+        public void Render()
+        {
+            for (int Index = 0; Index < VertexBuffers.Count; Index++)
+            {
+                VertexBuffer Vb = VertexBuffers[Index];
+
+                if (Vb.VaoHandle != 0 &&
+                    Vb.PrimCount != 0)
+                {
+                    GL.BindVertexArray(Vb.VaoHandle);
+                    GL.DrawArrays(PrimitiveType.TriangleStrip, 0, Vb.PrimCount);
+                }
+            }
+            
+        }
+
+        public void SendVertexBuffer(int Index, byte[] Buffer, int Stride, GalVertexAttrib[] Attribs)
+        {
+            if (Index < 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(Index));
+            }
+
+            if (Buffer.Length == 0 || Stride == 0)
+            {
+                return;
+            }
+
+            EnsureVbInitialized(Index);
+
+            VertexBuffer Vb = VertexBuffers[Index];
+
+            Vb.PrimCount = Buffer.Length / Stride;
+
+            VertexBuffers[Index] = Vb;
+
+            IntPtr Length = new IntPtr(Buffer.Length);
+
+            GL.BindBuffer(BufferTarget.ArrayBuffer, Vb.VboHandle);
+            GL.BufferData(BufferTarget.ArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
+            GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
+
+            GL.BindVertexArray(Vb.VaoHandle);
+
+            for (int Attr = 0; Attr < 16; Attr++)
+            {
+                GL.DisableVertexAttribArray(Attr);
+            }
+
+            foreach (GalVertexAttrib Attrib in Attribs)
+            {
+                if (Attrib.Index >= 3) break;
+
+                GL.EnableVertexAttribArray(Attrib.Index);
+
+                GL.BindBuffer(BufferTarget.ArrayBuffer, Vb.VboHandle);
+
+                int Size = 0;
+
+                switch (Attrib.Size)
+                {
+                    case GalVertexAttribSize._8:
+                    case GalVertexAttribSize._16:
+                    case GalVertexAttribSize._32:
+                        Size = 1;
+                        break;
+                    case GalVertexAttribSize._8_8:
+                    case GalVertexAttribSize._16_16:
+                    case GalVertexAttribSize._32_32:
+                        Size = 2;
+                        break;
+                    case GalVertexAttribSize._8_8_8:
+                    case GalVertexAttribSize._11_11_10:
+                    case GalVertexAttribSize._16_16_16:
+                    case GalVertexAttribSize._32_32_32:
+                        Size = 3;
+                        break;
+                    case GalVertexAttribSize._8_8_8_8:
+                    case GalVertexAttribSize._10_10_10_2:
+                    case GalVertexAttribSize._16_16_16_16:
+                    case GalVertexAttribSize._32_32_32_32:
+                        Size = 4;
+                        break;
+                }
+
+                bool Signed =
+                    Attrib.Type == GalVertexAttribType.Snorm ||
+                    Attrib.Type == GalVertexAttribType.Sint  ||
+                    Attrib.Type == GalVertexAttribType.Sscaled;
+
+                bool Normalize =
+                    Attrib.Type == GalVertexAttribType.Snorm ||
+                    Attrib.Type == GalVertexAttribType.Unorm;
+
+                VertexAttribPointerType Type = 0;
+
+                switch (Attrib.Type)
+                {
+                    case GalVertexAttribType.Snorm:
+                    case GalVertexAttribType.Unorm:
+                    case GalVertexAttribType.Sint:
+                    case GalVertexAttribType.Uint:
+                    case GalVertexAttribType.Uscaled:
+                    case GalVertexAttribType.Sscaled:                    
+                    {
+                        switch (Attrib.Size)
+                        {
+                            case GalVertexAttribSize._8:
+                            case GalVertexAttribSize._8_8:
+                            case GalVertexAttribSize._8_8_8:
+                            case GalVertexAttribSize._8_8_8_8:
+                            {
+                                Type = Signed
+                                    ? VertexAttribPointerType.Byte
+                                    : VertexAttribPointerType.UnsignedByte;
+
+                                break;
+                            }
+
+                            case GalVertexAttribSize._16:
+                            case GalVertexAttribSize._16_16:
+                            case GalVertexAttribSize._16_16_16:
+                            case GalVertexAttribSize._16_16_16_16:
+                            {
+                                Type = Signed
+                                    ? VertexAttribPointerType.Short
+                                    : VertexAttribPointerType.UnsignedShort;
+
+                                break;
+                            }
+
+                            case GalVertexAttribSize._10_10_10_2:
+                            case GalVertexAttribSize._11_11_10:
+                            case GalVertexAttribSize._32:
+                            case GalVertexAttribSize._32_32:
+                            case GalVertexAttribSize._32_32_32:
+                            case GalVertexAttribSize._32_32_32_32:
+                            {
+                                Type = Signed
+                                    ? VertexAttribPointerType.Int
+                                    : VertexAttribPointerType.UnsignedInt;
+
+                                break;
+                            }
+                        }
+
+                        break;
+                    }
+
+                    case GalVertexAttribType.Float:
+                    {
+                        Type = VertexAttribPointerType.Float;
+
+                        break;
+                    }
+                }
+
+                GL.VertexAttribPointer(
+                    Attrib.Index,
+                    Size,
+                    Type,
+                    Normalize,
+                    Stride,
+                    Attrib.Offset);
+            }
+
+            GL.BindVertexArray(0);
+        }
+
+        public void SendR8G8B8A8Texture(int Index, byte[] Buffer, int Width, int Height)
+        {
+            EnsureTexInitialized(Index);
+
+            GL.BindTexture(TextureTarget.Texture2D, Textures[Index].Handle);
+            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
+            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
+            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,
+                Buffer);
+        }
+
+        public void BindTexture(int Index)
+        {
+            GL.ActiveTexture(TextureUnit.Texture0 + Index);
+
+            GL.BindTexture(TextureTarget.Texture2D, Textures[Index].Handle);            
+        }
+
+        private void EnsureVbInitialized(int VbIndex)
+        {
+            while (VbIndex >= VertexBuffers.Count)
+            {
+                VertexBuffers.Add(new VertexBuffer());
+            }
+
+            VertexBuffer Vb = VertexBuffers[VbIndex];
+
+            if (Vb.VaoHandle == 0)
+            {
+                Vb.VaoHandle = GL.GenVertexArray();
+            }
+
+            if (Vb.VboHandle == 0)
+            {
+                Vb.VboHandle = GL.GenBuffer();
+            }
+
+            VertexBuffers[VbIndex] = Vb;
+        }
+
+        private void EnsureTexInitialized(int TexIndex)
+        {
+            Texture Tex = Textures[TexIndex];
+
+            if (Tex.Handle == 0)
+            {
+                Tex.Handle = GL.GenTexture();
+            }
+
+            Textures[TexIndex] = Tex;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Gpu/BCn.cs b/Ryujinx/Gpu/BCn.cs
new file mode 100644
index 0000000000..bf782d167f
--- /dev/null
+++ b/Ryujinx/Gpu/BCn.cs
@@ -0,0 +1,468 @@
+using System;
+using System.Drawing;
+
+namespace Ryujinx.Gpu
+{
+    static class BCn
+    {
+        public static byte[] DecodeBC1(NsGpuTexture Tex, int Offset)
+        {
+            int W = (Tex.Width  + 3) / 4;
+            int H = (Tex.Height + 3) / 4;
+
+            byte[] Output = new byte[W * H * 64];
+
+            SwizzleAddr Swizzle = new SwizzleAddr(W, H, 8);
+
+            for (int Y = 0; Y < H; Y++)
+            {
+                for (int X = 0; X < W; X++)
+                {
+                    int IOffs = Offset + Swizzle.GetSwizzledAddress64(X, Y) * 8;
+
+                    byte[] Tile = BCnDecodeTile(Tex.Data, IOffs, true);
+
+                    int TOffset = 0;
+
+                    for (int TY = 0; TY < 4; TY++)
+                    {
+                        for (int TX = 0; TX < 4; TX++)
+                        {
+                            int OOffset = (X * 4 + TX + (Y * 4 + TY) * W * 4) * 4;
+
+                            Output[OOffset + 0] = Tile[TOffset + 0];
+                            Output[OOffset + 1] = Tile[TOffset + 1];
+                            Output[OOffset + 2] = Tile[TOffset + 2];
+                            Output[OOffset + 3] = Tile[TOffset + 3];
+
+                            TOffset += 4;
+                        }
+                    }
+                }
+            }
+
+            return Output;
+        }
+
+        public static byte[] DecodeBC2(NsGpuTexture Tex, int Offset)
+        {
+            int W = (Tex.Width  + 3) / 4;
+            int H = (Tex.Height + 3) / 4;
+
+            byte[] Output = new byte[W * H * 64];
+
+            SwizzleAddr Swizzle = new SwizzleAddr(W, H, 4);
+
+            for (int Y = 0; Y < H; Y++)
+            {
+                for (int X = 0; X < W; X++)
+                {
+                    int IOffs = Offset + Swizzle.GetSwizzledAddress128(X, Y) * 16;
+
+                    byte[] Tile = BCnDecodeTile(Tex.Data, IOffs + 8, false);
+
+                    int AlphaLow  = Get32(Tex.Data, IOffs + 0);
+                    int AlphaHigh = Get32(Tex.Data, IOffs + 4);
+
+                    ulong AlphaCh = (uint)AlphaLow | (ulong)AlphaHigh << 32;
+
+                    int TOffset = 0;
+
+                    for (int TY = 0; TY < 4; TY++)
+                    {
+                        for (int TX = 0; TX < 4; TX++)
+                        {
+                            ulong Alpha = (AlphaCh >> (TY * 16 + TX * 4)) & 0xf;
+
+                            int OOffset = (X * 4 + TX + (Y * 4 + TY) * W * 4) * 4;
+
+                            Output[OOffset + 0] = Tile[TOffset + 0];
+                            Output[OOffset + 1] = Tile[TOffset + 1];
+                            Output[OOffset + 2] = Tile[TOffset + 2];
+                            Output[OOffset + 3] = (byte)(Alpha | (Alpha << 4));
+
+                            TOffset += 4;
+                        }
+                    }
+                }
+            }
+
+            return Output;
+        }
+
+        public static byte[] DecodeBC3(NsGpuTexture Tex, int Offset)
+        {
+            int W = (Tex.Width  + 3) / 4;
+            int H = (Tex.Height + 3) / 4;
+
+            byte[] Output = new byte[W * H * 64];
+
+            SwizzleAddr Swizzle = new SwizzleAddr(W, H, 4);
+
+            for (int Y = 0; Y < H; Y++)
+            {
+                for (int X = 0; X < W; X++)
+                {
+                    int IOffs = Offset + Swizzle.GetSwizzledAddress128(X, Y) * 16;
+
+                    byte[] Tile = BCnDecodeTile(Tex.Data, IOffs + 8, false);
+
+                    byte[] Alpha = new byte[8];
+
+                    Alpha[0] = Tex.Data[IOffs + 0];
+                    Alpha[1] = Tex.Data[IOffs + 1];
+
+                    CalculateBC3Alpha(Alpha);
+
+                    int AlphaLow  = Get32(Tex.Data, IOffs + 2);
+                    int AlphaHigh = Get16(Tex.Data, IOffs + 6);
+
+                    ulong AlphaCh = (uint)AlphaLow | (ulong)AlphaHigh << 32;
+
+                    int TOffset = 0;
+
+                    for (int TY = 0; TY < 4; TY++)
+                    {
+                        for (int TX = 0; TX < 4; TX++)
+                        {
+                            int OOffset = (X * 4 + TX + (Y * 4 + TY) * W * 4) * 4;
+
+                            byte AlphaPx = Alpha[(AlphaCh >> (TY * 12 + TX * 3)) & 7];
+
+                            Output[OOffset + 0] = Tile[TOffset + 0];
+                            Output[OOffset + 1] = Tile[TOffset + 1];
+                            Output[OOffset + 2] = Tile[TOffset + 2];
+                            Output[OOffset + 3] = AlphaPx;
+
+                            TOffset += 4;
+                        }
+                    }
+                }
+            }
+
+            return Output;
+        }
+
+        public static byte[] DecodeBC4(NsGpuTexture Tex, int Offset)
+        {
+            int W = (Tex.Width  + 3) / 4;
+            int H = (Tex.Height + 3) / 4;
+
+            byte[] Output = new byte[W * H * 64];
+
+            SwizzleAddr Swizzle = new SwizzleAddr(W, H, 8);
+
+            for (int Y = 0; Y < H; Y++)
+            {
+                for (int X = 0; X < W; X++)
+                {
+                    int IOffs = Swizzle.GetSwizzledAddress64(X, Y) * 8;
+
+                    byte[] Red = new byte[8];
+
+                    Red[0] = Tex.Data[IOffs + 0];
+                    Red[1] = Tex.Data[IOffs + 1];
+
+                    CalculateBC3Alpha(Red);
+
+                    int RedLow  = Get32(Tex.Data, IOffs + 2);
+                    int RedHigh = Get16(Tex.Data, IOffs + 6);
+
+                    ulong RedCh = (uint)RedLow | (ulong)RedHigh << 32;
+
+                    int TOffset = 0;
+
+                    for (int TY = 0; TY < 4; TY++)
+                    {
+                        for (int TX = 0; TX < 4; TX++)
+                        {
+                            int OOffset = (X * 4 + TX + (Y * 4 + TY) * W * 4) * 4;
+
+                            byte RedPx = Red[(RedCh >> (TY * 12 + TX * 3)) & 7];
+
+                            Output[OOffset + 0] = RedPx;
+                            Output[OOffset + 1] = RedPx;
+                            Output[OOffset + 2] = RedPx;
+                            Output[OOffset + 3] = 0xff;
+
+                            TOffset += 4;
+                        }
+                    }
+                }
+            }
+
+            return Output;
+        }
+
+        public static byte[] DecodeBC5(NsGpuTexture Tex, int Offset, bool SNorm)
+        {
+            int W = (Tex.Width  + 3) / 4;
+            int H = (Tex.Height + 3) / 4;
+
+            byte[] Output = new byte[W * H * 64];
+
+            SwizzleAddr Swizzle = new SwizzleAddr(W, H, 4);
+
+            for (int Y = 0; Y < H; Y++)
+            {
+                for (int X = 0; X < W; X++)
+                {
+                    int IOffs = Swizzle.GetSwizzledAddress128(X, Y) * 16;
+
+                    byte[] Red   = new byte[8];
+                    byte[] Green = new byte[8];
+
+                    Red[0]   = Tex.Data[IOffs + 0];
+                    Red[1]   = Tex.Data[IOffs + 1];
+
+                    Green[0] = Tex.Data[IOffs + 8];
+                    Green[1] = Tex.Data[IOffs + 9];
+
+                    if (SNorm)
+                    {
+                        CalculateBC3AlphaS(Red);
+                        CalculateBC3AlphaS(Green);
+                    }
+                    else
+                    {
+                        CalculateBC3Alpha(Red);
+                        CalculateBC3Alpha(Green);
+                    }
+
+                    int RedLow    = Get32(Tex.Data, IOffs + 2);
+                    int RedHigh   = Get16(Tex.Data, IOffs + 6);
+
+                    int GreenLow  = Get32(Tex.Data, IOffs + 10);
+                    int GreenHigh = Get16(Tex.Data, IOffs + 14);
+
+                    ulong RedCh   = (uint)RedLow   | (ulong)RedHigh   << 32;
+                    ulong GreenCh = (uint)GreenLow | (ulong)GreenHigh << 32;
+
+                    int TOffset = 0;
+
+                    if (SNorm)
+                    {
+                        for (int TY = 0; TY < 4; TY++)
+                        {
+                            for (int TX = 0; TX < 4; TX++)
+                            {
+                                int Shift = TY * 12 + TX * 3;
+
+                                int OOffset = (X * 4 + TX + (Y * 4 + TY) * W * 4) * 4;
+
+                                byte RedPx   = Red  [(RedCh   >> Shift) & 7];
+                                byte GreenPx = Green[(GreenCh >> Shift) & 7];
+
+                                RedPx   += 0x80;
+                                GreenPx += 0x80;
+
+                                float NX = (RedPx   / 255f) * 2 - 1;
+                                float NY = (GreenPx / 255f) * 2 - 1;
+
+                                float NZ = (float)Math.Sqrt(1 - (NX * NX + NY * NY));
+
+                                Output[OOffset + 0] = Clamp((NZ + 1) * 0.5f);
+                                Output[OOffset + 1] = Clamp((NY + 1) * 0.5f);
+                                Output[OOffset + 2] = Clamp((NX + 1) * 0.5f);
+                                Output[OOffset + 3] = 0xff;
+
+                                TOffset += 4;
+                            }
+                        }
+                    }
+                    else
+                    {
+                        for (int TY = 0; TY < 4; TY++)
+                        {
+                            for (int TX = 0; TX < 4; TX++)
+                            {
+                                int Shift = TY * 12 + TX * 3;
+
+                                int OOffset = (X * 4 + TX + (Y * 4 + TY) * W * 4) * 4;
+
+                                byte RedPx   = Red  [(RedCh   >> Shift) & 7];
+                                byte GreenPx = Green[(GreenCh >> Shift) & 7];
+
+                                Output[OOffset + 0] = RedPx;
+                                Output[OOffset + 1] = RedPx;
+                                Output[OOffset + 2] = RedPx;
+                                Output[OOffset + 3] = GreenPx;
+
+                                TOffset += 4;
+                            }
+                        }
+                    }
+                }
+            }
+
+            return Output;
+        }
+
+        private static byte Clamp(float Value)
+        {
+            if (Value > 1)
+            {
+                return 0xff;
+            }
+            else if (Value < 0)
+            {
+                return 0;
+            }
+            else
+            {
+                return (byte)(Value * 0xff);
+            }
+        }
+
+        private static void CalculateBC3Alpha(byte[] Alpha)
+        {
+            for (int i = 2; i < 8; i++)
+            {
+                if (Alpha[0] > Alpha[1])
+                {
+                    Alpha[i] = (byte)(((8 - i) * Alpha[0] + (i - 1) * Alpha[1]) / 7);
+                }
+                else if (i < 6)
+                {
+                    Alpha[i] = (byte)(((6 - i) * Alpha[0] + (i - 1) * Alpha[1]) / 7);
+                }
+                else if (i == 6)
+                {
+                    Alpha[i] = 0;
+                }
+                else /* i == 7 */
+                {
+                    Alpha[i] = 0xff;
+                }
+            }
+        }
+
+        private static void CalculateBC3AlphaS(byte[] Alpha)
+        {
+            for (int i = 2; i < 8; i++)
+            {
+                if ((sbyte)Alpha[0] > (sbyte)Alpha[1])
+                {
+                    Alpha[i] = (byte)(((8 - i) * (sbyte)Alpha[0] + (i - 1) * (sbyte)Alpha[1]) / 7);
+                }
+                else if (i < 6)
+                {
+                    Alpha[i] = (byte)(((6 - i) * (sbyte)Alpha[0] + (i - 1) * (sbyte)Alpha[1]) / 7);
+                }
+                else if (i == 6)
+                {
+                    Alpha[i] = 0x80;
+                }
+                else /* i == 7 */
+                {
+                    Alpha[i] = 0x7f;
+                }
+            }
+        }
+
+        private static byte[] BCnDecodeTile(
+            byte[] Input,
+            int    Offset,
+            bool   IsBC1)
+        {
+            Color[] CLUT = new Color[4];
+
+            int c0 = Get16(Input, Offset + 0);
+            int c1 = Get16(Input, Offset + 2);
+
+            CLUT[0] = DecodeRGB565(c0);
+            CLUT[1] = DecodeRGB565(c1);
+            CLUT[2] = CalculateCLUT2(CLUT[0], CLUT[1], c0, c1, IsBC1);
+            CLUT[3] = CalculateCLUT3(CLUT[0], CLUT[1], c0, c1, IsBC1);
+
+            int Indices = Get32(Input, Offset + 4);
+
+            int IdxShift = 0;
+
+            byte[] Output = new byte[4 * 4 * 4];
+
+            int OOffset = 0;
+
+            for (int TY = 0; TY < 4; TY++)
+            {
+                for (int TX = 0; TX < 4; TX++)
+                {
+                    int Idx = (Indices >> IdxShift) & 3;
+
+                    IdxShift += 2;
+
+                    Color Pixel = CLUT[Idx];
+
+                    Output[OOffset + 0] = Pixel.R;
+                    Output[OOffset + 1] = Pixel.G;
+                    Output[OOffset + 2] = Pixel.B;
+                    Output[OOffset + 3] = Pixel.A;
+
+                    OOffset += 4;
+                }
+            }
+
+            return Output;
+        }
+
+        private static Color CalculateCLUT2(Color C0, Color C1, int c0, int c1, bool IsBC1)
+        {
+            if (c0 > c1 || !IsBC1)
+            {
+                return Color.FromArgb(
+                    (2 * C0.R + C1.R) / 3,
+                    (2 * C0.G + C1.G) / 3,
+                    (2 * C0.B + C1.B) / 3);
+            }
+            else
+            {
+                return Color.FromArgb(
+                    (C0.R + C1.R) / 2,
+                    (C0.G + C1.G) / 2,
+                    (C0.B + C1.B) / 2);
+            }
+        }
+
+        private static Color CalculateCLUT3(Color C0, Color C1, int c0, int c1, bool IsBC1)
+        {
+            if (c0 > c1 || !IsBC1)
+            {
+                return
+                    Color.FromArgb(
+                        (2 * C1.R + C0.R) / 3,
+                        (2 * C1.G + C0.G) / 3,
+                        (2 * C1.B + C0.B) / 3);
+            }
+
+            return Color.Transparent;
+        }
+
+        private static Color DecodeRGB565(int Value)
+        {
+            int B = ((Value >>  0) & 0x1f) << 3;
+            int G = ((Value >>  5) & 0x3f) << 2;
+            int R = ((Value >> 11) & 0x1f) << 3;
+
+            return Color.FromArgb(
+                R | (R >> 5),
+                G | (G >> 6),
+                B | (B >> 5));
+        }
+
+        private static int Get16(byte[] Data, int Address)
+        {
+            return
+                Data[Address + 0] << 0 |
+                Data[Address + 1] << 8;
+        }
+
+        private static int Get32(byte[] Data, int Address)
+        {
+            return
+                Data[Address + 0] << 0 |
+                Data[Address + 1] << 8 |
+                Data[Address + 2] << 16 |
+                Data[Address + 3] << 24;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Gpu/NsGpu.cs b/Ryujinx/Gpu/NsGpu.cs
new file mode 100644
index 0000000000..6aa7332cdf
--- /dev/null
+++ b/Ryujinx/Gpu/NsGpu.cs
@@ -0,0 +1,22 @@
+using Gal;
+
+namespace Ryujinx.Gpu
+{
+    class NsGpu
+    {
+        public IGalRenderer Renderer { get; private set; }
+
+        public NsGpuMemoryMgr MemoryMgr { get; private set; }
+
+        public NsGpuPGraph PGraph { get; private set; }
+
+        public NsGpu(IGalRenderer Renderer)
+        {
+            this.Renderer = Renderer;
+
+            MemoryMgr = new NsGpuMemoryMgr();
+
+            PGraph = new NsGpuPGraph(this);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Gpu/NsGpuEngine.cs b/Ryujinx/Gpu/NsGpuEngine.cs
new file mode 100644
index 0000000000..bf1045696a
--- /dev/null
+++ b/Ryujinx/Gpu/NsGpuEngine.cs
@@ -0,0 +1,13 @@
+namespace Ryujinx.Gpu
+{
+    enum NsGpuEngine
+    {
+        None    = 0,
+        _2d     = 0x902d,
+        _3d     = 0xb197,
+        Compute = 0xb1c0,
+        Kepler  = 0xa140,
+        Dma     = 0xb0b5,
+        GpFifo  = 0xb06f
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Gpu/NsGpuMemoryMgr.cs b/Ryujinx/Gpu/NsGpuMemoryMgr.cs
new file mode 100644
index 0000000000..e555f2af3e
--- /dev/null
+++ b/Ryujinx/Gpu/NsGpuMemoryMgr.cs
@@ -0,0 +1,204 @@
+using System.Runtime.CompilerServices;
+
+namespace Ryujinx.Gpu
+{
+    class NsGpuMemoryMgr
+    {
+        private const long AddrSize   = 1L << 40;
+
+        private const int  PTLvl0Bits = 14;
+        private const int  PTLvl1Bits = 14;
+        private const int  PTPageBits = 12;
+
+        private const int  PTLvl0Size = 1 << PTLvl0Bits;
+        private const int  PTLvl1Size = 1 << PTLvl1Bits;
+        private const int  PageSize   = 1 << PTPageBits;
+
+        private const int  PTLvl0Mask = PTLvl0Size - 1;
+        private const int  PTLvl1Mask = PTLvl1Size - 1;
+        private const int  PageMask   = PageSize - 1;
+
+        private const int  PTLvl0Bit  = PTPageBits + PTLvl0Bits;
+        private const int  PTLvl1Bit  = PTPageBits;
+
+        private const long PteUnmapped = -1;
+        private const long PteReserved = -2;
+
+        private long[][] PageTable;
+
+        public NsGpuMemoryMgr()
+        {
+            PageTable = new long[PTLvl0Size][];
+        }
+
+        public long Map(long CpuAddr, long GpuAddr, long Size)
+        {
+            CpuAddr &= ~PageMask;
+            GpuAddr &= ~PageMask;
+
+            for (long Offset = 0; Offset < Size; Offset += PageSize)
+            {
+                if (GetPTAddr(GpuAddr + Offset) != PteReserved)
+                {
+                    return Map(CpuAddr, Size);
+                }
+            }
+
+            for (long Offset = 0; Offset < Size; Offset += PageSize)
+            {
+                SetPTAddr(GpuAddr + Offset, CpuAddr + Offset);
+            }
+
+            return GpuAddr;
+        }
+
+        public long Map(long CpuAddr, long Size)
+        {
+            CpuAddr &= ~PageMask;
+
+            long Position = GetFreePosition(Size);
+            
+            if (Position != -1)
+            {
+                for (long Offset = 0; Offset < Size; Offset += PageSize)
+                {
+                    SetPTAddr(Position + Offset, CpuAddr + Offset);
+                }
+            }
+
+            return Position;
+        }
+
+        public long Reserve(long GpuAddr, long Size, long Align)
+        {
+            for (long Offset = 0; Offset < Size; Offset += PageSize)
+            {
+                if (HasPTAddr(GpuAddr + Offset))
+                {
+                    return Reserve(Size, Align);
+                }
+            }
+
+            for (long Offset = 0; Offset < Size; Offset += PageSize)
+            {
+                SetPTAddr(GpuAddr + Offset, PteReserved);
+            }
+
+            return GpuAddr;
+        }
+
+        public long Reserve(long Size, long Align)
+        {
+            long Position = GetFreePosition(Size, Align);
+
+            if (Position != -1)
+            {
+                for (long Offset = 0; Offset < Size; Offset += PageSize)
+                {
+                    SetPTAddr(Position + Offset, PteReserved);
+                }
+            }
+
+            return Position;
+        }
+
+        private long GetFreePosition(long Size, long Align = 1)
+        {
+            long Position = 0;
+            long FreeSize = 0;
+
+            Align = (Align + PageMask) & ~PageMask;
+
+            while (Position + FreeSize < AddrSize)
+            {
+                if (!HasPTAddr(Position + FreeSize))
+                {
+                    FreeSize += PageSize;
+
+                    if (FreeSize >= Size)
+                    {
+                        return Position;
+                    }
+                }
+                else
+                {
+                    Position += FreeSize + PageSize;
+                    FreeSize  = 0;
+
+                    long Remainder = Position % Align;
+
+                    if (Remainder != 0)
+                    {
+                        Position = (Position - Remainder) + Align;
+                    }
+                }
+            }
+
+            return -1;
+        }
+
+        public long GetCpuAddr(long Position)
+        {
+            long BasePos = GetPTAddr(Position);
+
+            if (BasePos < 0)
+            {
+                return -1;
+            }
+
+            return BasePos + (Position & PageMask);
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private bool HasPTAddr(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] != PteUnmapped;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private long GetPTAddr(long Position)
+        {
+            long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
+            long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
+
+            if (PageTable[L0] == null)
+            {
+                return -1;
+            }
+
+            return PageTable[L0][L1];
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private void SetPTAddr(long Position, long TgtAddr)
+        {
+            long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
+            long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
+
+            if (PageTable[L0] == null)
+            {
+                PageTable[L0] = new long[PTLvl1Size];
+
+                for (int Index = 0; Index < PTLvl1Size; Index++)
+                {
+                    PageTable[L0][Index] = PteUnmapped;
+                }
+            }
+
+            PageTable[L0][L1] = TgtAddr;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Gpu/NsGpuPBEntry.cs b/Ryujinx/Gpu/NsGpuPBEntry.cs
new file mode 100644
index 0000000000..226a7f61df
--- /dev/null
+++ b/Ryujinx/Gpu/NsGpuPBEntry.cs
@@ -0,0 +1,79 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
+
+namespace Ryujinx.Gpu
+{
+    struct NsGpuPBEntry
+    {
+        public NsGpuRegister Register { get; private set; }
+
+        public int SubChannel { get; private set; }
+
+        private int[] m_Arguments;
+
+        public ReadOnlyCollection<int> Arguments => Array.AsReadOnly(m_Arguments);
+
+        public NsGpuPBEntry(NsGpuRegister Register, int SubChannel, params int[] Arguments)
+        {
+            this.Register    = Register;
+            this.SubChannel  = SubChannel;
+            this.m_Arguments = Arguments;
+        }
+
+        public static NsGpuPBEntry[] DecodePushBuffer(byte[] Data)
+        {
+            using (MemoryStream MS = new MemoryStream(Data))
+            {
+                BinaryReader Reader = new BinaryReader(MS);
+
+                List<NsGpuPBEntry> GpFifos = new List<NsGpuPBEntry>();
+
+                bool CanRead() => MS.Position + 4 <= MS.Length;
+
+                while (CanRead())
+                {
+                    int Packed = Reader.ReadInt32();
+
+                    int Reg  = (Packed << 2)  & 0x7ffc;
+                    int SubC = (Packed >> 13) & 7;
+                    int Args = (Packed >> 16) & 0x1fff;
+                    int Mode = (Packed >> 29) & 7;
+
+                    if (Mode == 4)
+                    {
+                        //Inline Mode.
+                        GpFifos.Add(new NsGpuPBEntry((NsGpuRegister)Reg, SubC, Args));
+                    }
+                    else
+                    {
+                        //Word mode.
+                        if (Mode == 1)
+                        {
+                            //Sequential Mode.
+                            for (int Index = 0; Index < Args && CanRead(); Index++, Reg += 4)
+                            {
+                                GpFifos.Add(new NsGpuPBEntry((NsGpuRegister)Reg, SubC, Reader.ReadInt32()));
+                            }
+                        }
+                        else
+                        {
+                            //Non-Sequential Mode.
+                            int[] Arguments = new int[Args];
+
+                            for (int Index = 0; Index < Args && CanRead(); Index++)
+                            {
+                                Arguments[Index] = Reader.ReadInt32();
+                            }
+
+                            GpFifos.Add(new NsGpuPBEntry((NsGpuRegister)Reg, SubC, Arguments));
+                        }
+                    }
+                }
+
+                return GpFifos.ToArray();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Gpu/NsGpuPGraph.cs b/Ryujinx/Gpu/NsGpuPGraph.cs
new file mode 100644
index 0000000000..e40b6283ee
--- /dev/null
+++ b/Ryujinx/Gpu/NsGpuPGraph.cs
@@ -0,0 +1,276 @@
+using ChocolArm64.Memory;
+using Gal;
+using System.Collections.Generic;
+
+namespace Ryujinx.Gpu
+{
+    class NsGpuPGraph
+    {
+        private NsGpu Gpu;
+
+        private int[] Registers;
+
+        public NsGpuEngine[] SubChannels;
+
+        private Dictionary<long, int> CurrentVertexBuffers;
+
+        public NsGpuPGraph(NsGpu Gpu)
+        {
+            this.Gpu = Gpu;
+
+            Registers = new int[0x1000];
+
+            SubChannels = new NsGpuEngine[8];
+
+            CurrentVertexBuffers = new Dictionary<long, int>();
+        }
+
+        public void ProcessPushBuffer(NsGpuPBEntry[] PushBuffer, AMemory Memory)
+        {
+            bool HasQuery = false;
+
+            foreach (NsGpuPBEntry Entry in PushBuffer)
+            {
+                if (Entry.Arguments.Count == 1)
+                {
+                    SetRegister(Entry.Register, Entry.Arguments[0]);
+                }
+
+                switch (Entry.Register)
+                {
+                    case NsGpuRegister.BindChannel:
+                        if (Entry.Arguments.Count > 0)
+                        {
+                            SubChannels[Entry.SubChannel] = (NsGpuEngine)Entry.Arguments[0];
+                        }
+                        break;
+
+                    case NsGpuRegister._3dVertexArray0Fetch:
+                        SendVertexBuffers(Memory);
+                        break;
+                    
+                    case NsGpuRegister._3dCbData0:
+                        if (GetRegister(NsGpuRegister._3dCbPos) == 0x20)
+                        {
+                            SendTexture(Memory);
+                        }
+                        break;
+
+                    case NsGpuRegister._3dQueryAddressHigh:
+                    case NsGpuRegister._3dQueryAddressLow:
+                    case NsGpuRegister._3dQuerySequence:
+                    case NsGpuRegister._3dQueryGet:
+                        HasQuery = true;
+                        break;
+                }
+            }
+
+            if (HasQuery)
+            {
+                long Position =
+                    (long)GetRegister(NsGpuRegister._3dQueryAddressHigh) << 32 |
+                    (long)GetRegister(NsGpuRegister._3dQueryAddressLow)  << 0;
+
+                int Seq = GetRegister(NsGpuRegister._3dQuerySequence);
+                int Get = GetRegister(NsGpuRegister._3dQueryGet);
+
+                int Mode = Get & 3;
+
+                if (Mode == 0)
+                {
+                    //Write
+                    Position = Gpu.MemoryMgr.GetCpuAddr(Position);
+
+                    if (Position != -1)
+                    {
+                        Gpu.Renderer.QueueAction(delegate()
+                        {
+                            Memory.WriteInt32(Position, Seq);
+                        });
+                    }
+                }
+            }
+        }
+
+        private void SendVertexBuffers(AMemory Memory)
+        {
+            long Position =
+                (long)GetRegister(NsGpuRegister._3dVertexArray0StartHigh) << 32 |
+                (long)GetRegister(NsGpuRegister._3dVertexArray0StartLow)  << 0;
+
+            long Limit =
+                (long)GetRegister(NsGpuRegister._3dVertexArray0LimitHigh) << 32 |
+                (long)GetRegister(NsGpuRegister._3dVertexArray0LimitLow)  << 0;
+
+            int VbIndex = CurrentVertexBuffers.Count;
+
+            if (!CurrentVertexBuffers.TryAdd(Position, VbIndex))
+            {
+                VbIndex = CurrentVertexBuffers[Position];
+            }
+
+            if (Limit != 0)
+            {
+                long Size = (Limit - Position) + 1;
+
+                Position = Gpu.MemoryMgr.GetCpuAddr(Position);
+
+                if (Position != -1)
+                {
+                    byte[] Buffer = AMemoryHelper.ReadBytes(Memory, Position, (int)Size);
+
+                    int Stride = GetRegister(NsGpuRegister._3dVertexArray0Fetch) & 0xfff;
+
+                    List<GalVertexAttrib> Attribs = new List<GalVertexAttrib>();
+
+                    for (int Attr = 0; Attr < 16; Attr++)
+                    {
+                        int Packed = GetRegister(NsGpuRegister._3dVertexAttrib0Format + Attr * 4);
+
+                        GalVertexAttrib Attrib = new GalVertexAttrib(Attr,
+                                                  (Packed >>  0) & 0x1f,
+                                                 ((Packed >>  6) & 0x1) != 0,
+                                                  (Packed >>  7) & 0x3fff,
+                            (GalVertexAttribSize)((Packed >> 21) & 0x3f),
+                            (GalVertexAttribType)((Packed >> 27) & 0x7),
+                                                 ((Packed >> 31) & 0x1) != 0);
+
+                        if (Attrib.Offset < Stride)
+                        {
+                            Attribs.Add(Attrib);
+                        }
+                    }
+
+                    Gpu.Renderer.QueueAction(delegate()
+                    {
+                        Gpu.Renderer.SendVertexBuffer(VbIndex, Buffer, Stride, Attribs.ToArray());
+                    });
+                }
+            }
+        }
+
+        private void SendTexture(AMemory Memory)
+        {
+            long TicPos = (long)GetRegister(NsGpuRegister._3dTicAddressHigh) << 32 |
+                          (long)GetRegister(NsGpuRegister._3dTicAddressLow)  << 0;
+
+            int CbData = GetRegister(NsGpuRegister._3dCbData0);
+
+            int TicIndex = (CbData >>  0) & 0xfffff;
+            int TscIndex = (CbData >> 20) & 0xfff; //I guess?
+
+            TicPos = Gpu.MemoryMgr.GetCpuAddr(TicPos + TicIndex * 0x20);
+
+            if (TicPos != -1)
+            {
+                int Word0 = Memory.ReadInt32(TicPos + 0x0);
+                int Word1 = Memory.ReadInt32(TicPos + 0x4);
+                int Word2 = Memory.ReadInt32(TicPos + 0x8);
+                int Word3 = Memory.ReadInt32(TicPos + 0xc);
+                int Word4 = Memory.ReadInt32(TicPos + 0x10);
+                int Word5 = Memory.ReadInt32(TicPos + 0x14);
+                int Word6 = Memory.ReadInt32(TicPos + 0x18);
+                int Word7 = Memory.ReadInt32(TicPos + 0x1c);
+
+                long TexAddress = Word1;
+
+                TexAddress |= (long)(Word2 & 0xff) << 32;
+
+                TexAddress = Gpu.MemoryMgr.GetCpuAddr(TexAddress);
+
+                if (TexAddress != -1)
+                {
+                    NsGpuTextureFormat Format = (NsGpuTextureFormat)(Word0 & 0x7f);
+
+                    int Width  = (Word4 & 0xffff) + 1;
+                    int Height = (Word5 & 0xffff) + 1;
+
+                    byte[] Buffer = GetDecodedTexture(Memory, Format, TexAddress, Width, Height);
+
+                    if (Buffer != null)
+                    {
+                        Gpu.Renderer.QueueAction(delegate()
+                        {
+                            Gpu.Renderer.SendR8G8B8A8Texture(0, Buffer, Width, Height);
+                        });
+                    }
+                }
+            }
+        }
+
+        private static byte[] GetDecodedTexture(
+            AMemory            Memory,
+            NsGpuTextureFormat Format,
+            long               Position,
+            int                Width,
+            int                Height)
+        {
+            byte[] Data = null;
+
+            switch (Format)
+            {
+                case NsGpuTextureFormat.BC1:
+                {
+                    int Size = (Width * Height) >> 1;
+
+                    Data = AMemoryHelper.ReadBytes(Memory, Position, Size);
+
+                    Data = BCn.DecodeBC1(new NsGpuTexture()
+                    {
+                        Width  = Width,
+                        Height = Height,
+                        Data   = Data
+                    }, 0);
+
+                    break;
+                }
+
+                case NsGpuTextureFormat.BC2:
+                {
+                    int Size = Width * Height;
+
+                    Data = AMemoryHelper.ReadBytes(Memory, Position, Size);
+
+                    Data = BCn.DecodeBC2(new NsGpuTexture()
+                    {
+                        Width  = Width,
+                        Height = Height,
+                        Data   = Data
+                    }, 0);
+
+                    break;
+                }
+
+                case NsGpuTextureFormat.BC3:
+                {
+                    int Size = Width * Height;
+
+                    Data = AMemoryHelper.ReadBytes(Memory, Position, Size);
+
+                    Data = BCn.DecodeBC3(new NsGpuTexture()
+                    {
+                        Width  = Width,
+                        Height = Height,
+                        Data   = Data
+                    }, 0);
+
+                    break;
+                }
+
+                //default: throw new NotImplementedException(Format.ToString());
+            }
+
+            return Data;
+        }
+
+        public int GetRegister(NsGpuRegister Register)
+        {
+            return Registers[((int)Register >> 2) & 0xfff];
+        }
+
+        public void SetRegister(NsGpuRegister Register, int Value)
+        {
+            Registers[((int)Register >> 2) & 0xfff] = Value;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Gpu/NsGpuRegister.cs b/Ryujinx/Gpu/NsGpuRegister.cs
new file mode 100644
index 0000000000..740ca9feb5
--- /dev/null
+++ b/Ryujinx/Gpu/NsGpuRegister.cs
@@ -0,0 +1,93 @@
+namespace Ryujinx.Gpu
+{
+    enum NsGpuRegister
+    {
+        BindChannel               = 0,
+
+        _2dClipEnable             = 0x0290,
+        _2dOperation              = 0x02ac,
+
+        _3dGlobalBase             = 0x02c8,
+        _3dRt0AddressHigh         = 0x0800,
+        _3dRt0AddressLow          = 0x0804,
+        _3dRt0Horiz               = 0x0808,
+        _3dRt0Vert                = 0x080c,
+        _3dRt0Format              = 0x0810,
+        _3dRt0BlockDimensions     = 0x0814,
+        _3dRt0ArrayMode           = 0x0818,
+        _3dRt0LayerStride         = 0x081c,
+        _3dRt0BaseLayer           = 0x0820,
+        _3dViewportScaleX         = 0x0a00,
+        _3dViewportScaleY         = 0x0a04,
+        _3dViewportScaleZ         = 0x0a08,
+        _3dViewportTranslateX     = 0x0a0c,
+        _3dViewportTranslateY     = 0x0a10,
+        _3dViewportTranslateZ     = 0x0a14,
+        _3dViewportHoriz          = 0x0c00,
+        _3dViewportVert           = 0x0c04,
+        _3dDepthRangeNear         = 0x0c08,
+        _3dDepthRangeFar          = 0x0c0c,
+        _3dClearColorR            = 0x0d80,
+        _3dClearColorG            = 0x0d84,
+        _3dClearColorB            = 0x0d88,
+        _3dClearColorA            = 0x0d8c,
+        _3dScreenScissorHoriz     = 0x0ff4,
+        _3dScreenScissorVert      = 0x0ff8,
+        _3dVertexAttrib0Format    = 0x1160,
+        _3dVertexAttrib1Format    = 0x1164,
+        _3dVertexAttrib2Format    = 0x1168,
+        _3dVertexAttrib3Format    = 0x116c,
+        _3dVertexAttrib4Format    = 0x1170,
+        _3dVertexAttrib5Format    = 0x1174,
+        _3dVertexAttrib6Format    = 0x1178,
+        _3dVertexAttrib7Format    = 0x117c,
+        _3dVertexAttrib8Format    = 0x1180,
+        _3dVertexAttrib9Format    = 0x1184,
+        _3dVertexAttrib10Format   = 0x1188,
+        _3dVertexAttrib11Format   = 0x118c,
+        _3dVertexAttrib12Format   = 0x1190,
+        _3dVertexAttrib13Format   = 0x1194,
+        _3dVertexAttrib14Format   = 0x1198,
+        _3dVertexAttrib15Format   = 0x119c,
+        _3dScreenYControl         = 0x13ac,
+        _3dTscAddressHigh         = 0x155c,
+        _3dTscAddressLow          = 0x1560,
+        _3dTscLimit               = 0x1564,
+        _3dTicAddressHigh         = 0x1574,
+        _3dTicAddressLow          = 0x1578,
+        _3dTicLimit               = 0x157c,
+        _3dMultiSampleMode        = 0x15d0,
+        _3dVertexEndGl            = 0x1614,
+        _3dVertexBeginGl          = 0x1618,
+        _3dQueryAddressHigh       = 0x1b00,
+        _3dQueryAddressLow        = 0x1b04,
+        _3dQuerySequence          = 0x1b08,
+        _3dQueryGet               = 0x1b0c,
+        _3dVertexArray0Fetch      = 0x1c00,
+        _3dVertexArray0StartHigh  = 0x1c04,
+        _3dVertexArray0StartLow   = 0x1c08,
+        _3dVertexArray1Fetch      = 0x1c10, //todo: the rest
+        _3dVertexArray0LimitHigh  = 0x1f00,
+        _3dVertexArray0LimitLow   = 0x1f04,
+        _3dCbSize                 = 0x2380,
+        _3dCbAddressHigh          = 0x2384,
+        _3dCbAddressLow           = 0x2388,
+        _3dCbPos                  = 0x238c,
+        _3dCbData0                = 0x2390,
+        _3dCbData1                = 0x2394,
+        _3dCbData2                = 0x2398,
+        _3dCbData3                = 0x239c,
+        _3dCbData4                = 0x23a0,
+        _3dCbData5                = 0x23a4,
+        _3dCbData6                = 0x23a8,
+        _3dCbData7                = 0x23ac,
+        _3dCbData8                = 0x23b0,
+        _3dCbData9                = 0x23b4,
+        _3dCbData10               = 0x23b8,
+        _3dCbData11               = 0x23bc,
+        _3dCbData12               = 0x23c0,
+        _3dCbData13               = 0x23c4,
+        _3dCbData14               = 0x23c8,
+        _3dCbData15               = 0x23cc,
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Gpu/NsGpuTexture.cs b/Ryujinx/Gpu/NsGpuTexture.cs
new file mode 100644
index 0000000000..26500c04a2
--- /dev/null
+++ b/Ryujinx/Gpu/NsGpuTexture.cs
@@ -0,0 +1,10 @@
+namespace Ryujinx.Gpu
+{
+    struct NsGpuTexture
+    {
+        public int Width;
+        public int Height;
+
+        public byte[] Data;
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Gpu/NsGpuTextureFormat.cs b/Ryujinx/Gpu/NsGpuTextureFormat.cs
new file mode 100644
index 0000000000..9bb122812d
--- /dev/null
+++ b/Ryujinx/Gpu/NsGpuTextureFormat.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.Gpu
+{
+    enum NsGpuTextureFormat
+    {
+        BC1 = 0x24,
+        BC2 = 0x25,
+        BC3 = 0x26
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Gpu/SwizzleAddr.cs b/Ryujinx/Gpu/SwizzleAddr.cs
new file mode 100644
index 0000000000..5ad35a5389
--- /dev/null
+++ b/Ryujinx/Gpu/SwizzleAddr.cs
@@ -0,0 +1,144 @@
+using System;
+
+namespace Ryujinx.Gpu
+{
+    class SwizzleAddr
+    {
+        private int Width;
+
+        private int XB;
+        private int YB;
+
+        public SwizzleAddr(int Width, int Height, int Pad)
+        {
+            int W = Pow2RoundUp(Width);
+            int H = Pow2RoundUp(Height);
+
+            XB = CountZeros(W);
+            YB = CountZeros(H);
+
+            int HH = H >> 1;
+
+            if (!IsPow2(Height) && Height <= HH + HH / 3 && YB > 3)
+            {
+                YB--;
+            }
+
+            this.Width = RoundSize(Width, Pad);
+        }
+
+        private static int Pow2RoundUp(int Value)
+        {
+            Value--;
+
+            Value |= (Value >>  1);
+            Value |= (Value >>  2);
+            Value |= (Value >>  4);
+            Value |= (Value >>  8);
+            Value |= (Value >> 16);
+
+            return ++Value;
+        }
+
+        private static bool IsPow2(int Value)
+        {
+            return Value != 0 && (Value & (Value - 1)) == 0;
+        }
+
+        private static int CountZeros(int Value)
+        {
+            int Count = 0;
+
+            for (int i = 0; i < 32; i++)
+            {
+                if ((Value & (1 << i)) != 0)
+                {
+                    break;
+                }
+
+                Count++;
+            }
+
+            return Count;
+        }
+
+        private static int RoundSize(int Size, int Pad)
+        {
+            int Mask = Pad - 1;
+
+            if ((Size & Mask) != 0)
+            {
+                Size &= ~Mask;
+                Size +=  Pad;
+            }
+
+            return Size;
+        }
+
+        public int GetSwizzledAddress8(int X, int Y)
+        {
+            return GetSwizzledAddress(X, Y, 4);
+        }
+
+        public int GetSwizzledAddress16(int X, int Y)
+        {
+            return GetSwizzledAddress(X, Y, 3);
+        }
+
+        public int GetSwizzledAddress32(int X, int Y)
+        {
+            return GetSwizzledAddress(X, Y, 2);
+        }
+
+        public int GetSwizzledAddress64(int X, int Y)
+        {
+            return GetSwizzledAddress(X, Y, 1);
+        }
+
+        public int GetSwizzledAddress128(int X, int Y)
+        {
+            return GetSwizzledAddress(X, Y, 0);
+        }
+
+        private int GetSwizzledAddress(int X, int Y, int XBase)
+        {
+            /*
+             * Examples of patterns:
+             *                     x x y x y y x y 0 0 0 0 64   x 64   dxt5
+             *         x x x x x y y y y x y y x y 0 0 0 0 512  x 512  dxt5
+             *     y x x x x x x y y y y x y y x y 0 0 0 0 1024 x 1024 dxt5
+             *   y y x x x x x x y y y y x y y x y x 0 0 0 2048 x 2048 dxt1
+             * y y y x x x x x x y y y y x y y x y x x 0 0 1024 x 1024 rgba8888
+             * 
+             * Read from right to left, LSB first.
+             */
+            int XCnt    = XBase;
+            int YCnt    = 1;
+            int XUsed   = 0;
+            int YUsed   = 0;
+            int Address = 0;
+
+            while (XUsed < XBase + 2 && XUsed + XCnt < XB)
+            {
+                int XMask = (1 << XCnt) - 1;
+                int YMask = (1 << YCnt) - 1;
+
+                Address |= (X & XMask) << XUsed + YUsed;
+                Address |= (Y & YMask) << XUsed + YUsed + XCnt;
+
+                X >>= XCnt;
+                Y >>= YCnt;
+
+                XUsed += XCnt;
+                YUsed += YCnt;
+
+                XCnt = Math.Min(XB - XUsed, 1);
+                YCnt = Math.Min(YB - YUsed, YCnt << 1);
+            }
+
+            Address |= (X + Y * (Width >> XUsed)) << (XUsed + YUsed);
+
+            return Address;
+        }
+    }
+}
diff --git a/Ryujinx/Loaders/Compression/Lz4.cs b/Ryujinx/Loaders/Compression/Lz4.cs
new file mode 100644
index 0000000000..aace200cc3
--- /dev/null
+++ b/Ryujinx/Loaders/Compression/Lz4.cs
@@ -0,0 +1,78 @@
+using System;
+
+namespace Ryujinx.Loaders.Compression
+{
+    static class Lz4
+    {
+        public static byte[] Decompress(byte[] Cmp, int DecLength)
+        {
+            byte[] Dec = new byte[DecLength];
+
+            int CmpPos = 0;
+            int DecPos = 0;
+
+            int GetLength(int Length)
+            {
+                byte Sum;
+
+                if (Length == 0xf)
+                {
+                    do
+                    {
+                        Length += (Sum = Cmp[CmpPos++]);
+                    }
+                    while (Sum == 0xff);
+                }
+
+                return Length;
+            }
+
+            do
+            {
+                byte Token = Cmp[CmpPos++];
+
+                int EncCount = (Token >> 0) & 0xf;
+                int LitCount = (Token >> 4) & 0xf;
+
+                //Copy literal chunck
+                LitCount = GetLength(LitCount);
+
+                Buffer.BlockCopy(Cmp, CmpPos, Dec, DecPos, LitCount);
+
+                CmpPos += LitCount;
+                DecPos += LitCount;
+
+                if (CmpPos >= Cmp.Length)
+                {
+                    break;
+                }
+
+                //Copy compressed chunck
+                int Back = Cmp[CmpPos++] << 0 |
+                           Cmp[CmpPos++] << 8;
+
+                EncCount = GetLength(EncCount) + 4;
+
+                int EncPos = DecPos - Back;
+
+                if (EncCount <= Back)
+                {
+                    Buffer.BlockCopy(Dec, EncPos, Dec, DecPos, EncCount);
+
+                    DecPos += EncCount;
+                }
+                else
+                {
+                    while (EncCount-- > 0)
+                    {
+                        Dec[DecPos++] = Dec[EncPos++];
+                    }
+                }
+            }
+            while (CmpPos < Cmp.Length &&
+                   DecPos < Dec.Length);
+
+            return Dec;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Loaders/ElfDyn.cs b/Ryujinx/Loaders/ElfDyn.cs
new file mode 100644
index 0000000000..595d6cfb5a
--- /dev/null
+++ b/Ryujinx/Loaders/ElfDyn.cs
@@ -0,0 +1,15 @@
+namespace Ryujinx.Loaders
+{
+    struct ElfDyn
+    {
+        public ElfDynTag Tag { get; private set; }
+
+        public long Value { get; private set; }
+
+        public ElfDyn(ElfDynTag Tag, long Value)
+        {
+            this.Tag   = Tag;
+            this.Value = Value;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Loaders/ElfDynTag.cs b/Ryujinx/Loaders/ElfDynTag.cs
new file mode 100644
index 0000000000..fb6cab3fb9
--- /dev/null
+++ b/Ryujinx/Loaders/ElfDynTag.cs
@@ -0,0 +1,72 @@
+namespace Ryujinx.Loaders
+{
+    enum ElfDynTag
+    {
+        DT_NULL            = 0,
+        DT_NEEDED          = 1,
+        DT_PLTRELSZ        = 2,
+        DT_PLTGOT          = 3,
+        DT_HASH            = 4,
+        DT_STRTAB          = 5,
+        DT_SYMTAB          = 6,
+        DT_RELA            = 7,
+        DT_RELASZ          = 8,
+        DT_RELAENT         = 9,
+        DT_STRSZ           = 10,
+        DT_SYMENT          = 11,
+        DT_INIT            = 12,
+        DT_FINI            = 13,
+        DT_SONAME          = 14,
+        DT_RPATH           = 15,
+        DT_SYMBOLIC        = 16,
+        DT_REL             = 17,
+        DT_RELSZ           = 18,
+        DT_RELENT          = 19,
+        DT_PLTREL          = 20,
+        DT_DEBUG           = 21,
+        DT_TEXTREL         = 22,
+        DT_JMPREL          = 23,
+        DT_BIND_NOW        = 24,
+        DT_INIT_ARRAY      = 25,
+        DT_FINI_ARRAY      = 26,
+        DT_INIT_ARRAYSZ    = 27,
+        DT_FINI_ARRAYSZ    = 28,
+        DT_RUNPATH         = 29,
+        DT_FLAGS           = 30,
+        DT_ENCODING        = 32,
+        DT_PREINIT_ARRAY   = 32,
+        DT_PREINIT_ARRAYSZ = 33,
+        DT_GNU_PRELINKED   = 0x6ffffdf5,
+        DT_GNU_CONFLICTSZ  = 0x6ffffdf6,
+        DT_GNU_LIBLISTSZ   = 0x6ffffdf7,
+        DT_CHECKSUM        = 0x6ffffdf8,
+        DT_PLTPADSZ        = 0x6ffffdf9,
+        DT_MOVEENT         = 0x6ffffdfa,
+        DT_MOVESZ          = 0x6ffffdfb,
+        DT_FEATURE_1       = 0x6ffffdfc,
+        DT_POSFLAG_1       = 0x6ffffdfd,
+        DT_SYMINSZ         = 0x6ffffdfe,
+        DT_SYMINENT        = 0x6ffffdff,
+        DT_GNU_HASH        = 0x6ffffef5,
+        DT_TLSDESC_PLT     = 0x6ffffef6,
+        DT_TLSDESC_GOT     = 0x6ffffef7,
+        DT_GNU_CONFLICT    = 0x6ffffef8,
+        DT_GNU_LIBLIST     = 0x6ffffef9,
+        DT_CONFIG          = 0x6ffffefa,
+        DT_DEPAUDIT        = 0x6ffffefb,
+        DT_AUDIT           = 0x6ffffefc,
+        DT_PLTPAD          = 0x6ffffefd,
+        DT_MOVETAB         = 0x6ffffefe,
+        DT_SYMINFO         = 0x6ffffeff,
+        DT_VERSYM          = 0x6ffffff0,
+        DT_RELACOUNT       = 0x6ffffff9,
+        DT_RELCOUNT        = 0x6ffffffa,
+        DT_FLAGS_1         = 0x6ffffffb,
+        DT_VERDEF          = 0x6ffffffc,
+        DT_VERDEFNUM       = 0x6ffffffd,
+        DT_VERNEED         = 0x6ffffffe,
+        DT_VERNEEDNUM      = 0x6fffffff,
+        DT_AUXILIARY       = 0x7ffffffd,
+        DT_FILTER          = 0x7fffffff
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Loaders/ElfRel.cs b/Ryujinx/Loaders/ElfRel.cs
new file mode 100644
index 0000000000..8b691d99c3
--- /dev/null
+++ b/Ryujinx/Loaders/ElfRel.cs
@@ -0,0 +1,19 @@
+namespace Ryujinx.Loaders
+{
+    struct ElfRel
+    {
+        public long Offset { get; private set; }
+        public long Addend { get; private set; }
+
+        public ElfSym     Symbol { get; private set; }
+        public ElfRelType Type   { get; private set; }
+
+        public ElfRel(long Offset, long Addend, ElfSym Symbol, ElfRelType Type)
+        {
+            this.Offset = Offset;
+            this.Addend = Addend;
+            this.Symbol = Symbol;
+            this.Type   = Type;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Loaders/ElfRelType.cs b/Ryujinx/Loaders/ElfRelType.cs
new file mode 100644
index 0000000000..cc638b19dd
--- /dev/null
+++ b/Ryujinx/Loaders/ElfRelType.cs
@@ -0,0 +1,128 @@
+namespace Ryujinx.Loaders
+{
+    enum ElfRelType
+    {
+        R_AARCH64_NONE                         = 0,
+        R_AARCH64_ABS64                        = 257,
+        R_AARCH64_ABS32                        = 258,
+        R_AARCH64_ABS16                        = 259,
+        R_AARCH64_PREL64                       = 260,
+        R_AARCH64_PREL32                       = 261,
+        R_AARCH64_PREL16                       = 262,
+        R_AARCH64_MOVW_UABS_G0                 = 263,
+        R_AARCH64_MOVW_UABS_G0_NC              = 264,
+        R_AARCH64_MOVW_UABS_G1                 = 265,
+        R_AARCH64_MOVW_UABS_G1_NC              = 266,
+        R_AARCH64_MOVW_UABS_G2                 = 267,
+        R_AARCH64_MOVW_UABS_G2_NC              = 268,
+        R_AARCH64_MOVW_UABS_G3                 = 269,
+        R_AARCH64_MOVW_SABS_G0                 = 270,
+        R_AARCH64_MOVW_SABS_G1                 = 271,
+        R_AARCH64_MOVW_SABS_G2                 = 272,
+        R_AARCH64_LD_PREL_LO19                 = 273,
+        R_AARCH64_ADR_PREL_LO21                = 274,
+        R_AARCH64_ADR_PREL_PG_HI21             = 275,
+        R_AARCH64_ADR_PREL_PG_HI21_NC          = 276,
+        R_AARCH64_ADD_ABS_LO12_NC              = 277,
+        R_AARCH64_LDST8_ABS_LO12_NC            = 278,
+        R_AARCH64_TSTBR14                      = 279,
+        R_AARCH64_CONDBR19                     = 280,
+        R_AARCH64_JUMP26                       = 282,
+        R_AARCH64_CALL26                       = 283,
+        R_AARCH64_LDST16_ABS_LO12_NC           = 284,
+        R_AARCH64_LDST32_ABS_LO12_NC           = 285,
+        R_AARCH64_LDST64_ABS_LO12_NC           = 286,
+        R_AARCH64_MOVW_PREL_G0                 = 287,
+        R_AARCH64_MOVW_PREL_G0_NC              = 288,
+        R_AARCH64_MOVW_PREL_G1                 = 289,
+        R_AARCH64_MOVW_PREL_G1_NC              = 290,
+        R_AARCH64_MOVW_PREL_G2                 = 291,
+        R_AARCH64_MOVW_PREL_G2_NC              = 292,
+        R_AARCH64_MOVW_PREL_G3                 = 293,
+        R_AARCH64_LDST128_ABS_LO12_NC          = 299,
+        R_AARCH64_MOVW_GOTOFF_G0               = 300,
+        R_AARCH64_MOVW_GOTOFF_G0_NC            = 301,
+        R_AARCH64_MOVW_GOTOFF_G1               = 302,
+        R_AARCH64_MOVW_GOTOFF_G1_NC            = 303,
+        R_AARCH64_MOVW_GOTOFF_G2               = 304,
+        R_AARCH64_MOVW_GOTOFF_G2_NC            = 305,
+        R_AARCH64_MOVW_GOTOFF_G3               = 306,
+        R_AARCH64_GOTREL64                     = 307,
+        R_AARCH64_GOTREL32                     = 308,
+        R_AARCH64_GOT_LD_PREL19                = 309,
+        R_AARCH64_LD64_GOTOFF_LO15             = 310,
+        R_AARCH64_ADR_GOT_PAGE                 = 311,
+        R_AARCH64_LD64_GOT_LO12_NC             = 312,
+        R_AARCH64_LD64_GOTPAGE_LO15            = 313,
+        R_AARCH64_TLSGD_ADR_PREL21             = 512,
+        R_AARCH64_TLSGD_ADR_PAGE21             = 513,
+        R_AARCH64_TLSGD_ADD_LO12_NC            = 514,
+        R_AARCH64_TLSGD_MOVW_G1                = 515,
+        R_AARCH64_TLSGD_MOVW_G0_NC             = 516,
+        R_AARCH64_TLSLD_ADR_PREL21             = 517,
+        R_AARCH64_TLSLD_ADR_PAGE21             = 518,
+        R_AARCH64_TLSLD_ADD_LO12_NC            = 519,
+        R_AARCH64_TLSLD_MOVW_G1                = 520,
+        R_AARCH64_TLSLD_MOVW_G0_NC             = 521,
+        R_AARCH64_TLSLD_LD_PREL19              = 522,
+        R_AARCH64_TLSLD_MOVW_DTPREL_G2         = 523,
+        R_AARCH64_TLSLD_MOVW_DTPREL_G1         = 524,
+        R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC      = 525,
+        R_AARCH64_TLSLD_MOVW_DTPREL_G0         = 526,
+        R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC      = 527,
+        R_AARCH64_TLSLD_ADD_DTPREL_HI12        = 528,
+        R_AARCH64_TLSLD_ADD_DTPREL_LO12        = 529,
+        R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC     = 530,
+        R_AARCH64_TLSLD_LDST8_DTPREL_LO12      = 531,
+        R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC   = 532,
+        R_AARCH64_TLSLD_LDST16_DTPREL_LO12     = 533,
+        R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC  = 534,
+        R_AARCH64_TLSLD_LDST32_DTPREL_LO12     = 535,
+        R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC  = 536,
+        R_AARCH64_TLSLD_LDST64_DTPREL_LO12     = 537,
+        R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC  = 538,
+        R_AARCH64_TLSIE_MOVW_GOTTPREL_G1       = 539,
+        R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC    = 540,
+        R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21    = 541,
+        R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC  = 542,
+        R_AARCH64_TLSIE_LD_GOTTPREL_PREL19     = 543,
+        R_AARCH64_TLSLE_MOVW_TPREL_G2          = 544,
+        R_AARCH64_TLSLE_MOVW_TPREL_G1          = 545,
+        R_AARCH64_TLSLE_MOVW_TPREL_G1_NC       = 546,
+        R_AARCH64_TLSLE_MOVW_TPREL_G0          = 547,
+        R_AARCH64_TLSLE_MOVW_TPREL_G0_NC       = 548,
+        R_AARCH64_TLSLE_ADD_TPREL_HI12         = 549,
+        R_AARCH64_TLSLE_ADD_TPREL_LO12         = 550,
+        R_AARCH64_TLSLE_ADD_TPREL_LO12_NC      = 551,
+        R_AARCH64_TLSLE_LDST8_TPREL_LO12       = 552,
+        R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC    = 553,
+        R_AARCH64_TLSLE_LDST16_TPREL_LO12      = 554,
+        R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC   = 555,
+        R_AARCH64_TLSLE_LDST32_TPREL_LO12      = 556,
+        R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC   = 557,
+        R_AARCH64_TLSLE_LDST64_TPREL_LO12      = 558,
+        R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC   = 559,
+        R_AARCH64_TLSDESC_LD_PREL19            = 560,
+        R_AARCH64_TLSDESC_ADR_PREL21           = 561,
+        R_AARCH64_TLSDESC_ADR_PAGE21           = 562,
+        R_AARCH64_TLSDESC_LD64_LO12            = 563,
+        R_AARCH64_TLSDESC_ADD_LO12             = 564,
+        R_AARCH64_TLSDESC_OFF_G1               = 565,
+        R_AARCH64_TLSDESC_OFF_G0_NC            = 566,
+        R_AARCH64_TLSDESC_LDR                  = 567,
+        R_AARCH64_TLSDESC_ADD                  = 568,
+        R_AARCH64_TLSDESC_CALL                 = 569,
+        R_AARCH64_TLSLE_LDST128_TPREL_LO12     = 570,
+        R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC  = 571,
+        R_AARCH64_TLSLD_LDST128_DTPREL_LO12    = 572,
+        R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC = 573,
+        R_AARCH64_COPY                         = 1024,
+        R_AARCH64_GLOB_DAT                     = 1025,
+        R_AARCH64_JUMP_SLOT                    = 1026,
+        R_AARCH64_RELATIVE                     = 1027,
+        R_AARCH64_TLS_DTPMOD64                 = 1028,
+        R_AARCH64_TLS_DTPREL64                 = 1029,
+        R_AARCH64_TLS_TPREL64                  = 1030,
+        R_AARCH64_TLSDESC                      = 1031
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Loaders/ElfSym.cs b/Ryujinx/Loaders/ElfSym.cs
new file mode 100644
index 0000000000..c4ed810ce6
--- /dev/null
+++ b/Ryujinx/Loaders/ElfSym.cs
@@ -0,0 +1,43 @@
+namespace Ryujinx.Loaders
+{
+    struct ElfSym
+    {
+        public string Name { get; private set; }
+
+        public ElfSymType       Type       { get; private set; }
+        public ElfSymBinding    Binding    { get; private set; }
+        public ElfSymVisibility Visibility { get; private set; }
+
+        public bool IsFuncOrObject =>
+            Type == ElfSymType.STT_FUNC ||
+            Type == ElfSymType.STT_OBJECT;
+
+        public bool IsGlobalOrWeak => 
+            Binding == ElfSymBinding.STB_GLOBAL ||
+            Binding == ElfSymBinding.STB_WEAK;
+
+        public int  SHIdx    { get; private set; }
+        public long ValueAbs { get; private set; }
+        public long Value    { get; private set; }
+        public long Size     { get; private set; }
+
+        public ElfSym(
+            string Name,
+            int    Info, 
+            int    Other,
+            int    SHIdx,
+            long   ImageBase,
+            long   Value,
+            long   Size)
+        {
+            this.Name       = Name;
+            this.Type       = (ElfSymType)(Info & 0xf);
+            this.Binding    = (ElfSymBinding)(Info >> 4);
+            this.Visibility = (ElfSymVisibility)Other;
+            this.SHIdx      = SHIdx;
+            this.ValueAbs   = Value + ImageBase;
+            this.Value      = Value;
+            this.Size       = Size;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Loaders/ElfSymBinding.cs b/Ryujinx/Loaders/ElfSymBinding.cs
new file mode 100644
index 0000000000..8bbc6d4e0f
--- /dev/null
+++ b/Ryujinx/Loaders/ElfSymBinding.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.Loaders
+{
+    enum ElfSymBinding
+    {
+        STB_LOCAL  = 0,
+        STB_GLOBAL = 1,
+        STB_WEAK   = 2
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Loaders/ElfSymType.cs b/Ryujinx/Loaders/ElfSymType.cs
new file mode 100644
index 0000000000..e504411e17
--- /dev/null
+++ b/Ryujinx/Loaders/ElfSymType.cs
@@ -0,0 +1,13 @@
+namespace Ryujinx.Loaders
+{
+    enum ElfSymType
+    {
+        STT_NOTYPE  = 0,
+        STT_OBJECT  = 1,
+        STT_FUNC    = 2,
+        STT_SECTION = 3,
+        STT_FILE    = 4,
+        STT_COMMON  = 5,
+        STT_TLS     = 6
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Loaders/ElfSymVisibility.cs b/Ryujinx/Loaders/ElfSymVisibility.cs
new file mode 100644
index 0000000000..a308ef7952
--- /dev/null
+++ b/Ryujinx/Loaders/ElfSymVisibility.cs
@@ -0,0 +1,10 @@
+namespace Ryujinx.Loaders
+{
+    enum ElfSymVisibility
+    {
+        STV_DEFAULT   = 0,
+        STV_INTERNAL  = 1,
+        STV_HIDDEN    = 2,
+        STV_PROTECTED = 3 
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Loaders/Executable.cs b/Ryujinx/Loaders/Executable.cs
new file mode 100644
index 0000000000..31caf2946f
--- /dev/null
+++ b/Ryujinx/Loaders/Executable.cs
@@ -0,0 +1,144 @@
+using ChocolArm64.Memory;
+using Ryujinx.Loaders.Executables;
+using Ryujinx.OsHle;
+using System.Collections.Generic;
+
+namespace Ryujinx.Loaders
+{
+    class Executable
+    {
+        private IElf    NsoData;
+        private AMemory Memory;
+
+        private ElfDyn[] Dynamic;
+
+        public long ImageBase { get; private set; }
+        public long ImageEnd  { get; private set; }
+
+        public Executable(IElf NsoData, AMemory Memory, long ImageBase)
+        {
+            this.NsoData   = NsoData;
+            this.Memory    = Memory;
+            this.ImageBase = ImageBase;
+            this.ImageEnd  = ImageBase;
+
+            WriteData(ImageBase + NsoData.TextOffset, NsoData.Text, MemoryType.CodeStatic, AMemoryPerm.RX);
+            WriteData(ImageBase + NsoData.ROOffset,   NsoData.RO,   MemoryType.Normal,     AMemoryPerm.Read);
+            WriteData(ImageBase + NsoData.DataOffset, NsoData.Data, MemoryType.Normal,     AMemoryPerm.RW);
+
+            if (NsoData.Text.Count == 0)
+            {
+                return;
+            }
+
+            long Mod0Offset = ImageBase + NsoData.Mod0Offset;
+
+            int  Mod0Magic        = Memory.ReadInt32(Mod0Offset + 0x0);
+            long DynamicOffset    = Memory.ReadInt32(Mod0Offset + 0x4)  + Mod0Offset;
+            long BssStartOffset   = Memory.ReadInt32(Mod0Offset + 0x8)  + Mod0Offset;
+            long BssEndOffset     = Memory.ReadInt32(Mod0Offset + 0xc)  + Mod0Offset;
+            long EhHdrStartOffset = Memory.ReadInt32(Mod0Offset + 0x10) + Mod0Offset;
+            long EhHdrEndOffset   = Memory.ReadInt32(Mod0Offset + 0x14) + Mod0Offset;
+            long ModObjOffset     = Memory.ReadInt32(Mod0Offset + 0x18) + Mod0Offset;
+
+             long BssSize = BssEndOffset - BssStartOffset;
+
+            Memory.Manager.MapPhys(BssStartOffset, BssSize, (int)MemoryType.Normal, AMemoryPerm.RW);
+
+            ImageEnd = BssEndOffset;
+
+            List<ElfDyn> Dynamic = new List<ElfDyn>();
+
+            while (true)
+            {
+                long TagVal = Memory.ReadInt64(DynamicOffset + 0);
+                long Value  = Memory.ReadInt64(DynamicOffset + 8);
+
+                DynamicOffset += 0x10;
+
+                ElfDynTag Tag = (ElfDynTag)TagVal;
+
+                if (Tag == ElfDynTag.DT_NULL)
+                {
+                    break;
+                }
+
+                Dynamic.Add(new ElfDyn(Tag, Value));
+            }
+
+            this.Dynamic = Dynamic.ToArray();
+        }
+
+        private void WriteData(
+            long        Position,
+            IList<byte> Data,
+            MemoryType  Type,
+            AMemoryPerm Perm)
+        {
+            Memory.Manager.MapPhys(Position, Data.Count, (int)Type, Perm);
+
+            for (int Index = 0; Index < Data.Count; Index++)
+            {
+                Memory.WriteByte(Position + Index, Data[Index]);
+            }
+        }
+
+        private ElfRel GetRelocation(long Position)
+        {
+            long Offset = Memory.ReadInt64(Position + 0);
+            long Info   = Memory.ReadInt64(Position + 8);
+            long Addend = Memory.ReadInt64(Position + 16);
+
+            int RelType = (int)(Info >> 0);
+            int SymIdx  = (int)(Info >> 32);
+
+            ElfSym Symbol = GetSymbol(SymIdx);
+
+            return new ElfRel(Offset, Addend, Symbol, (ElfRelType)RelType);
+        }
+
+        private ElfSym GetSymbol(int Index)
+        {
+            long StrTblAddr = ImageBase + GetFirstValue(ElfDynTag.DT_STRTAB);
+            long SymTblAddr = ImageBase + GetFirstValue(ElfDynTag.DT_SYMTAB);
+
+            long SymEntSize = GetFirstValue(ElfDynTag.DT_SYMENT);
+
+            long Position = SymTblAddr + Index * SymEntSize;
+
+            return GetSymbol(Position, StrTblAddr);
+        }
+
+        private ElfSym GetSymbol(long Position, long StrTblAddr)
+        {
+            int  NameIndex = Memory.ReadInt32(Position + 0);
+            int  Info      = Memory.ReadByte(Position + 4);
+            int  Other     = Memory.ReadByte(Position + 5);
+            int  SHIdx     = Memory.ReadInt16(Position + 6);
+            long Value     = Memory.ReadInt64(Position + 8);
+            long Size      = Memory.ReadInt64(Position + 16);
+
+            string Name = string.Empty;
+
+            for (int Chr; (Chr = Memory.ReadByte(StrTblAddr + NameIndex++)) != 0;)
+            {
+                Name += (char)Chr;
+            }
+
+            return new ElfSym(Name, Info, Other, SHIdx, ImageBase, Value, Size);
+        }
+
+        private long GetFirstValue(ElfDynTag Tag)
+        {
+            foreach (ElfDyn Entry in Dynamic)
+            {
+                if (Entry.Tag == Tag)
+                {
+                    return Entry.Value;
+                }
+            }
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Loaders/Executables/IElf.cs b/Ryujinx/Loaders/Executables/IElf.cs
new file mode 100644
index 0000000000..bc8eb1bcfc
--- /dev/null
+++ b/Ryujinx/Loaders/Executables/IElf.cs
@@ -0,0 +1,17 @@
+using System.Collections.ObjectModel;
+
+namespace Ryujinx.Loaders.Executables
+{
+    interface IElf
+    {
+        ReadOnlyCollection<byte> Text { get; }
+        ReadOnlyCollection<byte> RO   { get; }
+        ReadOnlyCollection<byte> Data { get; }
+
+        int Mod0Offset { get; }
+        int TextOffset { get; }
+        int ROOffset   { get; }
+        int DataOffset { get; }
+        int BssSize    { get; }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Loaders/Executables/Nro.cs b/Ryujinx/Loaders/Executables/Nro.cs
new file mode 100644
index 0000000000..5067ba12e4
--- /dev/null
+++ b/Ryujinx/Loaders/Executables/Nro.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Collections.ObjectModel;
+using System.IO;
+
+namespace Ryujinx.Loaders.Executables
+{
+    class Nro : IElf
+    {
+        private byte[] m_Text;
+        private byte[] m_RO;
+        private byte[] m_Data;
+
+        public ReadOnlyCollection<byte> Text => Array.AsReadOnly(m_Text);
+        public ReadOnlyCollection<byte> RO   => Array.AsReadOnly(m_RO);
+        public ReadOnlyCollection<byte> Data => Array.AsReadOnly(m_Data);
+
+        public int Mod0Offset { get; private set; }
+        public int TextOffset { get; private set; }
+        public int ROOffset   { get; private set; }
+        public int DataOffset { get; private set; }
+        public int BssSize    { get; private set; }
+
+        public Nro(Stream Input)
+        {
+            BinaryReader Reader = new BinaryReader(Input);
+
+            Input.Seek(4, SeekOrigin.Begin);
+
+            int Mod0Offset = Reader.ReadInt32();
+            int Padding8   = Reader.ReadInt32();
+            int Paddingc   = Reader.ReadInt32();
+            int NroMagic   = Reader.ReadInt32();
+            int Unknown14  = Reader.ReadInt32();
+            int FileSize   = Reader.ReadInt32();
+            int Unknown1c  = Reader.ReadInt32();
+            int TextOffset = Reader.ReadInt32();
+            int TextSize   = Reader.ReadInt32();
+            int ROOffset   = Reader.ReadInt32();
+            int ROSize     = Reader.ReadInt32();
+            int DataOffset = Reader.ReadInt32();
+            int DataSize   = Reader.ReadInt32();
+            int BssSize    = Reader.ReadInt32();
+
+            this.Mod0Offset = Mod0Offset;
+            this.TextOffset = TextOffset;
+            this.ROOffset   = ROOffset;
+            this.DataOffset = DataOffset;
+            this.BssSize    = BssSize;
+
+            byte[] Read(long Position, int Size)
+            {
+                Input.Seek(Position, SeekOrigin.Begin);
+
+                return Reader.ReadBytes(Size);
+            }
+
+            m_Text = Read(TextOffset, TextSize);
+            m_RO   = Read(ROOffset,   ROSize);
+            m_Data = Read(DataOffset, DataSize);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Loaders/Executables/Nso.cs b/Ryujinx/Loaders/Executables/Nso.cs
new file mode 100644
index 0000000000..ae9a9af627
--- /dev/null
+++ b/Ryujinx/Loaders/Executables/Nso.cs
@@ -0,0 +1,122 @@
+using Ryujinx.Loaders.Compression;
+using System;
+using System.Collections.ObjectModel;
+using System.IO;
+
+namespace Ryujinx.Loaders.Executables
+{
+    class Nso : IElf
+    {
+        private byte[] m_Text;
+        private byte[] m_RO;
+        private byte[] m_Data;
+
+        public ReadOnlyCollection<byte> Text => Array.AsReadOnly(m_Text);
+        public ReadOnlyCollection<byte> RO   => Array.AsReadOnly(m_RO);
+        public ReadOnlyCollection<byte> Data => Array.AsReadOnly(m_Data);
+
+        public int Mod0Offset { get; private set; }
+        public int TextOffset { get; private set; }
+        public int ROOffset   { get; private set; }
+        public int DataOffset { get; private set; }
+        public int BssSize    { get; private set; }
+
+        [Flags]
+        private enum NsoFlags
+        {
+            IsTextCompressed = 1 << 0,
+            IsROCompressed   = 1 << 1,
+            IsDataCompressed = 1 << 2,
+            HasTextHash      = 1 << 3,
+            HasROHash        = 1 << 4,
+            HasDataHash      = 1 << 5
+        }
+
+        public Nso(Stream Input)
+        {
+            BinaryReader Reader = new BinaryReader(Input);
+
+            Input.Seek(0, SeekOrigin.Begin);
+
+            int NsoMagic      = Reader.ReadInt32();
+            int Version       = Reader.ReadInt32();
+            int Reserved      = Reader.ReadInt32();
+            int FlagsMsk      = Reader.ReadInt32();
+            int TextOffset    = Reader.ReadInt32();
+            int TextMemOffset = Reader.ReadInt32();
+            int TextDecSize   = Reader.ReadInt32();
+            int ModNameOffset = Reader.ReadInt32();
+            int ROOffset      = Reader.ReadInt32();
+            int ROMemOffset   = Reader.ReadInt32();
+            int RODecSize     = Reader.ReadInt32();
+            int ModNameSize   = Reader.ReadInt32();
+            int DataOffset    = Reader.ReadInt32();
+            int DataMemOffset = Reader.ReadInt32();
+            int DataDecSize   = Reader.ReadInt32();
+            int BssSize       = Reader.ReadInt32();
+
+            byte[] BuildId = Reader.ReadBytes(0x20);
+
+            int TextSize   = Reader.ReadInt32();
+            int ROSize     = Reader.ReadInt32();
+            int DataSize   = Reader.ReadInt32();
+
+            Input.Seek(0x24, SeekOrigin.Current);
+
+            int DynStrOffset = Reader.ReadInt32();
+            int DynStrSize   = Reader.ReadInt32();
+            int DynSymOffset = Reader.ReadInt32();
+            int DynSymSize   = Reader.ReadInt32();
+
+            byte[] TextHash = Reader.ReadBytes(0x20);
+            byte[] ROHash   = Reader.ReadBytes(0x20);
+            byte[] DataHash = Reader.ReadBytes(0x20);
+
+            NsoFlags Flags = (NsoFlags)FlagsMsk;
+
+            this.TextOffset = TextMemOffset;
+            this.ROOffset   = ROMemOffset;
+            this.DataOffset = DataMemOffset;
+            this.BssSize    = BssSize;
+
+            //Text segment
+            Input.Seek(TextOffset, SeekOrigin.Begin);
+
+            m_Text = Reader.ReadBytes(TextSize);
+
+            if (Flags.HasFlag(NsoFlags.IsTextCompressed) || true)
+            {
+                m_Text = Lz4.Decompress(m_Text, TextDecSize);
+            }
+
+            //Read-only data segment
+            Input.Seek(ROOffset, SeekOrigin.Begin);
+
+            m_RO = Reader.ReadBytes(ROSize);
+
+            if (Flags.HasFlag(NsoFlags.IsROCompressed) || true)
+            {
+                m_RO = Lz4.Decompress(m_RO, RODecSize);
+            }
+
+            //Data segment
+            Input.Seek(DataOffset, SeekOrigin.Begin);
+
+            m_Data = Reader.ReadBytes(DataSize);
+
+            if (Flags.HasFlag(NsoFlags.IsDataCompressed) || true)
+            {
+                m_Data = Lz4.Decompress(m_Data, DataDecSize);
+            }
+
+            using (MemoryStream Text = new MemoryStream(m_Text))
+            {
+                BinaryReader TextReader = new BinaryReader(Text);
+
+                Text.Seek(4, SeekOrigin.Begin);
+
+                Mod0Offset = TextReader.ReadInt32();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/CondVar.cs b/Ryujinx/OsHle/CondVar.cs
new file mode 100644
index 0000000000..02fb8ba37f
--- /dev/null
+++ b/Ryujinx/OsHle/CondVar.cs
@@ -0,0 +1,86 @@
+using ChocolArm64.Memory;
+using System.Collections.Concurrent;
+using System.Threading;
+
+namespace Ryujinx.OsHle
+{
+    class CondVar
+    {
+        private AMemory Memory;
+
+        private long CondVarAddress;
+        private long Timeout;
+
+        private class WaitingThread
+        {
+            public int Handle;
+
+            public ManualResetEvent Event;
+
+            public WaitingThread(int Handle, ManualResetEvent Event)
+            {
+                this.Handle = Handle;
+                this.Event  = Event;
+            }
+        }
+
+        private ConcurrentQueue<WaitingThread> WaitingThreads;
+
+        public CondVar(AMemory Memory, long CondVarAddress, long Timeout)
+        {
+            this.Memory         = Memory;
+            this.CondVarAddress = CondVarAddress;
+            this.Timeout        = Timeout;
+
+            WaitingThreads = new ConcurrentQueue<WaitingThread>();
+        }
+
+        public void WaitForSignal(int ThreadHandle)
+        {
+            int Count = Memory.ReadInt32(CondVarAddress);
+
+            if (Count <= 0)
+            {
+                return;
+            }
+
+            Memory.WriteInt32(CondVarAddress, Count - 1);
+
+            ManualResetEvent Event = new ManualResetEvent(false);
+
+            WaitingThreads.Enqueue(new WaitingThread(ThreadHandle, Event));
+
+            if (Timeout != -1)
+            {
+                Event.WaitOne((int)(Timeout / 1000000));
+            }
+            else
+            {
+                Event.WaitOne();
+            }
+        }
+
+        public void SetSignal(int Count)
+        {
+            if (Count == -1)
+            {
+                while (WaitingThreads.TryDequeue(out WaitingThread Thread))
+                {
+                    Thread.Event.Set();
+                }
+
+                Memory.WriteInt32(CondVarAddress, WaitingThreads.Count);
+            }
+            else
+            {
+                //TODO: Threads with the highest priority needs to be signaled first.
+                if (WaitingThreads.TryDequeue(out WaitingThread Thread))
+                {
+                    Thread.Event.Set();
+                }
+
+                Memory.WriteInt32(CondVarAddress, Count);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Display.cs b/Ryujinx/OsHle/Display.cs
new file mode 100644
index 0000000000..f62430fa89
--- /dev/null
+++ b/Ryujinx/OsHle/Display.cs
@@ -0,0 +1,12 @@
+namespace Ryujinx.OsHle
+{
+    class Display
+    {
+        public string Name { get; private set; }
+
+        public Display(string Name)
+        {
+            this.Name = Name;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/FileDesc.cs b/Ryujinx/OsHle/FileDesc.cs
new file mode 100644
index 0000000000..2a21f5007c
--- /dev/null
+++ b/Ryujinx/OsHle/FileDesc.cs
@@ -0,0 +1,12 @@
+namespace Ryujinx.OsHle
+{
+    class FileDesc
+    {
+        public string Name { get; private set; }
+
+        public FileDesc(string Name)
+        {
+            this.Name = Name;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Handles/HDomain.cs b/Ryujinx/OsHle/Handles/HDomain.cs
new file mode 100644
index 0000000000..fd252f5d7a
--- /dev/null
+++ b/Ryujinx/OsHle/Handles/HDomain.cs
@@ -0,0 +1,58 @@
+using Ryujinx.OsHle.Utilities;
+using System;
+using System.Collections.Generic;
+
+namespace Ryujinx.OsHle.Handles
+{
+    class HDomain : HSession
+    {
+        private Dictionary<int, object> Objects;
+
+        private IdPool ObjIds;
+
+        public HDomain(HSession Session) : base(Session)
+        {
+            Objects = new Dictionary<int, object>();
+
+            ObjIds = new IdPool();
+        }
+
+        public int GenertateObjectId(object Obj)
+        {
+            int Id = ObjIds.GenerateId();
+
+            if (Id == -1)
+            {
+                throw new InvalidOperationException();
+            }
+
+            Objects.Add(Id, Obj);
+
+            return Id;
+        }
+
+        public void DeleteObject(int Id)
+        {
+            if (Objects.TryGetValue(Id, out object Obj))
+            {
+                if (Obj is IDisposable DisposableObj)
+                {
+                    DisposableObj.Dispose();
+                }
+
+                ObjIds.DeleteId(Id);
+                Objects.Remove(Id);
+            }
+        }
+
+        public object GetObject(int Id)
+        {
+            if (Objects.TryGetValue(Id, out object Obj))
+            {
+                return Obj;
+            }
+
+            return null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Handles/HEvent.cs b/Ryujinx/OsHle/Handles/HEvent.cs
new file mode 100644
index 0000000000..d9d0ff4c2d
--- /dev/null
+++ b/Ryujinx/OsHle/Handles/HEvent.cs
@@ -0,0 +1,7 @@
+namespace Ryujinx.OsHle.Handles
+{
+    class HEvent
+    {
+
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Handles/HNvMap.cs b/Ryujinx/OsHle/Handles/HNvMap.cs
new file mode 100644
index 0000000000..3e15eda385
--- /dev/null
+++ b/Ryujinx/OsHle/Handles/HNvMap.cs
@@ -0,0 +1,18 @@
+namespace Ryujinx.OsHle.Handles
+{
+    class HNvMap
+    {
+        public int Id    { get; private set; }
+        public int Size  { get; private set; }
+
+        public int  Align   { get; set; }
+        public int  Kind    { get; set; }
+        public long Address { get; set; }
+        
+        public HNvMap(int Id, int Size)
+        {
+            this.Id   = Id;
+            this.Size = Size;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Handles/HSession.cs b/Ryujinx/OsHle/Handles/HSession.cs
new file mode 100644
index 0000000000..6b90165916
--- /dev/null
+++ b/Ryujinx/OsHle/Handles/HSession.cs
@@ -0,0 +1,27 @@
+namespace Ryujinx.OsHle.Handles
+{
+    class HSession
+    {
+        public string ServiceName { get; private set; }
+
+        public bool IsInitialized { get; private set; }
+
+        public int State { get; set; }
+
+        public HSession(string ServiceName)
+        {
+            this.ServiceName = ServiceName;
+        }
+
+        public HSession(HSession Session)
+        {
+            ServiceName   = Session.ServiceName;
+            IsInitialized = Session.IsInitialized;
+        }
+
+        public void Initialize()
+        {
+            IsInitialized = true;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Handles/HSessionObj.cs b/Ryujinx/OsHle/Handles/HSessionObj.cs
new file mode 100644
index 0000000000..c1e5e41a4c
--- /dev/null
+++ b/Ryujinx/OsHle/Handles/HSessionObj.cs
@@ -0,0 +1,12 @@
+namespace Ryujinx.OsHle.Handles
+{
+    class HSessionObj : HSession
+    {
+        public object Obj { get; private set; }
+
+        public HSessionObj(HSession Session, object Obj) : base(Session)
+        {
+            this.Obj = Obj;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Handles/HSharedMem.cs b/Ryujinx/OsHle/Handles/HSharedMem.cs
new file mode 100644
index 0000000000..acc1e7ebd7
--- /dev/null
+++ b/Ryujinx/OsHle/Handles/HSharedMem.cs
@@ -0,0 +1,12 @@
+namespace Ryujinx.OsHle.Handles
+{
+    class HSharedMem
+    {
+        public long PhysPos { get; private set; }
+
+        public HSharedMem(long PhysPos)
+        {
+            this.PhysPos = PhysPos;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Handles/HThread.cs b/Ryujinx/OsHle/Handles/HThread.cs
new file mode 100644
index 0000000000..9fb0b57bee
--- /dev/null
+++ b/Ryujinx/OsHle/Handles/HThread.cs
@@ -0,0 +1,14 @@
+using ChocolArm64;
+
+namespace Ryujinx.OsHle.Handles
+{
+    class HThread
+    {
+        public AThread Thread { get; private set; }
+
+        public HThread(AThread Thread)
+        {
+            this.Thread = Thread;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Handles/HTransferMem.cs b/Ryujinx/OsHle/Handles/HTransferMem.cs
new file mode 100644
index 0000000000..962d1b6640
--- /dev/null
+++ b/Ryujinx/OsHle/Handles/HTransferMem.cs
@@ -0,0 +1,23 @@
+using ChocolArm64.Memory;
+
+namespace Ryujinx.OsHle.Handles
+{
+    class HTransferMem
+    {
+        public AMemory     Memory { get; private set; }
+        public AMemoryPerm Perm   { get; private set; }
+
+        public long Position { get; private set; }
+        public long Size     { get; private set; }
+        public long PhysPos  { get; private set; }
+
+        public HTransferMem(AMemory Memory, AMemoryPerm Perm, long Position, long Size, long PhysPos)
+        {
+            this.Memory   = Memory;
+            this.Perm     = Perm;
+            this.Position = Position;
+            this.Size     = Size;
+            this.PhysPos  = PhysPos;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Horizon.cs b/Ryujinx/OsHle/Horizon.cs
new file mode 100644
index 0000000000..bae33f8eb3
--- /dev/null
+++ b/Ryujinx/OsHle/Horizon.cs
@@ -0,0 +1,163 @@
+using ChocolArm64.Memory;
+using Ryujinx.Loaders.Executables;
+using Ryujinx.OsHle.Handles;
+using Ryujinx.OsHle.Utilities;
+using System.Collections.Concurrent;
+using System.IO;
+
+namespace Ryujinx.OsHle
+{
+    class Horizon
+    {
+        internal const int HidSize  = 0x40000;
+        internal const int FontSize = 0x50;
+
+        internal int HidHandle  { get; private set; }
+        internal int FontHandle { get; private set; }
+
+        public long HidOffset  { get; private set; }
+        public long FontOffset { get; private set; }
+
+        internal IdPool IdGen    { get; private set; }
+        internal IdPool NvMapIds { get; private set; }
+
+        internal IdPoolWithObj Handles  { get; private set; }
+        internal IdPoolWithObj Fds      { get; private set; }
+        internal IdPoolWithObj Displays { get; private set; }
+
+        public ConcurrentDictionary<long, Mutex>   Mutexes  { get; private set; }
+        public ConcurrentDictionary<long, CondVar> CondVars { get; private set; }
+
+        private ConcurrentDictionary<int, Process> Processes;
+
+        private AMemoryAlloc Allocator;
+
+        private Switch Ns;
+
+        public Horizon(Switch Ns)
+        {
+            this.Ns = Ns;
+
+            IdGen    = new IdPool();
+            NvMapIds = new IdPool();
+
+            Handles  = new IdPoolWithObj();
+            Fds      = new IdPoolWithObj();
+            Displays = new IdPoolWithObj();
+
+            Mutexes  = new ConcurrentDictionary<long, Mutex>();
+            CondVars = new ConcurrentDictionary<long, CondVar>();
+
+            Processes = new ConcurrentDictionary<int, Process>();
+
+            Allocator = new AMemoryAlloc();
+
+            HidOffset  = Allocator.Alloc(HidSize);
+            FontOffset = Allocator.Alloc(FontSize);
+
+            HidHandle  = Handles.GenerateId(new HSharedMem(HidOffset));
+            FontHandle = Handles.GenerateId(new HSharedMem(FontOffset));
+        }
+
+        public void LoadCart(string ExeFsDir, string RomFsFile = null)
+        {
+            if (RomFsFile != null)
+            {
+                Ns.VFs.LoadRomFs(RomFsFile);
+            }
+
+            int ProcessId = IdGen.GenerateId();
+
+            Process MainProcess = new Process(Ns, Allocator, ProcessId);
+
+            void LoadNso(string FileName)
+            {
+                foreach (string File in Directory.GetFiles(ExeFsDir, FileName))
+                {
+                    if (Path.GetExtension(File) != string.Empty)
+                    {
+                        continue;
+                    }
+
+                    using (FileStream Input = new FileStream(File, FileMode.Open))
+                    {
+                        Nso Program = new Nso(Input);
+
+                        MainProcess.LoadProgram(Program);
+                    }
+                }
+            }
+
+            LoadNso("rtld");
+
+            MainProcess.SetEmptyArgs();
+
+            LoadNso("main");
+            LoadNso("subsdk*");
+            LoadNso("sdk");
+
+            MainProcess.InitializeHeap();
+            MainProcess.Run();
+
+            Processes.TryAdd(ProcessId, MainProcess);
+        }
+
+         public void LoadProgram(string FileName)
+        {
+            int ProcessId = IdGen.GenerateId();
+
+            Process MainProcess = new Process(Ns, Allocator, ProcessId);
+
+            using (FileStream Input = new FileStream(FileName, FileMode.Open))
+            {
+                if (Path.GetExtension(FileName).ToLower() == ".nro")
+                {
+                    MainProcess.LoadProgram(new Nro(Input));
+                }
+                else
+                {
+                    MainProcess.LoadProgram(new Nso(Input));
+                }
+            }
+
+            MainProcess.SetEmptyArgs();
+            MainProcess.InitializeHeap();
+            MainProcess.Run();
+
+            Processes.TryAdd(ProcessId, MainProcess);
+        }
+
+        public void StopAllProcesses()
+        {
+            foreach (Process Process in Processes.Values)
+            {
+                Process.StopAllThreads();
+            }
+        }
+
+        internal bool TryGetProcess(int ProcessId, out Process Process)
+        {
+            if (!Processes.TryGetValue(ProcessId, out Process))
+            {
+                return false;
+            }
+
+            return true;
+        }
+
+        internal void CloseHandle(int Handle)
+        {
+            object HndData = Handles.GetData<object>(Handle);
+
+            if (HndData is HTransferMem TransferMem)
+            {
+                TransferMem.Memory.Manager.Reprotect(
+                    TransferMem.Position,
+                    TransferMem.Size,
+                    TransferMem.Perm);
+            }
+
+            Handles.Delete(Handle);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Ipc/IpcBuffDesc.cs b/Ryujinx/OsHle/Ipc/IpcBuffDesc.cs
new file mode 100644
index 0000000000..41b1efe031
--- /dev/null
+++ b/Ryujinx/OsHle/Ipc/IpcBuffDesc.cs
@@ -0,0 +1,27 @@
+using System.IO;
+
+namespace Ryujinx.OsHle.Ipc
+{
+    struct IpcBuffDesc
+    {
+        public long Position { get; private set; }
+        public long Size     { get; private set; }
+        public int  Flags    { get; private set; }
+
+        public IpcBuffDesc(BinaryReader Reader)
+        {
+            long Word0 = Reader.ReadUInt32();
+            long Word1 = Reader.ReadUInt32();
+            long Word2 = Reader.ReadUInt32();
+
+            Position  =  Word1;
+            Position |= (Word2 <<  4) & 0x0f00000000;
+            Position |= (Word2 << 34) & 0x7000000000;
+
+            Size  =  Word0;
+            Size |= (Word2 << 8) & 0xf00000000;
+
+            Flags = (int)Word2 & 3;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Ipc/IpcDomCmd.cs b/Ryujinx/OsHle/Ipc/IpcDomCmd.cs
new file mode 100644
index 0000000000..035671859a
--- /dev/null
+++ b/Ryujinx/OsHle/Ipc/IpcDomCmd.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.OsHle.Ipc
+{
+    enum IpcDomCmd
+    {
+        SendMsg   = 1,
+        DeleteObj = 2
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Ipc/IpcHandleDesc.cs b/Ryujinx/OsHle/Ipc/IpcHandleDesc.cs
new file mode 100644
index 0000000000..fa5d7e1de7
--- /dev/null
+++ b/Ryujinx/OsHle/Ipc/IpcHandleDesc.cs
@@ -0,0 +1,90 @@
+using System;
+using System.IO;
+
+namespace Ryujinx.OsHle.Ipc
+{
+    class IpcHandleDesc
+    {
+        public bool HasPId { get; private set; }
+
+        public long PId { get; private set; }
+
+        public int[] ToCopy { get; private set; }
+        public int[] ToMove { get; private set; }
+
+        public IpcHandleDesc(BinaryReader Reader)
+        {
+            int Word = Reader.ReadInt32();
+
+            HasPId = (Word & 1) != 0;
+
+            ToCopy = new int[(Word >> 1) & 0xf];
+            ToMove = new int[(Word >> 5) & 0xf];
+
+            PId = HasPId ? Reader.ReadInt64() : 0;
+
+            for (int Index = 0; Index < ToCopy.Length; Index++)
+            {
+                ToCopy[Index] = Reader.ReadInt32();
+            }
+
+            for (int Index = 0; Index < ToMove.Length; Index++)
+            {
+                ToMove[Index] = Reader.ReadInt32();
+            }
+        }
+
+        public IpcHandleDesc(int[] Copy, int[] Move)
+        {
+            ToCopy = Copy ?? throw new ArgumentNullException(nameof(Copy));
+            ToMove = Move ?? throw new ArgumentNullException(nameof(Move));
+        }
+
+        public IpcHandleDesc(int[] Copy, int[] Move, long PId) : this(Copy, Move)
+        {
+            this.PId = PId;
+
+            HasPId = true;
+        }
+
+        public static IpcHandleDesc MakeCopy(int Handle) => new IpcHandleDesc(
+                new int[] { Handle },
+                new int[0]);
+
+        public static IpcHandleDesc MakeMove(int Handle) => new IpcHandleDesc(
+                new int[0],
+                new int[] { Handle });
+
+        public byte[] GetBytes()
+        {
+            using (MemoryStream MS = new MemoryStream())
+            {
+                BinaryWriter Writer = new BinaryWriter(MS);
+
+                int Word = HasPId ? 1 : 0;
+
+                Word |= (ToCopy.Length & 0xf) << 1;
+                Word |= (ToMove.Length & 0xf) << 5;
+
+                Writer.Write(Word);
+
+                if (HasPId)
+                {
+                    Writer.Write((long)PId);
+                }
+
+                foreach (int Handle in ToCopy)
+                {
+                    Writer.Write(Handle);
+                }
+
+                foreach (int Handle in ToMove)
+                {
+                    Writer.Write(Handle);
+                }
+
+                return MS.ToArray();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Ipc/IpcHandler.cs b/Ryujinx/OsHle/Ipc/IpcHandler.cs
new file mode 100644
index 0000000000..444b1022b8
--- /dev/null
+++ b/Ryujinx/OsHle/Ipc/IpcHandler.cs
@@ -0,0 +1,358 @@
+using ChocolArm64.Memory;
+using Ryujinx.OsHle.Handles;
+using Ryujinx.OsHle.Objects;
+using Ryujinx.OsHle.Services;
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace Ryujinx.OsHle.Ipc
+{
+    static class IpcHandler
+    {
+        private delegate long ServiceProcessRequest(ServiceCtx Context);
+
+        private static Dictionary<(string, int), ServiceProcessRequest> ServiceCmds =
+                   new Dictionary<(string, int), ServiceProcessRequest>()
+        {
+            { ( "acc:u0",      3), Service.AccU0ListOpenUsers                       },
+            { ( "acc:u0",      5), Service.AccU0GetProfile                          },
+            { ( "acc:u0",    100), Service.AccU0InitializeApplicationInfo           },
+            { ( "acc:u0",    101), Service.AccU0GetBaasAccountManagerForApplication },
+            { ( "apm",         0), Service.ApmOpenSession                           },
+            { ( "appletOE",    0), Service.AppletOpenApplicationProxy               },
+            { ( "audout:u",    0), Service.AudOutListAudioOuts                      },
+            { ( "audout:u",    1), Service.AudOutOpenAudioOut                       },
+            { ( "audren:u",    0), Service.AudRenOpenAudioRenderer                  },
+            { ( "audren:u",    1), Service.AudRenGetAudioRendererWorkBufferSize     },
+            { ( "friend:a",    0), Service.FriendCreateFriendService                },
+            { ( "fsp-srv",     1), Service.FspSrvInitialize                         },
+            { ( "fsp-srv",    51), Service.FspSrvMountSaveData                      },
+            { ( "fsp-srv",   200), Service.FspSrvOpenDataStorageByCurrentProcess    },
+            { ( "fsp-srv",   203), Service.FspSrvOpenRomStorage                     },
+            { ( "fsp-srv",  1005), Service.FspSrvGetGlobalAccessLogMode             },
+            { ( "hid",         0), Service.HidCreateAppletResource                  },
+            { ( "hid",        11), Service.HidActivateTouchScreen                   },
+            { ( "hid",       100), Service.HidSetSupportedNpadStyleSet              },
+            { ( "hid",       102), Service.HidSetSupportedNpadIdType                },
+            { ( "hid",       103), Service.HidActivateNpad                          },
+            { ( "hid",       120), Service.HidSetNpadJoyHoldType                    },
+            { ( "lm",          0), Service.LmInitialize                             },
+            { ( "nvdrv",       0), Service.NvDrvOpen                                },
+            { ( "nvdrv",       1), Service.NvDrvIoctl                               },
+            { ( "nvdrv",       2), Service.NvDrvClose                               },
+            { ( "nvdrv",       3), Service.NvDrvInitialize                          },
+            { ( "nvdrv",       4), Service.NvDrvQueryEvent                          },
+            { ( "nvdrv:a",     0), Service.NvDrvOpen                                },
+            { ( "nvdrv:a",     1), Service.NvDrvIoctl                               },
+            { ( "nvdrv:a",     2), Service.NvDrvClose                               },
+            { ( "nvdrv:a",     3), Service.NvDrvInitialize                          },
+            { ( "nvdrv:a",     4), Service.NvDrvQueryEvent                          },
+            { ( "pctl:a",      0), Service.PctlCreateService                        },
+            { ( "pl:u",        1), Service.PlGetLoadState                           },
+            { ( "pl:u",        2), Service.PlGetFontSize                            },
+            { ( "pl:u",        3), Service.PlGetSharedMemoryAddressOffset           },
+            { ( "pl:u",        4), Service.PlGetSharedMemoryNativeHandle            },
+            { ( "set",         1), Service.SetGetAvailableLanguageCodes             },
+            { ( "sm:",         0), Service.SmInitialize                             },
+            { ( "sm:",         1), Service.SmGetService                             },
+            { ( "time:u",      0), Service.TimeGetStandardUserSystemClock           },
+            { ( "time:u",      1), Service.TimeGetStandardNetworkSystemClock        },
+            { ( "time:u",      2), Service.TimeGetStandardSteadyClock               },
+            { ( "time:u",      3), Service.TimeGetTimeZoneService                   },
+            { ( "time:s",      0), Service.TimeGetStandardUserSystemClock           },
+            { ( "time:s",      1), Service.TimeGetStandardNetworkSystemClock        },
+            { ( "time:s",      2), Service.TimeGetStandardSteadyClock               },
+            { ( "time:s",      3), Service.TimeGetTimeZoneService                   },
+            { ( "vi:m",        2), Service.ViGetDisplayService                      },
+        };
+
+        private static Dictionary<(Type, int), ServiceProcessRequest> ObjectCmds =
+                   new Dictionary<(Type, int), ServiceProcessRequest>()
+        {
+            //IManagerForApplication
+            { (typeof(AccIManagerForApplication), 0), AccIManagerForApplication.CheckAvailability },
+            { (typeof(AccIManagerForApplication), 1), AccIManagerForApplication.GetAccountId      },
+
+            //IProfile
+            { (typeof(AccIProfile), 1), AccIProfile.GetBase },
+
+            //IApplicationFunctions
+            { (typeof(AmIApplicationFunctions),  1), AmIApplicationFunctions.PopLaunchParameter },
+            { (typeof(AmIApplicationFunctions), 20), AmIApplicationFunctions.EnsureSaveData     },
+            { (typeof(AmIApplicationFunctions), 21), AmIApplicationFunctions.GetDesiredLanguage },
+
+            //IApplicationProxy
+            { (typeof(AmIApplicationProxy),    0), AmIApplicationProxy.GetCommonStateGetter    },
+            { (typeof(AmIApplicationProxy),    1), AmIApplicationProxy.GetSelfController       },
+            { (typeof(AmIApplicationProxy),    2), AmIApplicationProxy.GetWindowController     },
+            { (typeof(AmIApplicationProxy),    3), AmIApplicationProxy.GetAudioController      },
+            { (typeof(AmIApplicationProxy),    4), AmIApplicationProxy.GetDisplayController    },
+            { (typeof(AmIApplicationProxy),   11), AmIApplicationProxy.GetLibraryAppletCreator },
+            { (typeof(AmIApplicationProxy),   20), AmIApplicationProxy.GetApplicationFunctions },
+            { (typeof(AmIApplicationProxy), 1000), AmIApplicationProxy.GetDebugFunctions       },
+
+            //ICommonStateGetter
+            { (typeof(AmICommonStateGetter), 0), AmICommonStateGetter.GetEventHandle       },
+            { (typeof(AmICommonStateGetter), 1), AmICommonStateGetter.ReceiveMessage       },
+            { (typeof(AmICommonStateGetter), 5), AmICommonStateGetter.GetOperationMode     },
+            { (typeof(AmICommonStateGetter), 6), AmICommonStateGetter.GetPerformanceMode   },
+            { (typeof(AmICommonStateGetter), 9), AmICommonStateGetter.GetCurrentFocusState },
+
+            //ISelfController
+            { (typeof(AmISelfController), 11), AmISelfController.SetOperationModeChangedNotification   },
+            { (typeof(AmISelfController), 12), AmISelfController.SetPerformanceModeChangedNotification },
+            { (typeof(AmISelfController), 13), AmISelfController.SetFocusHandlingMode                  },
+
+            //IStorage
+            { (typeof(AmIStorage), 0), AmIStorage.Open },
+
+            //IStorageAccessor
+            { (typeof(AmIStorageAccessor),  0), AmIStorageAccessor.GetSize },
+            { (typeof(AmIStorageAccessor), 11), AmIStorageAccessor.Read    },
+
+            //IWindowController
+            { (typeof(AmIWindowController),  1), AmIWindowController.GetAppletResourceUserId },
+            { (typeof(AmIWindowController), 10), AmIWindowController.AcquireForegroundRights },
+
+            //ISession
+            { (typeof(ApmISession), 0), ApmISession.SetPerformanceConfiguration },
+
+            //IAudioRenderer
+            { (typeof(AudIAudioRenderer), 4), AudIAudioRenderer.RequestUpdateAudioRenderer },
+            { (typeof(AudIAudioRenderer), 5), AudIAudioRenderer.StartAudioRenderer         },
+            { (typeof(AudIAudioRenderer), 7), AudIAudioRenderer.QuerySystemEvent           },
+
+            //IFile
+            { (typeof(FspSrvIFile), 0), FspSrvIFile.Read  },
+            { (typeof(FspSrvIFile), 1), FspSrvIFile.Write },
+
+            //IFileSystem
+            { (typeof(FspSrvIFileSystem),  7), FspSrvIFileSystem.GetEntryType },
+            { (typeof(FspSrvIFileSystem),  8), FspSrvIFileSystem.OpenFile     },
+            { (typeof(FspSrvIFileSystem), 10), FspSrvIFileSystem.Commit       },
+
+            //IStorage
+            { (typeof(FspSrvIStorage), 0), FspSrvIStorage.Read },
+
+            //IAppletResource
+            { (typeof(HidIAppletResource), 0), HidIAppletResource.GetSharedMemoryHandle },
+
+            //ISystemClock
+            { (typeof(TimeISystemClock), 0), TimeISystemClock.GetCurrentTime },
+
+            //IApplicationDisplayService
+            { (typeof(ViIApplicationDisplayService),  100), ViIApplicationDisplayService.GetRelayService          },
+            { (typeof(ViIApplicationDisplayService),  101), ViIApplicationDisplayService.GetSystemDisplayService  },
+            { (typeof(ViIApplicationDisplayService),  102), ViIApplicationDisplayService.GetManagerDisplayService },
+            { (typeof(ViIApplicationDisplayService), 1010), ViIApplicationDisplayService.OpenDisplay              },
+            { (typeof(ViIApplicationDisplayService), 2020), ViIApplicationDisplayService.OpenLayer                },
+            { (typeof(ViIApplicationDisplayService), 2030), ViIApplicationDisplayService.CreateStrayLayer         },
+            { (typeof(ViIApplicationDisplayService), 2101), ViIApplicationDisplayService.SetLayerScalingMode      },
+            { (typeof(ViIApplicationDisplayService), 5202), ViIApplicationDisplayService.GetDisplayVSyncEvent     },
+
+            //IHOSBinderDriver
+            { (typeof(ViIHOSBinderDriver), 0), ViIHOSBinderDriver.TransactParcel  },
+            { (typeof(ViIHOSBinderDriver), 1), ViIHOSBinderDriver.AdjustRefcount  },
+            { (typeof(ViIHOSBinderDriver), 2), ViIHOSBinderDriver.GetNativeHandle },
+
+            //IManagerDisplayService
+            { (typeof(ViIManagerDisplayService), 2010), ViIManagerDisplayService.CreateManagedLayer },
+            { (typeof(ViIManagerDisplayService), 6000), ViIManagerDisplayService.AddToLayerStack    },
+
+            //ISystemDisplayService
+            { (typeof(ViISystemDisplayService), 2205), ViISystemDisplayService.SetLayerZ },
+        };
+
+        private const long SfciMagic = 'S' << 0 | 'F' << 8 | 'C' << 16 | 'I' << 24;
+        private const long SfcoMagic = 'S' << 0 | 'F' << 8 | 'C' << 16 | 'O' << 24;
+
+        public static void ProcessRequest(
+            Switch     Ns,
+            AMemory    Memory,
+            HSession   Session,
+            IpcMessage Request,
+            long       CmdPtr,
+            int        HndId)
+        {
+            IpcMessage Response = new IpcMessage(Request.IsDomain);
+
+            using (MemoryStream Raw = new MemoryStream(Request.RawData))
+            {
+                BinaryReader ReqReader = new BinaryReader(Raw);
+
+                if (Request.Type == IpcMessageType.Request)
+                {
+                    string ServiceName = Session.ServiceName;
+
+                    ServiceProcessRequest ProcReq = null;
+
+                    bool IgnoreNullPR = false;
+
+                    if (Session is HDomain Dom)
+                    {
+                        if (Request.DomCmd == IpcDomCmd.SendMsg)
+                        {
+                            long Magic =      ReqReader.ReadInt64();
+                            int  CmdId = (int)ReqReader.ReadInt64();
+
+                            object Obj = Dom.GetObject(Request.DomObjId);
+
+                            if (Obj is HDomain)
+                            {
+                                ServiceCmds.TryGetValue((ServiceName, CmdId), out ProcReq);
+                            }
+                            else if (Obj != null)
+                            {
+                                ObjectCmds.TryGetValue((Obj.GetType(), CmdId), out ProcReq);
+                            }
+                        }
+                        else if (Request.DomCmd == IpcDomCmd.DeleteObj)
+                        {
+                            Dom.DeleteObject(Request.DomObjId);
+
+                            Response = FillResponse(Response, 0);
+
+                            IgnoreNullPR = true;
+                        }
+                    }
+                    else
+                    {
+                        long Magic =      ReqReader.ReadInt64();
+                        int  CmdId = (int)ReqReader.ReadInt64();
+
+                        if (Session is HSessionObj)
+                        {
+                            object Obj = ((HSessionObj)Session).Obj;
+
+                            ObjectCmds.TryGetValue((Obj.GetType(), CmdId), out ProcReq);
+                        }
+                        else
+                        {
+                            ServiceCmds.TryGetValue((ServiceName, CmdId), out ProcReq);
+                        }                        
+                    }
+
+                    if (ProcReq != null)
+                    {
+                        using (MemoryStream ResMS = new MemoryStream())
+                        {
+                            BinaryWriter ResWriter = new BinaryWriter(ResMS);
+
+                            ServiceCtx Context = new ServiceCtx(
+                                Ns,
+                                Memory,
+                                Session,
+                                Request,
+                                Response,
+                                ReqReader,
+                                ResWriter);
+
+                            long Result = ProcReq(Context);
+
+                            Response = FillResponse(Response, Result, ResMS.ToArray());
+                        }
+                    }
+                    else if (!IgnoreNullPR)
+                    {   
+                        throw new NotImplementedException(ServiceName);
+                    }
+                }
+                else if (Request.Type == IpcMessageType.Control)
+                {
+                    long Magic = ReqReader.ReadInt64();
+                    long CmdId = ReqReader.ReadInt64();
+
+                    switch (CmdId)
+                    {
+                        case 0: Request = IpcConvertSessionToDomain(Ns, Session, Response, HndId); break;
+                        case 3: Request = IpcQueryBufferPointerSize(Response);                     break;
+                        case 4: Request = IpcDuplicateSessionEx(Ns, Session, Response, ReqReader); break;
+
+                        default: throw new NotImplementedException(CmdId.ToString());
+                    }
+                }
+                else if (Request.Type == IpcMessageType.Unknown2)
+                {
+                    //TODO
+                }
+                else
+                {
+                    throw new NotImplementedException(Request.Type.ToString());
+                }
+
+                AMemoryHelper.WriteBytes(Memory, CmdPtr, Response.GetBytes(CmdPtr));
+            }
+        }
+
+        private static IpcMessage IpcConvertSessionToDomain(
+            Switch     Ns,
+            HSession   Session,
+            IpcMessage Response,
+            int        HndId)
+        {
+            HDomain Dom = new HDomain(Session);
+
+            Ns.Os.Handles.ReplaceData(HndId, Dom);
+
+            return FillResponse(Response, 0, Dom.GenertateObjectId(Dom));
+        }
+
+        private static IpcMessage IpcDuplicateSessionEx(
+            Switch       Ns,
+            HSession     Session,
+            IpcMessage   Response,
+            BinaryReader ReqReader)
+        {
+            int Unknown = ReqReader.ReadInt32();
+
+            int Handle = Ns.Os.Handles.GenerateId(Session);
+
+            Response.HandleDesc = IpcHandleDesc.MakeMove(Handle);
+
+            return FillResponse(Response, 0);
+        }
+
+        private static IpcMessage IpcQueryBufferPointerSize(IpcMessage Response)
+        {
+            return FillResponse(Response, 0, 0x500);
+        }
+
+        private static IpcMessage FillResponse(IpcMessage Response, long Result, params int[] Values)
+        {
+            using (MemoryStream MS = new MemoryStream())
+            {
+                BinaryWriter Writer = new BinaryWriter(MS);
+
+                foreach (int Value in Values)
+                {
+                    Writer.Write(Value);
+                }
+
+                return FillResponse(Response, Result, MS.ToArray());
+            }
+        }
+
+        private static IpcMessage FillResponse(IpcMessage Response, long Result, byte[] Data = null)
+        {
+            Response.Type = IpcMessageType.Response;
+
+            using (MemoryStream MS = new MemoryStream())
+            {
+                BinaryWriter Writer = new BinaryWriter(MS);
+
+                Writer.Write(SfcoMagic);
+                Writer.Write(Result);
+
+                if (Data != null)
+                {
+                    Writer.Write(Data);
+                }
+
+                Response.RawData = MS.ToArray();
+            }
+
+            return Response;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Ipc/IpcMessage.cs b/Ryujinx/OsHle/Ipc/IpcMessage.cs
new file mode 100644
index 0000000000..407fd65f9b
--- /dev/null
+++ b/Ryujinx/OsHle/Ipc/IpcMessage.cs
@@ -0,0 +1,231 @@
+using System.Collections.Generic;
+using System.IO;
+
+namespace Ryujinx.OsHle.Ipc
+{
+    class IpcMessage
+    {
+        public IpcMessageType Type { get; set; }
+
+        public IpcHandleDesc HandleDesc { get; set; }
+
+        public List<IpcPtrBuffDesc>      PtrBuff      { get; private set; }
+        public List<IpcBuffDesc>         SendBuff     { get; private set; }
+        public List<IpcBuffDesc>         ReceiveBuff  { get; private set; }
+        public List<IpcBuffDesc>         ExchangeBuff { get; private set; }
+        public List<IpcRecvListBuffDesc> RecvListBuff { get; private set; }
+
+        public List<int> ResponseObjIds { get; private set; }    
+
+        public bool      IsDomain { get; private set; }
+        public IpcDomCmd DomCmd   { get; private set; }
+        public int       DomObjId { get; private set; }
+
+        public byte[] RawData { get; set; }
+
+        public IpcMessage()
+        {
+            PtrBuff      = new List<IpcPtrBuffDesc>();
+            SendBuff     = new List<IpcBuffDesc>();
+            ReceiveBuff  = new List<IpcBuffDesc>();
+            ExchangeBuff = new List<IpcBuffDesc>();
+            RecvListBuff = new List<IpcRecvListBuffDesc>();
+
+            ResponseObjIds = new List<int>();
+        }
+
+        public IpcMessage(bool Domain) : this()
+        {
+            IsDomain = Domain;
+        }
+
+        public IpcMessage(byte[] Data, long CmdPtr, bool Domain) : this()
+        {
+            using (MemoryStream MS = new MemoryStream(Data))
+            {
+                BinaryReader Reader = new BinaryReader(MS);
+
+                Initialize(Reader, CmdPtr, Domain);
+            }
+        }
+
+        private void Initialize(BinaryReader Reader, long CmdPtr, bool Domain)
+        {
+            IsDomain = Domain;
+
+            int Word0 = Reader.ReadInt32();
+            int Word1 = Reader.ReadInt32();
+
+            Type = (IpcMessageType)(Word0 & 0xffff);
+
+            int  PtrBuffCount  = (Word0 >> 16) & 0xf;
+            int  SendBuffCount = (Word0 >> 20) & 0xf;
+            int  RecvBuffCount = (Word0 >> 24) & 0xf;
+            int  XchgBuffCount = (Word0 >> 28) & 0xf;
+
+            int  RawDataSize   =  (Word1 >>  0) & 0x3ff;
+            int  RecvListFlags =  (Word1 >> 10) & 0xf;
+            bool HndDescEnable = ((Word1 >> 31) & 0x1) != 0;
+
+            if (HndDescEnable)
+            {
+                HandleDesc = new IpcHandleDesc(Reader);
+            }
+
+            for (int Index = 0; Index < PtrBuffCount; Index++)
+            {
+                PtrBuff.Add(new IpcPtrBuffDesc(Reader));
+            }
+
+            void ReadBuff(List<IpcBuffDesc> Buff, int Count)
+            {
+                for (int Index = 0; Index < Count; Index++)
+                {
+                    Buff.Add(new IpcBuffDesc(Reader));
+                }
+            }
+
+            ReadBuff(SendBuff,     SendBuffCount);
+            ReadBuff(ReceiveBuff,  RecvBuffCount);
+            ReadBuff(ExchangeBuff, XchgBuffCount);
+
+            RawDataSize *= 4;
+
+            long RecvListPos = Reader.BaseStream.Position + RawDataSize;
+
+            long Pad0 = GetPadSize16(Reader.BaseStream.Position + CmdPtr);
+
+            Reader.BaseStream.Seek(Pad0, SeekOrigin.Current);            
+
+            int RecvListCount = RecvListFlags - 2;
+
+            if (RecvListCount == 0)
+            {
+                RecvListCount = 1;
+            }
+            else if (RecvListCount < 0)
+            {
+                RecvListCount = 0;
+            }
+
+            if (Domain)
+            {
+                int DomWord0 = Reader.ReadInt32();
+
+                DomCmd = (IpcDomCmd)(DomWord0 & 0xff);
+
+                RawDataSize = (DomWord0 >> 16) & 0xffff;
+
+                DomObjId = Reader.ReadInt32();
+
+                Reader.ReadInt64(); //Padding
+            }
+
+            RawData = Reader.ReadBytes(RawDataSize);
+
+            Reader.BaseStream.Seek(RecvListPos, SeekOrigin.Begin);
+
+            for (int Index = 0; Index < RecvListCount; Index++)
+            {
+                RecvListBuff.Add(new IpcRecvListBuffDesc(Reader));
+            }
+        }
+
+        public byte[] GetBytes(long CmdPtr)
+        {
+            //todo
+            using (MemoryStream MS = new MemoryStream())
+            {
+                BinaryWriter Writer = new BinaryWriter(MS);
+
+                int Word0;
+                int Word1;
+
+                Word0  = (int)Type;
+                Word0 |= (PtrBuff.Count      & 0xf) << 16;
+                Word0 |= (SendBuff.Count     & 0xf) << 20;
+                Word0 |= (ReceiveBuff.Count  & 0xf) << 24;
+                Word0 |= (ExchangeBuff.Count & 0xf) << 28;
+
+                byte[] HandleData = new byte[0];
+
+                if (HandleDesc != null)
+                {
+                    HandleData = HandleDesc.GetBytes();
+                }
+
+                int DataLength = RawData?.Length ?? 0;
+
+                int Pad0 = (int)GetPadSize16(CmdPtr + 8 + HandleData.Length);
+
+                //Apparently, padding after Raw Data is 16 bytes, however when there is
+                //padding before Raw Data too, we need to subtract the size of this padding.
+                //This is the weirdest padding I've seen so far...
+                int Pad1 = 0x10 - Pad0;
+
+                DataLength = (DataLength + Pad0 + Pad1 + (IsDomain ? 0x10 : 0)) / 4;
+
+                DataLength += ResponseObjIds.Count;
+
+                Word1 = DataLength & 0x3ff;
+
+                if (HandleDesc != null)
+                {
+                    Word1 |= 1 << 31;
+                }
+
+                Writer.Write(Word0);
+                Writer.Write(Word1);
+                Writer.Write(HandleData);
+
+                MS.Seek(Pad0, SeekOrigin.Current);
+
+                if (IsDomain)
+                {
+                    Writer.Write(ResponseObjIds.Count);
+                    Writer.Write(0);
+                    Writer.Write(0L);
+                }
+
+                if (RawData != null)
+                {
+                    Writer.Write(RawData);
+                }
+
+                foreach (int Id in ResponseObjIds)
+                {
+                    Writer.Write(Id);
+                }
+
+                Writer.Write(new byte[Pad1]);
+
+                return MS.ToArray();
+            }
+        }
+
+        private long GetPadSize16(long Position)
+        {
+            if ((Position & 0xf) != 0)
+            {
+                return 0x10 - (Position & 0xf);
+            }
+
+            return 0;
+        }
+
+        public long GetSendBuffPtr()
+        {
+            if (SendBuff.Count > 0 && SendBuff[0].Position != 0)
+            {
+                return SendBuff[0].Position;
+            }
+
+            if (PtrBuff.Count > 0 && PtrBuff[0].Position != 0)
+            {
+                return PtrBuff[0].Position;
+            }
+
+            return -1;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Ipc/IpcMessageType.cs b/Ryujinx/OsHle/Ipc/IpcMessageType.cs
new file mode 100644
index 0000000000..b0e283de74
--- /dev/null
+++ b/Ryujinx/OsHle/Ipc/IpcMessageType.cs
@@ -0,0 +1,10 @@
+namespace Ryujinx.OsHle.Ipc
+{
+    enum IpcMessageType
+    {
+        Response = 0,
+        Unknown2 = 2,
+        Request  = 4,
+        Control  = 5
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Ipc/IpcPtrBuffDesc.cs b/Ryujinx/OsHle/Ipc/IpcPtrBuffDesc.cs
new file mode 100644
index 0000000000..414d71f456
--- /dev/null
+++ b/Ryujinx/OsHle/Ipc/IpcPtrBuffDesc.cs
@@ -0,0 +1,26 @@
+using System.IO;
+
+namespace Ryujinx.OsHle.Ipc
+{
+    struct IpcPtrBuffDesc
+    {
+        public long  Position { get; private set; }
+        public int   Index    { get; private set; }
+        public short Size     { get; private set; }
+
+        public IpcPtrBuffDesc(BinaryReader Reader)
+        {
+            long Word0 = Reader.ReadUInt32();
+            long Word1 = Reader.ReadUInt32();
+
+            Position  =  Word1;
+            Position |= (Word0 << 20) & 0x0f00000000;
+            Position |= (Word0 << 30) & 0x7000000000;
+
+            Index  = ((int)Word0 >> 0) & 0x03f;
+            Index |= ((int)Word0 >> 3) & 0x1c0;
+
+            Size = (short)(Word0 >> 16);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Ipc/IpcRecvListBuffDesc.cs b/Ryujinx/OsHle/Ipc/IpcRecvListBuffDesc.cs
new file mode 100644
index 0000000000..8180b8dd39
--- /dev/null
+++ b/Ryujinx/OsHle/Ipc/IpcRecvListBuffDesc.cs
@@ -0,0 +1,19 @@
+using System.IO;
+
+namespace Ryujinx.OsHle.Ipc
+{
+    struct IpcRecvListBuffDesc
+    {
+        public long  Position { get; private set; }
+        public short Size     { get; private set; }
+
+        public IpcRecvListBuffDesc(BinaryReader Reader)
+        {
+            long Value = Reader.ReadInt64();
+
+            Position = Value & 0xffffffffffff;
+
+            Size = (short)(Value >> 48);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/MemoryInfo.cs b/Ryujinx/OsHle/MemoryInfo.cs
new file mode 100644
index 0000000000..adf863b274
--- /dev/null
+++ b/Ryujinx/OsHle/MemoryInfo.cs
@@ -0,0 +1,28 @@
+using ChocolArm64.Memory;
+
+namespace Ryujinx.OsHle
+{
+    struct MemoryInfo
+    {
+        public long BaseAddress;
+        public long Size;
+        public int  MemType;
+        public int  MemAttr;
+        public int  MemPerm;
+        public int  IpcRefCount;
+        public int  DeviceRefCount;
+        public int  Padding; //SBZ
+
+        public MemoryInfo(AMemoryMapInfo MapInfo)
+        {
+            BaseAddress    = MapInfo.Position;
+            Size           = MapInfo.Size;
+            MemType        = MapInfo.Type;
+            MemAttr        = 0;
+            MemPerm        = (int)MapInfo.Perm;
+            IpcRefCount    = 0;
+            DeviceRefCount = 0;
+            Padding        = 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/MemoryType.cs b/Ryujinx/OsHle/MemoryType.cs
new file mode 100644
index 0000000000..b1ac330ae5
--- /dev/null
+++ b/Ryujinx/OsHle/MemoryType.cs
@@ -0,0 +1,25 @@
+namespace Ryujinx.OsHle
+{
+    enum MemoryType
+    {
+        Unmapped               = 0,
+        Io                     = 1,
+        Normal                 = 2,
+        CodeStatic             = 3,
+        CodeMutable            = 4,
+        Heap                   = 5,
+        SharedMemory           = 6,
+        ModCodeStatic          = 8,
+        ModCodeMutable         = 9,
+        IpcBuffer0             = 10,
+        MappedMemory           = 11,
+        ThreadLocal            = 12,
+        TransferMemoryIsolated = 13,
+        TransferMemory         = 14,
+        ProcessMemory          = 15,
+        Reserved               = 16,
+        IpcBuffer1             = 17,
+        IpcBuffer3             = 18,
+        KernelStack            = 19
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Mutex.cs b/Ryujinx/OsHle/Mutex.cs
new file mode 100644
index 0000000000..99d12b28e4
--- /dev/null
+++ b/Ryujinx/OsHle/Mutex.cs
@@ -0,0 +1,89 @@
+using ChocolArm64;
+using ChocolArm64.Memory;
+using System.Threading;
+
+namespace Ryujinx.OsHle
+{
+    class Mutex
+    {
+        private const int MutexHasListenersMask = 0x40000000;
+
+        private AMemory Memory;
+
+        private long MutexAddress;
+
+        private int CurrRequestingThreadHandle;
+
+        private int HighestPriority;
+
+        private ManualResetEvent ThreadEvent;
+
+        private object EnterWaitLock;
+
+        public Mutex(AMemory Memory, long MutexAddress)
+        {
+            this.Memory       = Memory;
+            this.MutexAddress = MutexAddress;
+
+            ThreadEvent = new ManualResetEvent(false);
+
+            EnterWaitLock = new object();
+        }
+
+        public void WaitForLock(AThread RequestingThread, int RequestingThreadHandle)
+        {
+            lock (EnterWaitLock)
+            {               
+                int CurrentThreadHandle = Memory.ReadInt32(MutexAddress) & ~MutexHasListenersMask;
+
+                if (CurrentThreadHandle == RequestingThreadHandle ||
+                    CurrentThreadHandle == 0)
+                {
+                    return;
+                }
+
+                if (CurrRequestingThreadHandle == 0 || RequestingThread.Priority < HighestPriority)
+                {
+                    CurrRequestingThreadHandle = RequestingThreadHandle;
+
+                    HighestPriority = RequestingThread.Priority;
+                }
+            }
+
+            ThreadEvent.Reset();
+            ThreadEvent.WaitOne();
+        }
+
+        public void GiveUpLock(int ThreadHandle)
+        {
+            lock (EnterWaitLock)
+            {
+                int CurrentThread = Memory.ReadInt32(MutexAddress) & ~MutexHasListenersMask;
+
+                if (CurrentThread == ThreadHandle)
+                {
+                    Unlock();
+                }
+            }
+        }
+
+        public void Unlock()
+        {
+            lock (EnterWaitLock)
+            {
+                if (CurrRequestingThreadHandle != 0)
+                {
+                    Memory.WriteInt32(MutexAddress, CurrRequestingThreadHandle);
+                }
+                else
+                {
+                    Memory.WriteInt32(MutexAddress, 0);
+                }
+
+                CurrRequestingThreadHandle = 0;
+
+                ThreadEvent.Set();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Objects/AccIManagerForApplication.cs b/Ryujinx/OsHle/Objects/AccIManagerForApplication.cs
new file mode 100644
index 0000000000..8e2a002ba0
--- /dev/null
+++ b/Ryujinx/OsHle/Objects/AccIManagerForApplication.cs
@@ -0,0 +1,17 @@
+namespace Ryujinx.OsHle.Objects
+{
+    class AccIManagerForApplication
+    {
+        public static long CheckAvailability(ServiceCtx Context)
+        {           
+            return 0;
+        }
+
+        public static long GetAccountId(ServiceCtx Context)
+        {
+            Context.ResponseData.Write(0xcafeL);
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Objects/AccIProfile.cs b/Ryujinx/OsHle/Objects/AccIProfile.cs
new file mode 100644
index 0000000000..2dbe189d71
--- /dev/null
+++ b/Ryujinx/OsHle/Objects/AccIProfile.cs
@@ -0,0 +1,18 @@
+namespace Ryujinx.OsHle.Objects
+{
+    class AccIProfile
+    {
+        public static long GetBase(ServiceCtx Context)
+        {
+            Context.ResponseData.Write(0L);
+            Context.ResponseData.Write(0L);
+            Context.ResponseData.Write(0L);
+            Context.ResponseData.Write(0L);
+            Context.ResponseData.Write(0L);
+            Context.ResponseData.Write(0L);
+            Context.ResponseData.Write(0L);
+            
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Objects/AmIApplicationFunctions.cs b/Ryujinx/OsHle/Objects/AmIApplicationFunctions.cs
new file mode 100644
index 0000000000..e174f94339
--- /dev/null
+++ b/Ryujinx/OsHle/Objects/AmIApplicationFunctions.cs
@@ -0,0 +1,56 @@
+using System.IO;
+
+using static Ryujinx.OsHle.Objects.ObjHelper;
+
+namespace Ryujinx.OsHle.Objects
+{
+    class AmIApplicationFunctions
+    {
+        private const uint LaunchParamsMagic = 0xc79497ca;
+
+        public static long PopLaunchParameter(ServiceCtx Context)
+        {
+            //Only the first 0x18 bytes of the Data seems to be actually used.
+            MakeObject(Context, new AmIStorage(MakeLaunchParams()));
+
+            return 0;
+        }
+
+        public static long EnsureSaveData(ServiceCtx Context)
+        {
+            long UIdLow  = Context.RequestData.ReadInt64();
+            long UIdHigh = Context.RequestData.ReadInt64();
+
+            Context.ResponseData.Write(0L);
+
+            return 0;
+        }
+
+        public static long GetDesiredLanguage(ServiceCtx Context)
+        {
+            //This is an enumerator where each number is a differnet language.
+            //0 is Japanese and 1 is English, need to figure out the other codes.
+            Context.ResponseData.Write(1L);
+
+            return 0;
+        }
+
+        private static byte[] MakeLaunchParams()
+        {
+            //Size needs to be at least 0x88 bytes otherwise application errors.
+            using (MemoryStream MS = new MemoryStream())
+            {
+                BinaryWriter Writer = new BinaryWriter(MS);
+
+                MS.SetLength(0x88);
+
+                Writer.Write(LaunchParamsMagic);
+                Writer.Write(1);  //IsAccountSelected? Only lower 8 bits actually used.
+                Writer.Write(1L); //User Id Low (note: User Id needs to be != 0)
+                Writer.Write(0L); //User Id High
+
+                return MS.ToArray();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Objects/AmIApplicationProxy.cs b/Ryujinx/OsHle/Objects/AmIApplicationProxy.cs
new file mode 100644
index 0000000000..2b0bc346d4
--- /dev/null
+++ b/Ryujinx/OsHle/Objects/AmIApplicationProxy.cs
@@ -0,0 +1,63 @@
+using static Ryujinx.OsHle.Objects.ObjHelper;
+
+namespace Ryujinx.OsHle.Objects
+{
+    class AmIApplicationProxy
+    {
+        public static long GetCommonStateGetter(ServiceCtx Context)
+        {
+            MakeObject(Context, new AmICommonStateGetter());
+
+            return 0;
+        }
+
+        public static long GetSelfController(ServiceCtx Context)
+        {
+            MakeObject(Context, new AmISelfController());
+
+            return 0;
+        }
+
+        public static long GetWindowController(ServiceCtx Context)
+        {
+            MakeObject(Context, new AmIWindowController());
+
+            return 0;
+        }
+
+        public static long GetAudioController(ServiceCtx Context)
+        {
+            MakeObject(Context, new AmIAudioController());
+
+            return 0;
+        }
+
+        public static long GetDisplayController(ServiceCtx Context)
+        {
+            MakeObject(Context, new AmIDisplayController());
+
+            return 0;
+        }
+
+        public static long GetLibraryAppletCreator(ServiceCtx Context)
+        {
+            MakeObject(Context, new AmILibraryAppletCreator());
+
+            return 0;
+        }
+
+        public static long GetApplicationFunctions(ServiceCtx Context)
+        {
+            MakeObject(Context, new AmIApplicationFunctions());
+
+            return 0;
+        }
+
+        public static long GetDebugFunctions(ServiceCtx Context)
+        {
+            MakeObject(Context, new AmIDebugFunctions());
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Objects/AmIAudioController.cs b/Ryujinx/OsHle/Objects/AmIAudioController.cs
new file mode 100644
index 0000000000..59d94bda49
--- /dev/null
+++ b/Ryujinx/OsHle/Objects/AmIAudioController.cs
@@ -0,0 +1,7 @@
+namespace Ryujinx.OsHle.Objects
+{
+    class AmIAudioController
+    {
+        
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Objects/AmICommonStateGetter.cs b/Ryujinx/OsHle/Objects/AmICommonStateGetter.cs
new file mode 100644
index 0000000000..32f065c81b
--- /dev/null
+++ b/Ryujinx/OsHle/Objects/AmICommonStateGetter.cs
@@ -0,0 +1,55 @@
+namespace Ryujinx.OsHle.Objects
+{
+    class AmICommonStateGetter
+    {
+        private enum FocusState
+        {
+            InFocus    = 1,
+            OutOfFocus = 2
+        }
+
+        private enum OperationMode
+        {
+            Handheld = 0,
+            Docked   = 1
+        }
+
+        public static long GetEventHandle(ServiceCtx Context)
+        {
+            Context.ResponseData.Write(0L);
+
+            return 0;
+        }
+
+        public static long ReceiveMessage(ServiceCtx Context)
+        {
+            //Program expects 0xF at 0x17ae70 on puyo sdk,
+            //otherwise runs on a infinite loop until it reads said value.
+            //What it means is still unknown.
+            Context.ResponseData.Write(0xfL);
+
+            return 0; //0x680;
+        }
+
+        public static long GetOperationMode(ServiceCtx Context)
+        {
+            Context.ResponseData.Write((byte)OperationMode.Handheld);
+
+            return 0;
+        }
+
+        public static long GetPerformanceMode(ServiceCtx Context)
+        {
+            Context.ResponseData.Write((byte)0);
+
+            return 0;
+        }
+
+        public static long GetCurrentFocusState(ServiceCtx Context)
+        {
+            Context.ResponseData.Write((byte)FocusState.InFocus);
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Objects/AmIDebugFunctions.cs b/Ryujinx/OsHle/Objects/AmIDebugFunctions.cs
new file mode 100644
index 0000000000..9794c43f40
--- /dev/null
+++ b/Ryujinx/OsHle/Objects/AmIDebugFunctions.cs
@@ -0,0 +1,7 @@
+namespace Ryujinx.OsHle.Objects
+{
+    class AmIDebugFunctions
+    {
+
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Objects/AmIDisplayController.cs b/Ryujinx/OsHle/Objects/AmIDisplayController.cs
new file mode 100644
index 0000000000..be36ca0ca1
--- /dev/null
+++ b/Ryujinx/OsHle/Objects/AmIDisplayController.cs
@@ -0,0 +1,7 @@
+namespace Ryujinx.OsHle.Objects
+{
+    class AmIDisplayController
+    {
+        
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Objects/AmILibraryAppletCreator.cs b/Ryujinx/OsHle/Objects/AmILibraryAppletCreator.cs
new file mode 100644
index 0000000000..585df9e99b
--- /dev/null
+++ b/Ryujinx/OsHle/Objects/AmILibraryAppletCreator.cs
@@ -0,0 +1,7 @@
+namespace Ryujinx.OsHle.Objects
+{
+    class AmILibraryAppletCreator
+    {
+        
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Objects/AmIParentalControlService.cs b/Ryujinx/OsHle/Objects/AmIParentalControlService.cs
new file mode 100644
index 0000000000..12c3c2a96d
--- /dev/null
+++ b/Ryujinx/OsHle/Objects/AmIParentalControlService.cs
@@ -0,0 +1,7 @@
+namespace Ryujinx.OsHle.Objects
+{
+    class AmIParentalControlService
+    {
+
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Objects/AmISelfController.cs b/Ryujinx/OsHle/Objects/AmISelfController.cs
new file mode 100644
index 0000000000..33cde09588
--- /dev/null
+++ b/Ryujinx/OsHle/Objects/AmISelfController.cs
@@ -0,0 +1,28 @@
+namespace Ryujinx.OsHle.Objects
+{
+    class AmISelfController
+    {
+        public static long SetOperationModeChangedNotification(ServiceCtx Context)
+        {
+            bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
+
+            return 0;
+        }
+
+        public static long SetPerformanceModeChangedNotification(ServiceCtx Context)
+        {
+            bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
+
+            return 0;
+        }
+
+        public static long SetFocusHandlingMode(ServiceCtx Context)
+        {
+            bool Flag1 = Context.RequestData.ReadByte() != 0 ? true : false;
+            bool Flag2 = Context.RequestData.ReadByte() != 0 ? true : false;
+            bool Flag3 = Context.RequestData.ReadByte() != 0 ? true : false;
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Objects/AmIStorage.cs b/Ryujinx/OsHle/Objects/AmIStorage.cs
new file mode 100644
index 0000000000..7e608ac8cf
--- /dev/null
+++ b/Ryujinx/OsHle/Objects/AmIStorage.cs
@@ -0,0 +1,23 @@
+using static Ryujinx.OsHle.Objects.ObjHelper;
+
+namespace Ryujinx.OsHle.Objects
+{
+    class AmIStorage
+    {
+        public byte[] Data { get; private set; }
+
+        public AmIStorage(byte[] Data)
+        {
+            this.Data = Data;
+        }
+
+        public static long Open(ServiceCtx Context)
+        {
+            AmIStorage Storage = Context.GetObject<AmIStorage>();
+
+            MakeObject(Context, new AmIStorageAccessor(Storage));
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Objects/AmIStorageAccessor.cs b/Ryujinx/OsHle/Objects/AmIStorageAccessor.cs
new file mode 100644
index 0000000000..62007ef817
--- /dev/null
+++ b/Ryujinx/OsHle/Objects/AmIStorageAccessor.cs
@@ -0,0 +1,56 @@
+using ChocolArm64.Memory;
+using System;
+
+namespace Ryujinx.OsHle.Objects
+{
+    class AmIStorageAccessor
+    {
+        public AmIStorage Storage { get; private set; }
+
+        public AmIStorageAccessor(AmIStorage Storage)
+        {
+            this.Storage = Storage;
+        }
+
+        public static long GetSize(ServiceCtx Context)
+        {
+            AmIStorageAccessor Accessor = Context.GetObject<AmIStorageAccessor>();
+
+            Context.ResponseData.Write((long)Accessor.Storage.Data.Length);
+
+            return 0;
+        }
+
+        public static long Read(ServiceCtx Context)
+        {
+            AmIStorageAccessor Accessor = Context.GetObject<AmIStorageAccessor>();
+
+            AmIStorage Storage = Accessor.Storage;
+
+            long ReadPosition = Context.RequestData.ReadInt64();
+
+            if (Context.Request.RecvListBuff.Count > 0)
+            {
+                long  Position = Context.Request.RecvListBuff[0].Position;
+                short Size     = Context.Request.RecvListBuff[0].Size;
+
+                byte[] Data;
+
+                if (Storage.Data.Length > Size)
+                {
+                    Data = new byte[Size];
+                    
+                    Buffer.BlockCopy(Storage.Data, 0, Data, 0, Size);
+                }
+                else
+                {
+                    Data = Storage.Data;
+                }
+
+                AMemoryHelper.WriteBytes(Context.Memory, Position, Data);
+            }
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Objects/AmIWindowController.cs b/Ryujinx/OsHle/Objects/AmIWindowController.cs
new file mode 100644
index 0000000000..ea967ae84a
--- /dev/null
+++ b/Ryujinx/OsHle/Objects/AmIWindowController.cs
@@ -0,0 +1,17 @@
+namespace Ryujinx.OsHle.Objects
+{
+    class AmIWindowController
+    {
+        public static long GetAppletResourceUserId(ServiceCtx Context)
+        {
+            Context.ResponseData.Write(0L);
+
+            return 0;
+        }
+
+        public static long AcquireForegroundRights(ServiceCtx Context)
+        {
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Objects/ApmISession.cs b/Ryujinx/OsHle/Objects/ApmISession.cs
new file mode 100644
index 0000000000..2b2c035158
--- /dev/null
+++ b/Ryujinx/OsHle/Objects/ApmISession.cs
@@ -0,0 +1,13 @@
+namespace Ryujinx.OsHle.Objects
+{
+    class ApmISession
+    {
+        public static long SetPerformanceConfiguration(ServiceCtx Context)
+        {
+            int PerfMode   = Context.RequestData.ReadInt32();
+            int PerfConfig = Context.RequestData.ReadInt32();
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Objects/AudIAudioRenderer.cs b/Ryujinx/OsHle/Objects/AudIAudioRenderer.cs
new file mode 100644
index 0000000000..b451d07ba7
--- /dev/null
+++ b/Ryujinx/OsHle/Objects/AudIAudioRenderer.cs
@@ -0,0 +1,36 @@
+namespace Ryujinx.OsHle.Objects
+{
+    class AudIAudioRenderer
+    {
+        public static long RequestUpdateAudioRenderer(ServiceCtx Context)
+        {
+            long Position = Context.Request.ReceiveBuff[0].Position;
+
+            //0x40 bytes header
+            Context.Memory.WriteInt32(Position + 0x4,  0xb0); //Behavior Out State Size? (note: this is the last section)
+            Context.Memory.WriteInt32(Position + 0x8,  0x18e0); //Memory Pool Out State Size?
+            Context.Memory.WriteInt32(Position + 0xc,  0x600); //Voice Out State Size?
+            Context.Memory.WriteInt32(Position + 0x14, 0xe0); //Effect Out State Size?
+            Context.Memory.WriteInt32(Position + 0x1c, 0x20); //Sink Out State Size?
+            Context.Memory.WriteInt32(Position + 0x20, 0x10); //Performance Out State Size?
+            Context.Memory.WriteInt32(Position + 0x3c, 0x20e0); //Total Size (including 0x40 bytes header)
+
+            for (int Offset = 0x40; Offset < 0x40 + 0x18e0; Offset += 0x10)
+            {
+                Context.Memory.WriteInt32(Position + Offset, 5);
+            }
+            
+            return 0;
+        }
+
+        public static long StartAudioRenderer(ServiceCtx Context)
+        {
+            return 0;
+        }
+
+        public static long QuerySystemEvent(ServiceCtx Context)
+        {
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Objects/FriendIFriendService.cs b/Ryujinx/OsHle/Objects/FriendIFriendService.cs
new file mode 100644
index 0000000000..9a39380a5a
--- /dev/null
+++ b/Ryujinx/OsHle/Objects/FriendIFriendService.cs
@@ -0,0 +1,7 @@
+namespace Ryujinx.OsHle.Objects
+{
+    class FriendIFriendService
+    {
+        
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Objects/FspSrvIFile.cs b/Ryujinx/OsHle/Objects/FspSrvIFile.cs
new file mode 100644
index 0000000000..4b9f8c37b2
--- /dev/null
+++ b/Ryujinx/OsHle/Objects/FspSrvIFile.cs
@@ -0,0 +1,72 @@
+using ChocolArm64.Memory;
+using System;
+
+using System.IO;
+
+namespace Ryujinx.OsHle.Objects
+{
+    class FspSrvIFile : IDisposable
+    {
+        public Stream BaseStream { get; private set; }
+
+        public FspSrvIFile(Stream BaseStream)
+        {
+            this.BaseStream = BaseStream;
+        }
+
+        public static long Read(ServiceCtx Context)
+        {
+            FspSrvIFile File = Context.GetObject<FspSrvIFile>();
+
+            long Position = Context.Request.ReceiveBuff[0].Position;
+
+            long Zero   = Context.RequestData.ReadInt64();
+            long Offset = Context.RequestData.ReadInt64();
+            long Size   = Context.RequestData.ReadInt64();
+
+            byte[] Data = new byte[Size];
+
+            int ReadSize = File.BaseStream.Read(Data, 0, (int)Size);
+
+            AMemoryHelper.WriteBytes(Context.Memory, Position, Data);
+
+            //TODO: Use ReadSize, we need to return the size that was REALLY read from the file.
+            //This is a workaround because we are doing something wrong and the game expects to read
+            //data from a file that doesn't yet exists -- and breaks if it can't read anything.
+            Context.ResponseData.Write((long)Size);
+
+            return 0;
+        }
+
+        public static long Write(ServiceCtx Context)
+        {
+            FspSrvIFile File = Context.GetObject<FspSrvIFile>();
+
+            long Position = Context.Request.SendBuff[0].Position;
+
+            long Zero   = Context.RequestData.ReadInt64();
+            long Offset = Context.RequestData.ReadInt64();
+            long Size   = Context.RequestData.ReadInt64();
+
+            byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, Position, (int)Size);
+
+            File.BaseStream.Seek(Offset, SeekOrigin.Begin);
+            File.BaseStream.Write(Data, 0, (int)Size);
+
+            return 0;
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+        }
+
+        protected virtual void Dispose(bool disposing)
+        {
+            if (disposing && BaseStream != null)
+            {
+                BaseStream.Dispose();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Objects/FspSrvIFileSystem.cs b/Ryujinx/OsHle/Objects/FspSrvIFileSystem.cs
new file mode 100644
index 0000000000..fed1c55acc
--- /dev/null
+++ b/Ryujinx/OsHle/Objects/FspSrvIFileSystem.cs
@@ -0,0 +1,70 @@
+using ChocolArm64.Memory;
+using System.IO;
+
+using static Ryujinx.OsHle.Objects.ObjHelper;
+
+namespace Ryujinx.OsHle.Objects
+{
+    class FspSrvIFileSystem
+    {
+        public string FilePath { get; private set; }
+
+        public FspSrvIFileSystem(string Path)
+        {
+            this.FilePath = Path;
+        }
+
+        public static long GetEntryType(ServiceCtx Context)
+        {
+            FspSrvIFileSystem FileSystem = Context.GetObject<FspSrvIFileSystem>();
+
+            long Position = Context.Request.PtrBuff[0].Position;
+
+            string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position);
+
+            string FileName = Context.Ns.VFs.GetFullPath(FileSystem.FilePath, Name);
+
+            if (FileName == null)
+            {
+                //TODO: Correct error code.
+                return -1;
+            }
+
+            bool IsFile = File.Exists(FileName);
+
+            Context.ResponseData.Write(IsFile ? 1 : 0);
+
+            return 0;
+        }
+
+        public static long OpenFile(ServiceCtx Context)
+        {
+            FspSrvIFileSystem FileSystem = Context.GetObject<FspSrvIFileSystem>();
+
+            long Position = Context.Request.PtrBuff[0].Position;
+
+            int FilterFlags = Context.RequestData.ReadInt32();
+
+            string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position);
+
+            string FileName = Context.Ns.VFs.GetFullPath(FileSystem.FilePath, Name);
+
+            if (FileName == null)
+            {
+                //TODO: Correct error code.
+                return -1;
+            }
+
+            FileStream Stream = new FileStream(FileName, FileMode.OpenOrCreate);
+
+            MakeObject(Context, new FspSrvIFile(Stream));
+
+            return 0;
+        }
+
+        public static long Commit(ServiceCtx Context)
+        {
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Objects/FspSrvIStorage.cs b/Ryujinx/OsHle/Objects/FspSrvIStorage.cs
new file mode 100644
index 0000000000..76ad8917ae
--- /dev/null
+++ b/Ryujinx/OsHle/Objects/FspSrvIStorage.cs
@@ -0,0 +1,44 @@
+using ChocolArm64.Memory;
+using Ryujinx.OsHle.Ipc;
+using System.IO;
+
+namespace Ryujinx.OsHle.Objects
+{
+    class FspSrvIStorage
+    {
+        public Stream BaseStream { get; private set; }
+
+        public FspSrvIStorage(Stream BaseStream)
+        {
+            this.BaseStream = BaseStream;
+        }
+
+        public static long Read(ServiceCtx Context)
+        {
+            FspSrvIStorage Storage = Context.GetObject<FspSrvIStorage>();
+
+            long Offset = Context.RequestData.ReadInt64();
+            long Size   = Context.RequestData.ReadInt64();
+
+            if (Context.Request.ReceiveBuff.Count > 0)
+            {
+                IpcBuffDesc BuffDesc = Context.Request.ReceiveBuff[0];
+
+                //Use smaller length to avoid overflows.
+                if (Size > BuffDesc.Size)
+                {
+                    Size = BuffDesc.Size;
+                }
+
+                byte[] Data = new byte[Size];
+
+                Storage.BaseStream.Seek(Offset, SeekOrigin.Begin);
+                Storage.BaseStream.Read(Data, 0, Data.Length);
+
+                AMemoryHelper.WriteBytes(Context.Memory, BuffDesc.Position, Data);
+            }
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Objects/HidIAppletResource.cs b/Ryujinx/OsHle/Objects/HidIAppletResource.cs
new file mode 100644
index 0000000000..73b948df7a
--- /dev/null
+++ b/Ryujinx/OsHle/Objects/HidIAppletResource.cs
@@ -0,0 +1,22 @@
+using Ryujinx.OsHle.Handles;
+using Ryujinx.OsHle.Ipc;
+
+namespace Ryujinx.OsHle.Objects
+{
+    class HidIAppletResource
+    {
+        public HSharedMem Handle;
+
+        public HidIAppletResource(HSharedMem Handle)
+        {
+            this.Handle = Handle;
+        }
+
+        public static long GetSharedMemoryHandle(ServiceCtx Context)
+        {
+            Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Context.Ns.Os.HidHandle);
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Objects/ObjHelper.cs b/Ryujinx/OsHle/Objects/ObjHelper.cs
new file mode 100644
index 0000000000..e37f5320ea
--- /dev/null
+++ b/Ryujinx/OsHle/Objects/ObjHelper.cs
@@ -0,0 +1,24 @@
+using Ryujinx.OsHle.Handles;
+using Ryujinx.OsHle.Ipc;
+
+namespace Ryujinx.OsHle.Objects
+{
+    static class ObjHelper
+    {
+        public static void MakeObject(ServiceCtx Context, object Obj)
+        {
+            if (Context.Session is HDomain Dom)
+            {
+                Context.Response.ResponseObjIds.Add(Dom.GenertateObjectId(Obj));
+            }
+            else
+            {
+                HSessionObj HndData = new HSessionObj(Context.Session, Obj);
+
+                int VHandle = Context.Ns.Os.Handles.GenerateId(HndData);
+
+                Context.Response.HandleDesc = IpcHandleDesc.MakeMove(VHandle);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Objects/Parcel.cs b/Ryujinx/OsHle/Objects/Parcel.cs
new file mode 100644
index 0000000000..0d322bab8c
--- /dev/null
+++ b/Ryujinx/OsHle/Objects/Parcel.cs
@@ -0,0 +1,58 @@
+using System;
+using System.IO;
+
+namespace Ryujinx.OsHle.Objects.Android
+{
+    static class Parcel
+    {
+        public static byte[] GetParcelData(byte[] Parcel)
+        {
+            if (Parcel == null)
+            {
+                throw new ArgumentNullException(nameof(Parcel));
+            }
+
+            using (MemoryStream MS = new MemoryStream(Parcel))
+            {
+                BinaryReader Reader = new BinaryReader(MS);
+
+                int DataSize   = Reader.ReadInt32();
+                int DataOffset = Reader.ReadInt32();
+                int ObjsSize   = Reader.ReadInt32();
+                int ObjsOffset = Reader.ReadInt32();
+
+                MS.Seek(DataOffset - 0x10, SeekOrigin.Current);
+
+                return Reader.ReadBytes(DataSize);
+            }
+        }
+
+        public static byte[] MakeParcel(byte[] Data, byte[] Objs)
+        {
+            if (Data == null)
+            {
+                throw new ArgumentNullException(nameof(Data));
+            }
+
+            if (Objs == null)
+            {
+                throw new ArgumentNullException(nameof(Objs));
+            }
+
+            using (MemoryStream MS = new MemoryStream())
+            {
+                BinaryWriter Writer = new BinaryWriter(MS);
+
+                Writer.Write(Data.Length);
+                Writer.Write(0x10);
+                Writer.Write(Objs.Length);
+                Writer.Write(Data.Length + 0x10);
+
+                Writer.Write(Data);
+                Writer.Write(Objs);
+
+                return MS.ToArray();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Objects/TimeISteadyClock.cs b/Ryujinx/OsHle/Objects/TimeISteadyClock.cs
new file mode 100644
index 0000000000..ead8c41a18
--- /dev/null
+++ b/Ryujinx/OsHle/Objects/TimeISteadyClock.cs
@@ -0,0 +1,7 @@
+namespace Ryujinx.OsHle.Objects
+{
+    class TimeISteadyClock
+    {
+
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Objects/TimeISystemClock.cs b/Ryujinx/OsHle/Objects/TimeISystemClock.cs
new file mode 100644
index 0000000000..d9a3a073ec
--- /dev/null
+++ b/Ryujinx/OsHle/Objects/TimeISystemClock.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace Ryujinx.OsHle.Objects
+{
+    class TimeISystemClock
+    {
+        private static DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+
+        public static long GetCurrentTime(ServiceCtx Context)
+        {
+            Context.ResponseData.Write((long)(DateTime.Now - Epoch).TotalSeconds);
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Objects/TimeITimeZoneService.cs b/Ryujinx/OsHle/Objects/TimeITimeZoneService.cs
new file mode 100644
index 0000000000..af5490a613
--- /dev/null
+++ b/Ryujinx/OsHle/Objects/TimeITimeZoneService.cs
@@ -0,0 +1,7 @@
+namespace Ryujinx.OsHle.Objects
+{
+    class TimeITimeZoneService
+    {
+
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Objects/ViIApplicationDisplayService.cs b/Ryujinx/OsHle/Objects/ViIApplicationDisplayService.cs
new file mode 100644
index 0000000000..81616ae115
--- /dev/null
+++ b/Ryujinx/OsHle/Objects/ViIApplicationDisplayService.cs
@@ -0,0 +1,148 @@
+using ChocolArm64.Memory;
+using Ryujinx.OsHle.Handles;
+using Ryujinx.OsHle.Ipc;
+using System.IO;
+
+using static Ryujinx.OsHle.Objects.Android.Parcel;
+using static Ryujinx.OsHle.Objects.ObjHelper;
+
+namespace Ryujinx.OsHle.Objects
+{
+    class ViIApplicationDisplayService
+    {
+        public static long GetRelayService(ServiceCtx Context)
+        {
+            MakeObject(Context, new ViIHOSBinderDriver());
+
+            return 0;
+        }
+
+        public static long GetSystemDisplayService(ServiceCtx Context)
+        {
+            MakeObject(Context, new ViISystemDisplayService());
+
+            return 0;
+        }
+
+        public static long GetManagerDisplayService(ServiceCtx Context)
+        {
+            MakeObject(Context, new ViIManagerDisplayService());
+
+            return 0;
+        }
+
+        public static long OpenDisplay(ServiceCtx Context)
+        {
+            string Name = GetDisplayName(Context);
+
+            long DisplayId = Context.Ns.Os.Displays.GenerateId(new Display(Name));
+
+            Context.ResponseData.Write(DisplayId);
+
+            return 0;
+        }
+
+        public static long OpenLayer(ServiceCtx Context)
+        {
+            long LayerId = Context.RequestData.ReadInt64();
+            long UserId  = Context.RequestData.ReadInt64();
+
+            long ParcelPtr = Context.Request.ReceiveBuff[0].Position;
+
+            byte[] Parcel = MakeIGraphicsBufferProducer(ParcelPtr);
+
+            AMemoryHelper.WriteBytes(Context.Memory, ParcelPtr, Parcel);
+
+            Context.ResponseData.Write((long)Parcel.Length);
+
+            return 0;
+        }
+
+        public static long CreateStrayLayer(ServiceCtx Context)
+        {
+            long LayerFlags = Context.RequestData.ReadInt64();
+            long DisplayId  = Context.RequestData.ReadInt64();
+
+            long ParcelPtr = Context.Request.ReceiveBuff[0].Position;
+
+            Display Disp = Context.Ns.Os.Displays.GetData<Display>((int)DisplayId);
+
+            byte[] Parcel = MakeIGraphicsBufferProducer(ParcelPtr);
+
+            AMemoryHelper.WriteBytes(Context.Memory, ParcelPtr, Parcel);
+
+            Context.ResponseData.Write(0L);
+            Context.ResponseData.Write((long)Parcel.Length);
+
+            return 0;
+        }
+
+        public static long SetLayerScalingMode(ServiceCtx Context)
+        {
+            int  ScalingMode = Context.RequestData.ReadInt32();
+            long Unknown     = Context.RequestData.ReadInt64();
+
+            return 0;
+        }
+
+        public static long GetDisplayVSyncEvent(ServiceCtx Context)
+        {
+            string Name = GetDisplayName(Context);
+
+            int Handle = Context.Ns.Os.Handles.GenerateId(new HEvent());
+
+            Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
+
+            return 0;
+        }
+
+        private static byte[] MakeIGraphicsBufferProducer(long BasePtr)
+        {
+            long Id        = 0x20;
+            long CookiePtr = 0L;
+
+            using (MemoryStream MS = new MemoryStream())
+            {
+                BinaryWriter Writer = new BinaryWriter(MS);
+
+                //flat_binder_object (size is 0x28)
+                Writer.Write(2); //Type (BINDER_TYPE_WEAK_BINDER)
+                Writer.Write(0); //Flags
+                Writer.Write((int)(Id >> 0));
+                Writer.Write((int)(Id >> 32));
+                Writer.Write((int)(CookiePtr >> 0));
+                Writer.Write((int)(CookiePtr >> 32));
+                Writer.Write((byte)'d');
+                Writer.Write((byte)'i');
+                Writer.Write((byte)'s');
+                Writer.Write((byte)'p');
+                Writer.Write((byte)'d');
+                Writer.Write((byte)'r');
+                Writer.Write((byte)'v');
+                Writer.Write((byte)'\0');
+                Writer.Write(0L); //Pad
+
+                return MakeParcel(MS.ToArray(), new byte[] { 0, 0, 0, 0 });
+            }
+        }
+
+        private static string GetDisplayName(ServiceCtx Context)
+        {
+            string Name = string.Empty;
+
+            for (int Index = 0; Index < 8 &&
+                Context.RequestData.BaseStream.Position <
+                Context.RequestData.BaseStream.Length; Index++)
+            {
+                byte Chr = Context.RequestData.ReadByte();
+
+                if (Chr >= 0x20 && Chr < 0x7f)
+                {
+                    Name += (char)Chr;
+                }
+            }
+
+            return Name;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Objects/ViIHOSBinderDriver.cs b/Ryujinx/OsHle/Objects/ViIHOSBinderDriver.cs
new file mode 100644
index 0000000000..72a472ae51
--- /dev/null
+++ b/Ryujinx/OsHle/Objects/ViIHOSBinderDriver.cs
@@ -0,0 +1,211 @@
+using ChocolArm64.Memory;
+using Ryujinx.OsHle.Handles;
+using Ryujinx.OsHle.Ipc;
+using Ryujinx.OsHle.Utilities;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+using static Ryujinx.OsHle.Objects.Android.Parcel;
+
+namespace Ryujinx.OsHle.Objects
+{
+    class ViIHOSBinderDriver
+    {
+        private delegate long ServiceProcessRequest(ServiceCtx Context, byte[] ParcelData);
+
+        private static Dictionary<(string, int), ServiceProcessRequest> InterfaceMthd =
+                   new Dictionary<(string, int), ServiceProcessRequest>()
+        {
+            { ("android.gui.IGraphicBufferProducer", 0x1), GraphicBufferProducerRequestBuffer },
+            { ("android.gui.IGraphicBufferProducer", 0x3), GraphicBufferProducerDequeueBuffer },
+            { ("android.gui.IGraphicBufferProducer", 0x7), GraphicBufferProducerQueueBuffer   },
+            //{ ("android.gui.IGraphicBufferProducer", 0x8), GraphicBufferProducerCancelBuffer  },
+            { ("android.gui.IGraphicBufferProducer", 0x9), GraphicBufferProducerQuery         },
+            { ("android.gui.IGraphicBufferProducer", 0xa), GraphicBufferProducerConnect       },
+            { ("android.gui.IGraphicBufferProducer", 0xe), GraphicBufferPreallocateBuffer     },
+        };
+
+        private class BufferObj
+        {
+
+        }
+
+        public IdPoolWithObj BufferSlots { get; private set; }
+
+        public byte[] Gbfr;
+
+        public ViIHOSBinderDriver()
+        {
+            BufferSlots = new IdPoolWithObj();
+        }
+
+        public static long TransactParcel(ServiceCtx Context)
+        {
+            int Id   = Context.RequestData.ReadInt32();
+            int Code = Context.RequestData.ReadInt32();
+
+            long DataPos  = Context.Request.SendBuff[0].Position;
+            long DataSize = Context.Request.SendBuff[0].Size;
+
+            byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, DataPos, (int)DataSize);
+
+            Data = GetParcelData(Data);
+
+            using (MemoryStream MS = new MemoryStream(Data))
+            {
+                BinaryReader Reader = new BinaryReader(MS);
+
+                MS.Seek(4, SeekOrigin.Current);
+
+                int StrSize = Reader.ReadInt32();
+
+                string InterfaceName = Encoding.Unicode.GetString(Data, 8, StrSize * 2);
+
+                if (InterfaceMthd.TryGetValue((InterfaceName, Code), out ServiceProcessRequest ProcReq))
+                {
+                    return ProcReq(Context, Data);
+                }
+                else
+                {
+                    throw new NotImplementedException($"{InterfaceName} {Code}");
+                }
+            }
+        }
+
+        private static long GraphicBufferProducerRequestBuffer(ServiceCtx Context, byte[] ParcelData)
+        {
+            ViIHOSBinderDriver BinderDriver = Context.GetObject<ViIHOSBinderDriver>();
+
+            int GbfrSize = BinderDriver.Gbfr?.Length ?? 0;
+
+            byte[] Data = new byte[GbfrSize + 4];
+
+            if (BinderDriver.Gbfr != null)
+            {
+                Buffer.BlockCopy(BinderDriver.Gbfr, 0, Data, 0, GbfrSize);
+            }
+
+            return MakeReplyParcel(Context, Data);
+        }
+
+        private static long GraphicBufferProducerDequeueBuffer(ServiceCtx Context, byte[] ParcelData)
+        {
+            ViIHOSBinderDriver BinderDriver = Context.GetObject<ViIHOSBinderDriver>();
+
+            //Note: It seems that the maximum number of slots is 64, because if we return
+            //a Slot number > 63, it seems to cause a buffer overrun and it reads garbage.
+            //Note 2: The size of each object associated with the slot is 0x30.
+            int Slot = BinderDriver.BufferSlots.GenerateId(new BufferObj());
+
+            return MakeReplyParcel(Context, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+        }
+
+        private static long GraphicBufferProducerQueueBuffer(ServiceCtx Context, byte[] ParcelData)
+        {
+            return MakeReplyParcel(Context, 1280, 720, 0, 0, 0);
+        }
+
+        private static long GraphicBufferProducerCancelBuffer(ServiceCtx Context, byte[] ParcelData)
+        {
+            ViIHOSBinderDriver BinderDriver = Context.GetObject<ViIHOSBinderDriver>();
+
+            using (MemoryStream MS = new MemoryStream(ParcelData))
+            {
+                BinaryReader Reader = new BinaryReader(MS);
+
+                MS.Seek(0x50, SeekOrigin.Begin);
+
+                int Slot = Reader.ReadInt32();
+
+                BinderDriver.BufferSlots.Delete(Slot);
+
+                return MakeReplyParcel(Context, 0);
+            }
+        }
+
+        private static long GraphicBufferProducerQuery(ServiceCtx Context, byte[] ParcelData)
+        {
+            return MakeReplyParcel(Context, 0, 0);
+        }
+
+        private static long GraphicBufferProducerConnect(ServiceCtx Context, byte[] ParcelData)
+        {
+            return MakeReplyParcel(Context, 1280, 720, 0, 0, 0);
+        }
+
+        private static long GraphicBufferPreallocateBuffer(ServiceCtx Context, byte[] ParcelData)
+        {
+            ViIHOSBinderDriver BinderDriver = Context.GetObject<ViIHOSBinderDriver>();
+
+            int GbfrSize = ParcelData.Length - 0x54;
+
+            BinderDriver.Gbfr = new byte[GbfrSize];
+
+            Buffer.BlockCopy(ParcelData, 0x54, BinderDriver.Gbfr, 0, GbfrSize);
+
+            using (MemoryStream MS = new MemoryStream(ParcelData))
+            {
+                BinaryReader Reader = new BinaryReader(MS);
+
+                MS.Seek(0xd4, SeekOrigin.Begin);
+
+                int Handle = Reader.ReadInt32();
+
+                HNvMap NvMap = Context.Ns.Os.Handles.GetData<HNvMap>(Handle);
+
+                Context.Ns.Gpu.Renderer.FrameBufferPtr =
+                    Context.Memory.Manager.GetPhys(NvMap.Address, AMemoryPerm.Read);
+            }
+
+            return MakeReplyParcel(Context, 0);
+        }
+
+        private static long MakeReplyParcel(ServiceCtx Context, params int[] Ints)
+        {
+            using (MemoryStream MS = new MemoryStream())
+            {
+                BinaryWriter Writer = new BinaryWriter(MS);
+
+                foreach (int Int in Ints)
+                {
+                    Writer.Write(Int);
+                }
+
+                return MakeReplyParcel(Context, MS.ToArray());
+            }
+        }
+
+        private static long MakeReplyParcel(ServiceCtx Context, byte[] Data)
+        {
+            long ReplyPos  = Context.Request.ReceiveBuff[0].Position;
+            long ReplySize = Context.Request.ReceiveBuff[0].Position;
+
+            byte[] Reply = MakeParcel(Data, new byte[0]);
+
+            AMemoryHelper.WriteBytes(Context.Memory, ReplyPos, Reply);
+
+            return 0;
+        }
+
+        public static long AdjustRefcount(ServiceCtx Context)
+        {
+            int Id     = Context.RequestData.ReadInt32();
+            int AddVal = Context.RequestData.ReadInt32();
+            int Type   = Context.RequestData.ReadInt32();
+
+            return 0;
+        }
+
+        public static long GetNativeHandle(ServiceCtx Context)
+        {
+            int  Id  = Context.RequestData.ReadInt32();
+            uint Unk = Context.RequestData.ReadUInt32();
+
+            Context.Response.HandleDesc = IpcHandleDesc.MakeMove(0xbadcafe);
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Objects/ViIManagerDisplayService.cs b/Ryujinx/OsHle/Objects/ViIManagerDisplayService.cs
new file mode 100644
index 0000000000..0fdca3bac2
--- /dev/null
+++ b/Ryujinx/OsHle/Objects/ViIManagerDisplayService.cs
@@ -0,0 +1,17 @@
+namespace Ryujinx.OsHle.Objects
+{
+    class ViIManagerDisplayService
+    {
+        public static long CreateManagedLayer(ServiceCtx Context)
+        {
+            Context.ResponseData.Write(0L); //LayerId
+
+            return 0;
+        }
+
+        public static long AddToLayerStack(ServiceCtx Context)
+        {
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Objects/ViISystemDisplayService.cs b/Ryujinx/OsHle/Objects/ViISystemDisplayService.cs
new file mode 100644
index 0000000000..8d3a51f4eb
--- /dev/null
+++ b/Ryujinx/OsHle/Objects/ViISystemDisplayService.cs
@@ -0,0 +1,10 @@
+namespace Ryujinx.OsHle.Objects
+{
+    class ViISystemDisplayService
+    {
+        public static long SetLayerZ(ServiceCtx Context)
+        {
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Process.cs b/Ryujinx/OsHle/Process.cs
new file mode 100644
index 0000000000..1cfae92935
--- /dev/null
+++ b/Ryujinx/OsHle/Process.cs
@@ -0,0 +1,179 @@
+using ChocolArm64;
+using ChocolArm64.Memory;
+using Ryujinx.Loaders;
+using Ryujinx.Loaders.Executables;
+using Ryujinx.OsHle.Handles;
+using Ryujinx.OsHle.Svc;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+
+namespace Ryujinx.OsHle
+{
+    class Process
+    {
+        private const int  MaxStackSize  = 8 * 1024 * 1024;
+
+        private const int  TlsSize       = 0x200;
+        private const int  TotalTlsSlots = 32;
+        private const int  TlsTotalSize  = TotalTlsSlots * TlsSize;
+        private const long TlsPageAddr   = (AMemoryMgr.AddrSize - TlsTotalSize) & ~AMemoryMgr.PageMask;
+
+        private Switch Ns;
+
+        public int ProcessId { get; private set; }
+
+        public AMemory Memory { get; private set; }
+
+        private SvcHandler SvcHandler;
+
+        private AThread MainThread;
+
+        private ConcurrentDictionary<int, AThread> TlsSlots;
+
+        private List<Executable> Executables;
+
+        private long ImageBase;
+
+        public Process(Switch Ns, AMemoryAlloc Allocator, int ProcessId)
+        {
+            this.Ns        = Ns;
+            this.ProcessId = ProcessId;
+
+            Memory      = new AMemory(Ns.Ram, Allocator);
+            SvcHandler  = new SvcHandler(Ns, Memory);
+            TlsSlots    = new ConcurrentDictionary<int, AThread>();
+            Executables = new List<Executable>();
+
+            ImageBase = 0x8000000;
+
+            Memory.Manager.MapPhys(
+                TlsPageAddr,
+                TlsTotalSize,
+                (int)MemoryType.ThreadLocal,
+                AMemoryPerm.RW);
+        }
+
+        public void LoadProgram(IElf Program)
+        {
+            Executable Executable = new Executable(Program, Memory, ImageBase);
+
+            Executables.Add(Executable);
+
+            ImageBase = AMemoryHelper.PageRoundUp(Executable.ImageEnd);
+        }
+
+        public void SetEmptyArgs()
+        {
+            ImageBase += AMemoryMgr.PageSize;
+        }
+
+        public void InitializeHeap()
+        {
+            Memory.Manager.SetHeapAddr((ImageBase + 0x3fffffff) & ~0x3fffffff);
+        }
+
+        public bool Run()
+        {
+            if (Executables.Count == 0)
+            {
+                return false;
+            }
+
+            long StackBot = TlsPageAddr - MaxStackSize;
+
+            Memory.Manager.MapPhys(StackBot, MaxStackSize, (int)MemoryType.Normal, AMemoryPerm.RW);
+
+            int Handle = MakeThread(Executables[0].ImageBase, TlsPageAddr, 0, 48, 0);
+
+            if (Handle == -1)
+            {
+                return false;
+            }
+
+            MainThread = Ns.Os.Handles.GetData<HThread>(Handle).Thread;
+
+            MainThread.Execute();
+
+            return true;
+        }
+
+        public void StopAllThreads()
+        {
+            if (MainThread != null)
+            {
+                while (MainThread.IsAlive)
+                {
+                    MainThread.StopExecution();
+                }
+            }
+
+            foreach (AThread Thread in TlsSlots.Values)
+            {
+                while (Thread.IsAlive)
+                {
+                    Thread.StopExecution();
+                }
+            }
+        }
+
+        public int MakeThread(
+            long EntryPoint,
+            long StackTop,
+            long ArgsPtr,
+            int  Priority,
+            int  ProcessorId)
+        {
+            AThread Thread = new AThread(Memory, EntryPoint, Priority);
+
+            int TlsSlot = GetFreeTlsSlot(Thread);
+
+            int Handle = Ns.Os.Handles.GenerateId(new HThread(Thread));
+
+            if (TlsSlot == -1 || Handle  == -1)
+            {
+                return -1;
+            }
+
+            Thread.Registers.SvcCall  += SvcHandler.SvcCall;
+            Thread.Registers.ProcessId = ProcessId;
+            Thread.Registers.ThreadId  = Ns.Os.IdGen.GenerateId();
+            Thread.Registers.TlsAddr   = TlsPageAddr + TlsSlot * TlsSize;
+            Thread.Registers.X0        = (ulong)ArgsPtr;
+            Thread.Registers.X1        = (ulong)Handle;
+            Thread.Registers.X31       = (ulong)StackTop;
+
+            Thread.WorkFinished += ThreadFinished;
+
+            return Handle;
+        }
+
+        private int GetFreeTlsSlot(AThread Thread)
+        {
+            for (int Index = 1; Index < TotalTlsSlots; Index++)
+            {
+                if (TlsSlots.TryAdd(Index, Thread))
+                {
+                    return Index;
+                }
+            }
+
+            return -1;
+        }
+
+        private void ThreadFinished(object sender, EventArgs e)
+        {
+            if (sender is AThread Thread)
+            {
+                TlsSlots.TryRemove(GetTlsSlot(Thread.Registers.TlsAddr), out _);
+
+                Ns.Os.IdGen.DeleteId(Thread.ThreadId);
+            }
+        }
+
+        private int GetTlsSlot(long Position)
+        {
+            return (int)((Position - TlsPageAddr) / TlsSize);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/ServiceCtx.cs b/Ryujinx/OsHle/ServiceCtx.cs
new file mode 100644
index 0000000000..88ddcdb772
--- /dev/null
+++ b/Ryujinx/OsHle/ServiceCtx.cs
@@ -0,0 +1,52 @@
+using ChocolArm64.Memory;
+using Ryujinx.OsHle.Handles;
+using Ryujinx.OsHle.Ipc;
+using System.IO;
+
+namespace Ryujinx.OsHle
+{
+    class ServiceCtx
+    {
+        public Switch       Ns         { get; private set; }
+        public AMemory      Memory       { get; private set; }
+        public HSession     Session      { get; private set; }
+        public IpcMessage   Request      { get; private set; }
+        public IpcMessage   Response     { get; private set; }
+        public BinaryReader RequestData  { get; private set; }
+        public BinaryWriter ResponseData { get; private set; }
+
+        public ServiceCtx(
+            Switch       Ns,
+            AMemory      Memory,
+            HSession     Session,
+            IpcMessage   Request,
+            IpcMessage   Response,
+            BinaryReader RequestData,
+            BinaryWriter ResponseData)
+        {
+            this.Ns           = Ns;
+            this.Memory       = Memory;
+            this.Session      = Session;
+            this.Request      = Request;
+            this.Response     = Response;
+            this.RequestData  = RequestData;
+            this.ResponseData = ResponseData;
+        }
+
+        public T GetObject<T>()
+        {
+            object Obj = null;
+    
+            if (Session is HSessionObj SessionObj)
+            {
+                Obj = SessionObj.Obj; 
+            }
+            if (Session is HDomain Dom)
+            {
+                Obj = Dom.GetObject(Request.DomObjId);
+            }
+
+            return Obj is T ? (T)Obj : default(T);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Services/ServiceAcc.cs b/Ryujinx/OsHle/Services/ServiceAcc.cs
new file mode 100644
index 0000000000..14a3e83ea3
--- /dev/null
+++ b/Ryujinx/OsHle/Services/ServiceAcc.cs
@@ -0,0 +1,33 @@
+using Ryujinx.OsHle.Objects;
+
+using static Ryujinx.OsHle.Objects.ObjHelper;
+
+namespace Ryujinx.OsHle.Services
+{
+    static partial class Service
+    {
+        public static long AccU0ListOpenUsers(ServiceCtx Context)
+        {
+            return 0;
+        }
+
+        public static long AccU0GetProfile(ServiceCtx Context)
+        {
+            MakeObject(Context, new AccIProfile());
+
+            return 0;
+        }
+
+        public static long AccU0InitializeApplicationInfo(ServiceCtx Context)
+        {
+            return 0;
+        }
+
+        public static long AccU0GetBaasAccountManagerForApplication(ServiceCtx Context)
+        {
+            MakeObject(Context, new AccIManagerForApplication());
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Services/ServiceApm.cs b/Ryujinx/OsHle/Services/ServiceApm.cs
new file mode 100644
index 0000000000..f0df462b7b
--- /dev/null
+++ b/Ryujinx/OsHle/Services/ServiceApm.cs
@@ -0,0 +1,16 @@
+using Ryujinx.OsHle.Objects;
+
+using static Ryujinx.OsHle.Objects.ObjHelper;
+
+namespace Ryujinx.OsHle.Services
+{
+    static partial class Service
+    {
+        public static long ApmOpenSession(ServiceCtx Context)
+        {
+            MakeObject(Context, new ApmISession());
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Services/ServiceAppletOE.cs b/Ryujinx/OsHle/Services/ServiceAppletOE.cs
new file mode 100644
index 0000000000..2f98a20143
--- /dev/null
+++ b/Ryujinx/OsHle/Services/ServiceAppletOE.cs
@@ -0,0 +1,16 @@
+using Ryujinx.OsHle.Objects;
+
+using static Ryujinx.OsHle.Objects.ObjHelper;
+
+namespace Ryujinx.OsHle.Services
+{
+    static partial class Service
+    {
+        public static long AppletOpenApplicationProxy(ServiceCtx Context)
+        {
+            MakeObject(Context, new AmIApplicationProxy());
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Services/ServiceAud.cs b/Ryujinx/OsHle/Services/ServiceAud.cs
new file mode 100644
index 0000000000..96e7e54813
--- /dev/null
+++ b/Ryujinx/OsHle/Services/ServiceAud.cs
@@ -0,0 +1,60 @@
+using ChocolArm64.Memory;
+using Ryujinx.OsHle.Objects;
+using System.Text;
+
+using static Ryujinx.OsHle.Objects.ObjHelper;
+
+namespace Ryujinx.OsHle.Services
+{
+    static partial class Service
+    {
+        public static long AudOutListAudioOuts(ServiceCtx Context)
+        {
+            long Position = Context.Request.ReceiveBuff[0].Position;
+
+            AMemoryHelper.WriteBytes(Context.Memory, Position, Encoding.ASCII.GetBytes("iface"));
+
+            Context.ResponseData.Write(1);
+
+            return 0;
+        }
+
+        public static long AudOutOpenAudioOut(ServiceCtx Context)
+        {
+            Context.ResponseData.Write(48000);
+            Context.ResponseData.Write(2);
+            Context.ResponseData.Write(2);
+            Context.ResponseData.Write(0);
+
+            return 0;
+        }
+
+        public static long AudRenOpenAudioRenderer(ServiceCtx Context)
+        {
+            MakeObject(Context, new AudIAudioRenderer());
+
+            return 0;
+        }
+
+        public static long AudRenGetAudioRendererWorkBufferSize(ServiceCtx Context)
+        {
+            int SampleRate = Context.RequestData.ReadInt32();
+            int Unknown4   = Context.RequestData.ReadInt32();
+            int Unknown8   = Context.RequestData.ReadInt32();
+            int UnknownC   = Context.RequestData.ReadInt32();
+            int Unknown10  = Context.RequestData.ReadInt32();
+            int Unknown14  = Context.RequestData.ReadInt32();
+            int Unknown18  = Context.RequestData.ReadInt32();
+            int Unknown1c  = Context.RequestData.ReadInt32();
+            int Unknown20  = Context.RequestData.ReadInt32();
+            int Unknown24  = Context.RequestData.ReadInt32();
+            int Unknown28  = Context.RequestData.ReadInt32();
+            int Unknown2c  = Context.RequestData.ReadInt32();
+            int Rev1Magic  = Context.RequestData.ReadInt32();
+
+            Context.ResponseData.Write(0x400L);
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Services/ServiceFriend.cs b/Ryujinx/OsHle/Services/ServiceFriend.cs
new file mode 100644
index 0000000000..980d421967
--- /dev/null
+++ b/Ryujinx/OsHle/Services/ServiceFriend.cs
@@ -0,0 +1,16 @@
+using Ryujinx.OsHle.Objects;
+
+using static Ryujinx.OsHle.Objects.ObjHelper;
+
+namespace Ryujinx.OsHle.Services
+{
+    static partial class Service
+    {
+        public static long FriendCreateFriendService(ServiceCtx Context)
+        {
+            MakeObject(Context, new FriendIFriendService());
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Services/ServiceFspSrv.cs b/Ryujinx/OsHle/Services/ServiceFspSrv.cs
new file mode 100644
index 0000000000..e2ceb0aed7
--- /dev/null
+++ b/Ryujinx/OsHle/Services/ServiceFspSrv.cs
@@ -0,0 +1,42 @@
+using Ryujinx.OsHle.Objects;
+
+using static Ryujinx.OsHle.Objects.ObjHelper;
+
+namespace Ryujinx.OsHle.Services
+{
+    static partial class Service
+    {
+        public static long FspSrvInitialize(ServiceCtx Context)
+        {
+            return 0;
+        }
+
+        public static long FspSrvMountSaveData(ServiceCtx Context)
+        {
+            MakeObject(Context, new FspSrvIFileSystem(Context.Ns.VFs.GetGameSavesPath()));
+
+            return 0;
+        }
+
+        public static long FspSrvOpenDataStorageByCurrentProcess(ServiceCtx Context)
+        {
+            MakeObject(Context, new FspSrvIStorage(Context.Ns.VFs.RomFs));
+
+            return 0;
+        }
+
+        public static long FspSrvOpenRomStorage(ServiceCtx Context)
+        {
+            MakeObject(Context, new FspSrvIStorage(Context.Ns.VFs.RomFs));
+
+            return 0;
+        }
+
+        public static long FspSrvGetGlobalAccessLogMode(ServiceCtx Context)
+        {
+            Context.ResponseData.Write(0);
+
+            return 0;
+        }        
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Services/ServiceHid.cs b/Ryujinx/OsHle/Services/ServiceHid.cs
new file mode 100644
index 0000000000..30093f49fc
--- /dev/null
+++ b/Ryujinx/OsHle/Services/ServiceHid.cs
@@ -0,0 +1,56 @@
+using Ryujinx.OsHle.Handles;
+using Ryujinx.OsHle.Objects;
+
+using static Ryujinx.OsHle.Objects.ObjHelper;
+
+namespace Ryujinx.OsHle.Services
+{
+    static partial class Service
+    {
+        public static long HidCreateAppletResource(ServiceCtx Context)
+        {
+            HSharedMem HidHndData = Context.Ns.Os.Handles.GetData<HSharedMem>(Context.Ns.Os.HidHandle);
+
+            MakeObject(Context, new HidIAppletResource(HidHndData));
+
+            return 0;
+        }
+
+        public static long HidActivateTouchScreen(ServiceCtx Context)
+        {
+            long Unknown = Context.RequestData.ReadInt64();
+
+            return 0;
+        }
+
+        public static long HidSetSupportedNpadStyleSet(ServiceCtx Context)
+        {
+            long Unknown0 = Context.RequestData.ReadInt64();
+            long Unknown8 = Context.RequestData.ReadInt64();
+
+            return 0;
+        }
+
+        public static long HidSetSupportedNpadIdType(ServiceCtx Context)
+        {
+            long Unknown = Context.RequestData.ReadInt64();
+
+            return 0;
+        }
+
+        public static long HidActivateNpad(ServiceCtx Context)
+        {
+            long Unknown = Context.RequestData.ReadInt64();
+
+            return 0;
+        }
+
+        public static long HidSetNpadJoyHoldType(ServiceCtx Context)
+        {
+            long Unknown0 = Context.RequestData.ReadInt64();
+            long Unknown8 = Context.RequestData.ReadInt64();
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Services/ServiceLm.cs b/Ryujinx/OsHle/Services/ServiceLm.cs
new file mode 100644
index 0000000000..dc6acad952
--- /dev/null
+++ b/Ryujinx/OsHle/Services/ServiceLm.cs
@@ -0,0 +1,12 @@
+namespace Ryujinx.OsHle.Services
+{
+    static partial class Service
+    {
+        public static long LmInitialize(ServiceCtx Context)
+        {
+            Context.Session.Initialize();
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Services/ServiceNvDrv.cs b/Ryujinx/OsHle/Services/ServiceNvDrv.cs
new file mode 100644
index 0000000000..f7c0d3022b
--- /dev/null
+++ b/Ryujinx/OsHle/Services/ServiceNvDrv.cs
@@ -0,0 +1,601 @@
+using ChocolArm64.Memory;
+using Ryujinx.Gpu;
+using Ryujinx.OsHle.Handles;
+using Ryujinx.OsHle.Ipc;
+using Ryujinx.OsHle.Utilities;
+using System;
+using System.Collections.Generic;
+
+namespace Ryujinx.OsHle.Services
+{
+    static partial class Service
+    {
+        private delegate long ServiceProcessRequest(ServiceCtx Context);
+
+        private static Dictionary<(string, int), ServiceProcessRequest> IoctlCmds =
+                   new Dictionary<(string, int), ServiceProcessRequest>()
+        {
+            { ("/dev/nvhost-as-gpu",   0x4101), NvGpuAsIoctlBindChannel           },
+            { ("/dev/nvhost-as-gpu",   0x4102), NvGpuAsIoctlAllocSpace            },
+            { ("/dev/nvhost-as-gpu",   0x4106), NvGpuAsIoctlMapBufferEx           },
+            { ("/dev/nvhost-as-gpu",   0x4108), NvGpuAsIoctlGetVaRegions          },
+            { ("/dev/nvhost-as-gpu",   0x4109), NvGpuAsIoctlInitializeEx          },
+            { ("/dev/nvhost-ctrl",     0x001b), NvHostIoctlCtrlGetConfig          },
+            { ("/dev/nvhost-ctrl",     0x001d), NvHostIoctlCtrlEventWait          },
+            { ("/dev/nvhost-ctrl-gpu", 0x4701), NvGpuIoctlZcullGetCtxSize         },
+            { ("/dev/nvhost-ctrl-gpu", 0x4702), NvGpuIoctlZcullGetInfo            },
+            { ("/dev/nvhost-ctrl-gpu", 0x4705), NvGpuIoctlGetCharacteristics      },
+            { ("/dev/nvhost-ctrl-gpu", 0x4706), NvGpuIoctlGetTpcMasks             },
+            { ("/dev/nvhost-ctrl-gpu", 0x4714), NvGpuIoctlZbcGetActiveSlotMask    },
+            { ("/dev/nvhost-gpu",      0x4714), NvMapIoctlChannelSetUserData      },
+            { ("/dev/nvhost-gpu",      0x4801), NvMapIoctlChannelSetNvMap         },
+            { ("/dev/nvhost-gpu",      0x4808), NvMapIoctlChannelSubmitGpFifo     },
+            { ("/dev/nvhost-gpu",      0x4809), NvMapIoctlChannelAllocObjCtx      },
+            { ("/dev/nvhost-gpu",      0x480b), NvMapIoctlChannelZcullBind        },
+            { ("/dev/nvhost-gpu",      0x480c), NvMapIoctlChannelSetErrorNotifier },
+            { ("/dev/nvhost-gpu",      0x480d), NvMapIoctlChannelSetPriority      },
+            { ("/dev/nvhost-gpu",      0x481a), NvMapIoctlChannelAllocGpFifoEx2   },
+            { ("/dev/nvmap",           0x0101), NvMapIocCreate                    },
+            { ("/dev/nvmap",           0x0103), NvMapIocFromId                    },
+            { ("/dev/nvmap",           0x0104), NvMapIocAlloc                     },
+            { ("/dev/nvmap",           0x0109), NvMapIocParam                     },
+            { ("/dev/nvmap",           0x010e), NvMapIocGetId                     },
+        };
+
+        public static long NvDrvOpen(ServiceCtx Context)
+        {
+            long NamePtr = Context.Request.SendBuff[0].Position;
+
+            string Name = AMemoryHelper.ReadAsciiString(Context.Memory, NamePtr);
+
+            int Fd = Context.Ns.Os.Fds.GenerateId(new FileDesc(Name));
+
+            Context.ResponseData.Write(Fd);
+            Context.ResponseData.Write(0);
+
+            return 0;
+        }
+
+        public static long NvDrvIoctl(ServiceCtx Context)
+        {
+            int Fd  = Context.RequestData.ReadInt32();
+            int Cmd = Context.RequestData.ReadInt32() & 0xffff;
+            
+            FileDesc FdData = Context.Ns.Os.Fds.GetData<FileDesc>(Fd);
+
+            long Position = Context.Request.PtrBuff[0].Position;
+
+            Context.ResponseData.Write(0);
+
+            if (IoctlCmds.TryGetValue((FdData.Name, Cmd), out ServiceProcessRequest ProcReq))
+            {
+                return ProcReq(Context);
+            }
+            else
+            {
+                throw new NotImplementedException($"{FdData.Name} {Cmd:x4}");
+            }
+        }
+
+        public static long NvDrvClose(ServiceCtx Context)
+        {
+            int Fd = Context.RequestData.ReadInt32();
+
+            Context.Ns.Os.Fds.Delete(Fd);
+
+            Context.ResponseData.Write(0);
+
+            return 0;
+        }
+
+        public static long NvDrvInitialize(ServiceCtx Context)
+        {
+            long TransferMemSize   = Context.RequestData.ReadInt64();
+            int  TransferMemHandle = Context.Request.HandleDesc.ToCopy[0];
+
+            Context.ResponseData.Write(0);
+
+            return 0;
+        }
+
+        public static long NvDrvQueryEvent(ServiceCtx Context)
+        {
+            int Fd      = Context.RequestData.ReadInt32();
+            int EventId = Context.RequestData.ReadInt32();
+
+            Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(0xcafe);
+
+            Context.ResponseData.Write(0);
+
+            return 0;
+        }
+
+        private static long NvGpuAsIoctlBindChannel(ServiceCtx Context)
+        {
+            long Position = Context.Request.PtrBuff[0].Position;
+
+            int Fd = Context.Memory.ReadInt32(Position);
+
+            return 0;
+        }
+
+        private static long NvGpuAsIoctlAllocSpace(ServiceCtx Context)
+        {
+            long Position = Context.Request.PtrBuff[0].Position;
+
+            MemReader Reader = new MemReader(Context.Memory, Position);
+
+            int  Pages    = Reader.ReadInt32();
+            int  PageSize = Reader.ReadInt32();
+            int  Flags    = Reader.ReadInt32();
+            int  Padding  = Reader.ReadInt32();
+            long Align    = Reader.ReadInt64();
+
+            if ((Flags & 1) != 0)
+            {
+                Align = Context.Ns.Gpu.MemoryMgr.Reserve(Align, (long)Pages * PageSize, 1);
+            }
+            else
+            {
+                Align = Context.Ns.Gpu.MemoryMgr.Reserve((long)Pages * PageSize, Align);
+            }
+
+            Context.Memory.WriteInt64(Position + 0x10, Align);
+
+            return 0;
+        }
+
+        private static long NvGpuAsIoctlMapBufferEx(ServiceCtx Context)
+        {
+            long Position = Context.Request.PtrBuff[0].Position;
+
+            MemReader Reader = new MemReader(Context.Memory, Position);
+
+            int  Flags    = Reader.ReadInt32();
+            int  Kind     = Reader.ReadInt32();
+            int  Handle   = Reader.ReadInt32();
+            int  PageSize = Reader.ReadInt32();            
+            long BuffAddr = Reader.ReadInt64();
+            long MapSize  = Reader.ReadInt64();
+            long Offset   = Reader.ReadInt64();
+
+            HNvMap NvMap = Context.Ns.Os.Handles.GetData<HNvMap>(Handle);
+
+            if (NvMap != null)
+            {
+                if ((Flags & 1) != 0)
+                {
+                    Offset = Context.Ns.Gpu.MemoryMgr.Map(NvMap.Address, Offset, NvMap.Size);
+                }
+                else
+                {
+                    Offset = Context.Ns.Gpu.MemoryMgr.Map(NvMap.Address, NvMap.Size);
+                }
+            }
+
+            Context.Memory.WriteInt64(Position + 0x20, Offset);
+
+            return 0;
+        }
+
+        private static long NvGpuAsIoctlGetVaRegions(ServiceCtx Context)
+        {
+            long Position = Context.Request.PtrBuff[0].Position;
+
+            MemReader Reader = new MemReader(Context.Memory, Position);
+            MemWriter Writer = new MemWriter(Context.Memory, Position);
+
+            long Unused   = Reader.ReadInt64();
+            int  BuffSize = Reader.ReadInt32();
+            int  Padding  = Reader.ReadInt32();
+
+            BuffSize = 0x30;
+
+            Writer.WriteInt64(Unused);
+            Writer.WriteInt32(BuffSize);
+            Writer.WriteInt32(Padding);
+
+            Writer.WriteInt64(0);
+            Writer.WriteInt32(0);
+            Writer.WriteInt32(0);
+            Writer.WriteInt64(0);
+
+            Writer.WriteInt64(0);
+            Writer.WriteInt32(0);
+            Writer.WriteInt32(0);
+            Writer.WriteInt64(0);
+
+            return 0;
+        }
+
+        private static long NvGpuAsIoctlInitializeEx(ServiceCtx Context)
+        {
+            long Position = Context.Request.PtrBuff[0].Position;
+
+            MemReader Reader = new MemReader(Context.Memory, Position);
+
+            int  BigPageSize = Reader.ReadInt32();
+            int  AsFd        = Reader.ReadInt32();
+            int  Flags       = Reader.ReadInt32();
+            int  Reserved    = Reader.ReadInt32();
+            long Unknown10   = Reader.ReadInt64();
+            long Unknown18   = Reader.ReadInt64();
+            long Unknown20   = Reader.ReadInt64();
+
+            return 0;
+        }
+
+        private static long NvHostIoctlCtrlGetConfig(ServiceCtx Context)
+        {
+            long Position = Context.Request.PtrBuff[0].Position;
+
+            MemReader Reader = new MemReader(Context.Memory, Position);
+            MemWriter Writer = new MemWriter(Context.Memory, Position + 0x82);
+
+            for (int Index = 0; Index < 0x101; Index++)
+            {
+                Writer.WriteByte(0);
+            }
+
+            return 0;
+        }
+
+        private static long NvHostIoctlCtrlEventWait(ServiceCtx Context)
+        {
+            long Position = Context.Request.PtrBuff[0].Position;
+
+            MemReader Reader = new MemReader(Context.Memory, Position);
+
+            int SyncPtId  = Reader.ReadInt32();
+            int Threshold = Reader.ReadInt32();
+            int Timeout   = Reader.ReadInt32();
+            int Value     = Reader.ReadInt32();
+
+            Context.Memory.WriteInt32(Position + 0xc, 0xcafe);
+
+            return 0;
+        }
+
+        private static long NvGpuIoctlZcullGetCtxSize(ServiceCtx Context)
+        {
+            long Position = Context.Request.PtrBuff[0].Position;
+
+            Context.Memory.WriteInt32(Position, 1);
+
+            return 0;
+        }
+
+        private static long NvGpuIoctlZcullGetInfo(ServiceCtx Context)
+        {
+            long Position = Context.Request.PtrBuff[0].Position;
+
+            MemWriter Writer = new MemWriter(Context.Memory, Position);
+
+            Writer.WriteInt32(0);
+            Writer.WriteInt32(0);
+            Writer.WriteInt32(0);
+            Writer.WriteInt32(0);
+            Writer.WriteInt32(0);
+            Writer.WriteInt32(0);
+            Writer.WriteInt32(0);
+            Writer.WriteInt32(0);
+            Writer.WriteInt32(0);
+            Writer.WriteInt32(0);
+
+            return 0;
+        }
+
+        private static long NvGpuIoctlGetCharacteristics(ServiceCtx Context)
+        {
+            long Position = Context.Request.PtrBuff[0].Position;
+
+            MemReader Reader = new MemReader(Context.Memory, Position);
+            MemWriter Writer = new MemWriter(Context.Memory, Position);
+
+            //Note: We should just ignore the BuffAddr, because official code
+            //does __memcpy_device from Position + 0x10 to BuffAddr.
+            long BuffSize = Reader.ReadInt64();
+            long BuffAddr = Reader.ReadInt64();
+
+            BuffSize = 0xa0;
+
+            Writer.WriteInt64(BuffSize);
+            Writer.WriteInt64(BuffAddr);
+            Writer.WriteInt32(0x120);  //NVGPU_GPU_ARCH_GM200
+            Writer.WriteInt32(0xb);    //NVGPU_GPU_IMPL_GM20B
+            Writer.WriteInt32(0xa1);
+            Writer.WriteInt32(1);
+            Writer.WriteInt64(0x40000);
+            Writer.WriteInt64(0);
+            Writer.WriteInt32(2);
+            Writer.WriteInt32(0x20);   //NVGPU_GPU_BUS_TYPE_AXI
+            Writer.WriteInt32(0x20000);
+            Writer.WriteInt32(0x20000);
+            Writer.WriteInt32(0x1b);
+            Writer.WriteInt32(0x30000);
+            Writer.WriteInt32(1);
+            Writer.WriteInt32(0x503);
+            Writer.WriteInt32(0x503);
+            Writer.WriteInt32(0x80);
+            Writer.WriteInt32(0x28);
+            Writer.WriteInt32(0);
+            Writer.WriteInt64(0x55);
+            Writer.WriteInt32(0x902d); //FERMI_TWOD_A
+            Writer.WriteInt32(0xb197); //MAXWELL_B
+            Writer.WriteInt32(0xb1c0); //MAXWELL_COMPUTE_B
+            Writer.WriteInt32(0xb06f); //MAXWELL_CHANNEL_GPFIFO_A
+            Writer.WriteInt32(0xa140); //KEPLER_INLINE_TO_MEMORY_B
+            Writer.WriteInt32(0xb0b5); //MAXWELL_DMA_COPY_A
+            Writer.WriteInt32(1);
+            Writer.WriteInt32(0);
+            Writer.WriteInt32(2);
+            Writer.WriteInt32(1);
+            Writer.WriteInt32(0);
+            Writer.WriteInt32(1);
+            Writer.WriteInt32(0x21d70);
+            Writer.WriteInt32(0);
+            Writer.WriteByte((byte)'g');
+            Writer.WriteByte((byte)'m');
+            Writer.WriteByte((byte)'2');
+            Writer.WriteByte((byte)'0');
+            Writer.WriteByte((byte)'b');
+            Writer.WriteByte((byte)'\0');
+            Writer.WriteByte((byte)'\0');
+            Writer.WriteByte((byte)'\0');
+            Writer.WriteInt64(0);
+
+            return 0;
+        }
+
+        private static long NvGpuIoctlGetTpcMasks(ServiceCtx Context)
+        {
+            long Position = Context.Request.PtrBuff[0].Position;
+
+            MemReader Reader = new MemReader(Context.Memory, Position);
+
+            int  MaskBuffSize = Reader.ReadInt32();
+            int  Reserved     = Reader.ReadInt32();
+            long MaskBuffAddr = Reader.ReadInt64();
+            long Unknown      = Reader.ReadInt64();
+
+            return 0;
+        }
+
+        private static long NvGpuIoctlZbcGetActiveSlotMask(ServiceCtx Context)
+        {
+            long Position = Context.Request.PtrBuff[0].Position;
+
+            Context.Memory.WriteInt32(Position + 0, 7);
+            Context.Memory.WriteInt32(Position + 4, 1);
+
+            return 0;
+        }
+
+        private static long NvMapIoctlChannelSetUserData(ServiceCtx Context)
+        {
+            long Position = Context.Request.PtrBuff[0].Position;
+
+            return 0;
+        }
+
+        private static long NvMapIoctlChannelSetNvMap(ServiceCtx Context)
+        {
+            long Position = Context.Request.PtrBuff[0].Position;
+
+            int Fd = Context.Memory.ReadInt32(Position);
+
+            return 0;
+        }
+
+        private static long NvMapIoctlChannelSubmitGpFifo(ServiceCtx Context)
+        {
+            long Position = Context.Request.PtrBuff[0].Position;
+
+            MemReader Reader = new MemReader(Context.Memory, Position);
+            MemWriter Writer = new MemWriter(Context.Memory, Position + 0x10);
+
+            long GpFifo   = Reader.ReadInt64();
+            int  Count    = Reader.ReadInt32();
+            int  Flags    = Reader.ReadInt32();
+            int  FenceId  = Reader.ReadInt32();
+            int  FenceVal = Reader.ReadInt32();
+
+            for (int Index = 0; Index < Count; Index++)
+            {
+                long GpFifoHdr = Reader.ReadInt64();
+
+                long GpuAddr = GpFifoHdr & 0xffffffffff;
+
+                int Size = (int)(GpFifoHdr >> 40) & 0x7ffffc;
+
+                long CpuAddr = Context.Ns.Gpu.MemoryMgr.GetCpuAddr(GpuAddr);
+
+                if (CpuAddr != -1)
+                {
+                    byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, CpuAddr, Size);
+
+                    NsGpuPBEntry[] PushBuffer = NsGpuPBEntry.DecodePushBuffer(Data);
+
+                    Context.Ns.Gpu.PGraph.ProcessPushBuffer(PushBuffer, Context.Memory);
+                }
+            }
+
+            Writer.WriteInt32(0);
+            Writer.WriteInt32(0);
+
+            return 0;
+        }
+
+        private static long NvMapIoctlChannelAllocObjCtx(ServiceCtx Context)
+        {
+            long Position = Context.Request.PtrBuff[0].Position;
+
+            int ClassNum = Context.Memory.ReadInt32(Position + 0);
+            int Flags    = Context.Memory.ReadInt32(Position + 4);
+
+            Context.Memory.WriteInt32(Position + 8, 0);
+
+            return 0;
+        }
+
+        private static long NvMapIoctlChannelZcullBind(ServiceCtx Context)
+        {
+            long Position = Context.Request.PtrBuff[0].Position;
+
+            MemReader Reader = new MemReader(Context.Memory, Position);
+
+            long GpuVa   = Reader.ReadInt64();
+            int  Mode    = Reader.ReadInt32();
+            int  Padding = Reader.ReadInt32();
+
+            return 0;
+        }
+
+        private static long NvMapIoctlChannelSetErrorNotifier(ServiceCtx Context)
+        {
+            long Position = Context.Request.PtrBuff[0].Position;
+
+            MemReader Reader = new MemReader(Context.Memory, Position);
+
+            long Offset  = Reader.ReadInt64();
+            long Size    = Reader.ReadInt64();
+            int  Mem     = Reader.ReadInt32();
+            int  Padding = Reader.ReadInt32();
+
+            return 0;
+        }
+
+        private static long NvMapIoctlChannelSetPriority(ServiceCtx Context)
+        {
+            long Position = Context.Request.PtrBuff[0].Position;
+
+            int Priority = Context.Memory.ReadInt32(Position);
+
+            return 0;
+        }
+
+        private static long NvMapIoctlChannelAllocGpFifoEx2(ServiceCtx Context)
+        {
+            long Position = Context.Request.PtrBuff[0].Position;
+
+            MemReader Reader = new MemReader(Context.Memory, Position);
+            MemWriter Writer = new MemWriter(Context.Memory, Position + 0xc);
+
+            int  Count     = Reader.ReadInt32();
+            int  Flags     = Reader.ReadInt32();
+            int  Unknown8  = Reader.ReadInt32();
+            long Fence     = Reader.ReadInt64();
+            int  Unknown14 = Reader.ReadInt32();
+            int  Unknown18 = Reader.ReadInt32();
+
+            Writer.WriteInt32(0);
+            Writer.WriteInt32(0);
+
+            return 0;
+        }
+
+        private static long NvMapIocCreate(ServiceCtx Context)
+        {
+            long Position = Context.Request.GetSendBuffPtr();
+
+            int Size = Context.Memory.ReadInt32(Position);
+
+            int Id = Context.Ns.Os.NvMapIds.GenerateId();
+
+            int Handle = Context.Ns.Os.Handles.GenerateId(new HNvMap(Id, Size));
+
+            Context.Memory.WriteInt32(Position + 4, Handle);
+
+            return 0;
+        }
+
+        private static long NvMapIocFromId(ServiceCtx Context)
+        {
+            long Position = Context.Request.GetSendBuffPtr();
+
+            int Id = Context.Memory.ReadInt32(Position);
+
+            int Handle = -1;
+
+            foreach (KeyValuePair<int, object> KV in Context.Ns.Os.Handles)
+            {
+                if (KV.Value is HNvMap NvMap && NvMap.Id == Id)
+                {
+                    Handle = KV.Key;
+
+                    break;
+                }
+            }
+
+            Context.Memory.WriteInt32(Position + 4, Handle);
+
+            return 0;
+        }
+
+        private static long NvMapIocAlloc(ServiceCtx Context)
+        {
+            long Position = Context.Request.GetSendBuffPtr();
+
+            MemReader Reader = new MemReader(Context.Memory, Position);
+
+            int  Handle   =       Reader.ReadInt32();
+            int  HeapMask =       Reader.ReadInt32();
+            int  Flags    =       Reader.ReadInt32();
+            int  Align    =       Reader.ReadInt32();
+            byte Kind     = (byte)Reader.ReadInt64();
+            long Addr     =       Reader.ReadInt64();
+
+            HNvMap NvMap = Context.Ns.Os.Handles.GetData<HNvMap>(Handle);
+
+            if (NvMap != null)
+            {
+                NvMap.Address = Addr;
+                NvMap.Align   = Align;
+                NvMap.Kind    = Kind;
+            }
+
+            Console.WriteLine($"NvMapIocAlloc at {NvMap.Address:x16}");
+
+            return 0;
+        }
+
+        private static long NvMapIocParam(ServiceCtx Context)
+        {
+            long Position = Context.Request.GetSendBuffPtr();
+
+            MemReader Reader = new MemReader(Context.Memory, Position);
+
+            int Handle = Reader.ReadInt32();
+            int Param  = Reader.ReadInt32();
+
+            HNvMap NvMap = Context.Ns.Os.Handles.GetData<HNvMap>(Handle);
+
+            int Response = 0;
+            
+            switch (Param)
+            {
+                case 1: Response = NvMap.Size;  break;
+                case 2: Response = NvMap.Align; break;
+                case 4: Response = 0x40000000;  break;
+                case 5: Response = NvMap.Kind;  break;
+            }
+
+            Context.Memory.WriteInt32(Position + 8, Response);
+
+            return 0;
+        }
+
+        private static long NvMapIocGetId(ServiceCtx Context)
+        {
+            long Position = Context.Request.GetSendBuffPtr();
+
+            int Handle = Context.Memory.ReadInt32(Position + 4);
+
+            HNvMap NvMap = Context.Ns.Os.Handles.GetData<HNvMap>(Handle);
+
+            Context.Memory.WriteInt32(Position, NvMap.Id);
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Services/ServicePctl.cs b/Ryujinx/OsHle/Services/ServicePctl.cs
new file mode 100644
index 0000000000..62537f2874
--- /dev/null
+++ b/Ryujinx/OsHle/Services/ServicePctl.cs
@@ -0,0 +1,16 @@
+using Ryujinx.OsHle.Objects;
+
+using static Ryujinx.OsHle.Objects.ObjHelper;
+
+namespace Ryujinx.OsHle.Services
+{
+    static partial class Service
+    {
+        public static long PctlCreateService(ServiceCtx Context)
+        {
+            MakeObject(Context, new AmIParentalControlService());
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Services/ServicePl.cs b/Ryujinx/OsHle/Services/ServicePl.cs
new file mode 100644
index 0000000000..6981637f9c
--- /dev/null
+++ b/Ryujinx/OsHle/Services/ServicePl.cs
@@ -0,0 +1,35 @@
+using Ryujinx.OsHle.Ipc;
+
+namespace Ryujinx.OsHle.Services
+{
+    static partial class Service
+    {
+        public static long PlGetLoadState(ServiceCtx Context)
+        {
+            Context.ResponseData.Write(1); //Loaded
+
+            return 0;
+        }
+
+        public static long PlGetFontSize(ServiceCtx Context)
+        {
+            Context.ResponseData.Write(Horizon.FontSize);
+
+            return 0;
+        }
+
+        public static long PlGetSharedMemoryAddressOffset(ServiceCtx Context)
+        {
+            Context.ResponseData.Write(0);
+
+            return 0;
+        }
+
+        public static long PlGetSharedMemoryNativeHandle(ServiceCtx Context)
+        {
+            Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Context.Ns.Os.FontHandle);
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Services/ServiceSet.cs b/Ryujinx/OsHle/Services/ServiceSet.cs
new file mode 100644
index 0000000000..f98e8f0de2
--- /dev/null
+++ b/Ryujinx/OsHle/Services/ServiceSet.cs
@@ -0,0 +1,32 @@
+using ChocolArm64.Memory;
+
+namespace Ryujinx.OsHle.Services
+{
+    static partial class Service
+    {
+        private const int LangCodesCount = 13;
+
+        public static long SetGetAvailableLanguageCodes(ServiceCtx Context)
+        {
+            int PtrBuffSize = Context.RequestData.ReadInt32();
+
+            if (Context.Request.RecvListBuff.Count > 0)
+            {
+                long  Position = Context.Request.RecvListBuff[0].Position;
+                short Size     = Context.Request.RecvListBuff[0].Size;
+
+                //This should return an array of ints with values matching the LanguageCode enum.
+                byte[] Data = new byte[Size];
+
+                Data[0] = 0;
+                Data[1] = 1;
+
+                AMemoryHelper.WriteBytes(Context.Memory, Position, Data);
+            }
+
+            Context.ResponseData.Write(LangCodesCount);
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Services/ServiceSm.cs b/Ryujinx/OsHle/Services/ServiceSm.cs
new file mode 100644
index 0000000000..8af3ed5e4d
--- /dev/null
+++ b/Ryujinx/OsHle/Services/ServiceSm.cs
@@ -0,0 +1,48 @@
+using Ryujinx.OsHle.Handles;
+using Ryujinx.OsHle.Ipc;
+
+namespace Ryujinx.OsHle.Services
+{
+    static partial class Service
+    {
+        private const int SmNotInitialized = 0x415;
+
+        public static long SmInitialize(ServiceCtx Context)
+        {
+            Context.Session.Initialize();
+
+            return 0;
+        }
+
+        public static long SmGetService(ServiceCtx Context)
+        {
+            //Only for kernel version > 3.0.0.
+            if (!Context.Session.IsInitialized)
+            {
+                //return SmNotInitialized;
+            }
+
+            string Name = string.Empty;
+
+            for (int Index = 0; Index < 8 &&
+                Context.RequestData.BaseStream.Position <
+                Context.RequestData.BaseStream.Length; Index++)
+            {
+                byte Chr = Context.RequestData.ReadByte();
+
+                if (Chr >= 0x20 && Chr < 0x7f)
+                {
+                    Name += (char)Chr;
+                }
+            }
+
+            HSession Session = new HSession(Name);
+
+            int Handle = Context.Ns.Os.Handles.GenerateId(Session);
+
+            Context.Response.HandleDesc = IpcHandleDesc.MakeMove(Handle);
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Services/ServiceTime.cs b/Ryujinx/OsHle/Services/ServiceTime.cs
new file mode 100644
index 0000000000..2b93e3db82
--- /dev/null
+++ b/Ryujinx/OsHle/Services/ServiceTime.cs
@@ -0,0 +1,37 @@
+using Ryujinx.OsHle.Objects;
+
+using static Ryujinx.OsHle.Objects.ObjHelper;
+
+namespace Ryujinx.OsHle.Services
+{
+    static partial class Service
+    {
+        public static long TimeGetStandardUserSystemClock(ServiceCtx Context)
+        {
+            MakeObject(Context, new TimeISystemClock());
+
+            return 0;
+        }
+
+        public static long TimeGetStandardNetworkSystemClock(ServiceCtx Context)
+        {
+            MakeObject(Context, new TimeISystemClock());
+
+            return 0;
+        }
+
+        public static long TimeGetStandardSteadyClock(ServiceCtx Context)
+        {
+            MakeObject(Context, new TimeISteadyClock());
+
+            return 0;
+        }
+
+        public static long TimeGetTimeZoneService(ServiceCtx Context)
+        {
+            MakeObject(Context, new TimeITimeZoneService());
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Services/ServiceVi.cs b/Ryujinx/OsHle/Services/ServiceVi.cs
new file mode 100644
index 0000000000..d5c86bfc26
--- /dev/null
+++ b/Ryujinx/OsHle/Services/ServiceVi.cs
@@ -0,0 +1,18 @@
+using Ryujinx.OsHle.Objects;
+
+using static Ryujinx.OsHle.Objects.ObjHelper;
+
+namespace Ryujinx.OsHle.Services
+{
+    static partial class Service
+    {
+        public static long ViGetDisplayService(ServiceCtx Context)
+        {
+            int Unknown = Context.RequestData.ReadInt32();
+
+            MakeObject(Context, new ViIApplicationDisplayService());
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Svc/SvcHandler.cs b/Ryujinx/OsHle/Svc/SvcHandler.cs
new file mode 100644
index 0000000000..245d243892
--- /dev/null
+++ b/Ryujinx/OsHle/Svc/SvcHandler.cs
@@ -0,0 +1,79 @@
+using ChocolArm64.Memory;
+using ChocolArm64.State;
+using System;
+using System.Collections.Generic;
+
+namespace Ryujinx.OsHle.Svc
+{
+    partial class SvcHandler
+    {
+        private delegate void SvcFunc(Switch Ns, ARegisters Registers, AMemory Memory);
+
+        private Dictionary<int, SvcFunc> SvcFuncs = new Dictionary<int, SvcFunc>()
+        {
+            { 0x01, SvcSetHeapSize                   },
+            { 0x03, SvcSetMemoryAttribute            },
+            { 0x04, SvcMapMemory                     },
+            { 0x06, SvcQueryMemory                   },
+            { 0x08, SvcCreateThread                  },
+            { 0x09, SvcStartThread                   },
+            { 0x0b, SvcSleepThread                   },
+            { 0x0c, SvcGetThreadPriority             },
+            { 0x13, SvcMapSharedMemory               },
+            { 0x14, SvcUnmapSharedMemory             },
+            { 0x15, SvcCreateTransferMemory          },
+            { 0x16, SvcCloseHandle                   },
+            { 0x17, SvcResetSignal                   },
+            { 0x18, SvcWaitSynchronization           },
+            { 0x1a, SvcArbitrateLock                 },
+            { 0x1b, SvcArbitrateUnlock               },
+            { 0x1c, SvcWaitProcessWideKeyAtomic      },
+            { 0x1d, SvcSignalProcessWideKey          },
+            { 0x1e, SvcGetSystemTick                 },
+            { 0x1f, SvcConnectToNamedPort            },
+            { 0x21, SvcSendSyncRequest               },
+            { 0x22, SvcSendSyncRequestWithUserBuffer },
+            { 0x26, SvcBreak                         },
+            { 0x27, SvcOutputDebugString             },
+            { 0x29, SvcGetInfo                       }
+        };
+
+        enum SvcResult
+        {
+            Success      = 0,
+            ErrBadHandle = 0xe401,
+            ErrTimeout   = 0xea01,
+            ErrBadIpcReq = 0xf601,
+        }
+
+        private Switch  Ns;
+        private AMemory Memory;
+
+        private static Random Rng;
+
+        public SvcHandler(Switch Ns, AMemory Memory)
+        {
+            this.Ns     = Ns;
+            this.Memory = Memory;
+        }
+
+        static SvcHandler()
+        {
+            Rng = new Random();
+        }
+
+        public void SvcCall(object sender, SvcEventArgs e)
+        {
+            ARegisters Registers = (ARegisters)sender;
+
+            if (SvcFuncs.TryGetValue(e.Id, out SvcFunc Func))
+            {
+                Func(Ns, Registers, Memory);
+            }
+            else
+            {
+                throw new NotImplementedException(e.Id.ToString("x3"));
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Svc/SvcMemory.cs b/Ryujinx/OsHle/Svc/SvcMemory.cs
new file mode 100644
index 0000000000..c1249b403a
--- /dev/null
+++ b/Ryujinx/OsHle/Svc/SvcMemory.cs
@@ -0,0 +1,126 @@
+using ChocolArm64.Memory;
+using ChocolArm64.State;
+using Ryujinx.OsHle.Handles;
+
+namespace Ryujinx.OsHle.Svc
+{
+    partial class SvcHandler
+    {
+        private static void SvcSetHeapSize(Switch Ns, ARegisters Registers, AMemory Memory)
+        {
+            int Size = (int)Registers.X1;
+
+            Memory.Manager.SetHeapSize(Size, (int)MemoryType.Heap);
+
+            Registers.X0 = (int)SvcResult.Success;
+            Registers.X1 = (ulong)Memory.Manager.HeapAddr;
+        }
+
+        private static void SvcSetMemoryAttribute(Switch Ns, ARegisters Registers, AMemory Memory)
+        {
+            long Position = (long)Registers.X0;
+            long Size     = (long)Registers.X1;
+            int  State0   =  (int)Registers.X2;
+            int  State1   =  (int)Registers.X3;
+
+            //TODO
+
+            Registers.X0 = (int)SvcResult.Success;
+        }
+
+        private static void SvcMapMemory(Switch Ns, ARegisters Registers, AMemory Memory)
+        {
+            long Src  = (long)Registers.X0;
+            long Dst  = (long)Registers.X1;
+            long Size = (long)Registers.X2;
+
+            Memory.Manager.MapMirror(Src, Dst, Size, (int)MemoryType.MappedMemory);
+
+            Registers.X0 = (int)SvcResult.Success;
+        }
+
+        private static void SvcQueryMemory(Switch Ns, ARegisters Registers, AMemory Memory)
+        {
+            long InfoPtr  = (long)Registers.X0;
+            long Position = (long)Registers.X2;
+
+            AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Position);
+
+            MemoryInfo Info = new MemoryInfo(MapInfo);
+
+            Memory.WriteInt64(InfoPtr + 0x00, Info.BaseAddress);
+            Memory.WriteInt64(InfoPtr + 0x08, Info.Size);
+            Memory.WriteInt32(InfoPtr + 0x10, Info.MemType);
+            Memory.WriteInt32(InfoPtr + 0x14, Info.MemAttr);
+            Memory.WriteInt32(InfoPtr + 0x18, Info.MemPerm);
+            Memory.WriteInt32(InfoPtr + 0x1c, Info.IpcRefCount);
+            Memory.WriteInt32(InfoPtr + 0x20, Info.DeviceRefCount);
+            Memory.WriteInt32(InfoPtr + 0x24, Info.Padding);
+
+            //TODO: X1.
+
+            Registers.X0 = (int)SvcResult.Success;
+            Registers.X1 = 0;
+        }
+
+        private static void SvcMapSharedMemory(Switch Ns, ARegisters Registers, AMemory Memory)
+        {
+            int  Handle   =  (int)Registers.X0;
+            long Position = (long)Registers.X1;
+            long Size     = (long)Registers.X2;
+            int  Perm     =  (int)Registers.X3;
+
+            HSharedMem HndData = Ns.Os.Handles.GetData<HSharedMem>(Handle);
+
+            if (HndData != null)
+            {
+                long Src = Position;
+                long Dst = HndData.PhysPos;
+
+                if (Memory.Manager.MapPhys(Src, Dst, Size,
+                    (int)MemoryType.SharedMemory, (AMemoryPerm)Perm))
+                {
+                    Registers.X0 = (int)SvcResult.Success;
+                }
+            }
+
+            //TODO: Error codes.
+        }
+
+        private static void SvcUnmapSharedMemory(Switch Ns, ARegisters Registers, AMemory Memory)
+        {
+            int  Handle   =  (int)Registers.X0;
+            long Position = (long)Registers.X1;
+            long Size     = (long)Registers.X2;
+
+            HSharedMem HndData = Ns.Os.Handles.GetData<HSharedMem>(Handle);
+
+            if (HndData != null)
+            {
+                Registers.X0 = (int)SvcResult.Success;
+            }
+
+            //TODO: Error codes.
+        }
+
+        private static void SvcCreateTransferMemory(Switch Ns, ARegisters Registers, AMemory Memory)
+        {
+            long Position = (long)Registers.X1;
+            long Size     = (long)Registers.X2;
+            int  Perm     =  (int)Registers.X3;
+
+            AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Position);
+
+            Memory.Manager.Reprotect(Position, Size, (AMemoryPerm)Perm);
+
+            long PhysPos = Memory.Manager.GetPhys(Position, AMemoryPerm.None);
+
+            HTransferMem HndData = new HTransferMem(Memory, MapInfo.Perm, Position, Size, PhysPos);
+
+            int Handle = Ns.Os.Handles.GenerateId(HndData);
+
+            Registers.X1 = (ulong)Handle;
+            Registers.X0 = (int)SvcResult.Success;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Svc/SvcSystem.cs b/Ryujinx/OsHle/Svc/SvcSystem.cs
new file mode 100644
index 0000000000..6f614d1622
--- /dev/null
+++ b/Ryujinx/OsHle/Svc/SvcSystem.cs
@@ -0,0 +1,163 @@
+using ChocolArm64.Memory;
+using ChocolArm64.State;
+using Ryujinx.OsHle.Handles;
+using Ryujinx.OsHle.Ipc;
+using System;
+
+namespace Ryujinx.OsHle.Svc
+{
+    partial class SvcHandler
+    {
+        private static void SvcCloseHandle(Switch Ns, ARegisters Registers, AMemory Memory)
+        {
+            int Handle = (int)Registers.X0;
+
+            Ns.Os.CloseHandle(Handle);
+
+            Registers.X0 = (int)SvcResult.Success;
+        }
+
+        private static void SvcResetSignal(Switch Ns, ARegisters Registers, AMemory Memory)
+        {
+            int Handle = (int)Registers.X0;
+
+            //TODO: Implement events.
+
+            Registers.X0 = (int)SvcResult.Success;
+        }
+
+        private static void SvcWaitSynchronization(Switch Ns, ARegisters Registers, AMemory Memory)
+        {
+            long HandlesPtr   = (long)Registers.X0;
+            int  HandlesCount =  (int)Registers.X2;
+            long Timeout      = (long)Registers.X3;
+
+            //TODO: Implement events.
+
+            Registers.X0 = (int)SvcResult.Success;
+        }
+
+        private static void SvcGetSystemTick(Switch Ns, ARegisters Registers, AMemory Memory)
+        {
+            Registers.X0 = (ulong)Registers.GetSystemReg(3, 3, 14, 0, 1);
+        }
+
+        private static void SvcConnectToNamedPort(Switch Ns, ARegisters Registers, AMemory Memory)
+        {
+            long StackPtr = (long)Registers.X0;
+            long NamePtr  = (long)Registers.X1;
+
+            string Name = AMemoryHelper.ReadAsciiString(Memory, NamePtr, 8);
+
+            //TODO: Validate that app has perms to access the service, and that the service
+            //actually exists, return error codes otherwise.
+
+            HSession Session = new HSession(Name);
+
+            Registers.X1 = (ulong)Ns.Os.Handles.GenerateId(Session);
+            Registers.X0 = (int)SvcResult.Success;
+        }
+
+        private static void SvcSendSyncRequest(Switch Ns, ARegisters Registers, AMemory Memory)
+        {
+            SendSyncRequest(Ns, Registers, Memory, false);
+        }
+
+        private static void SvcSendSyncRequestWithUserBuffer(Switch Ns, ARegisters Registers, AMemory Memory)
+        {
+            SendSyncRequest(Ns, Registers, Memory, true);
+        }
+
+        private static void SendSyncRequest(Switch Ns, ARegisters Registers, AMemory Memory, bool IsUser)
+        {
+            long CmdPtr = Registers.TlsAddr;
+            long Size   = 0x100;
+            int  Handle = 0;
+
+            if (IsUser)
+            {
+                CmdPtr = (long)Registers.X0;
+                Size   = (long)Registers.X1;
+                Handle =  (int)Registers.X2;
+            }
+            else
+            {
+                Handle = (int)Registers.X0;
+            }
+
+            byte[] CmdData = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size);
+
+            HSession Session = Ns.Os.Handles.GetData<HSession>(Handle);
+
+            IpcMessage Cmd = new IpcMessage(CmdData, CmdPtr, Session is HDomain);
+
+            if (Session != null)
+            {
+                IpcHandler.ProcessRequest(Ns, Memory, Session, Cmd, CmdPtr, Handle);
+
+                byte[] Response = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size);
+
+                Registers.X0 = (int)SvcResult.Success;
+            }
+            else
+            {
+                Registers.X0 = (int)SvcResult.ErrBadIpcReq;
+            }
+        }
+
+        private static void SvcBreak(Switch Ns, ARegisters Registers, AMemory Memory)
+        {
+            long Reason  = (long)Registers.X0;
+            long Unknown = (long)Registers.X1;
+            long Info    = (long)Registers.X2;
+
+            throw new Exception($"SvcBreak: {Reason} {Unknown} {Info}");
+        }
+
+        private static void SvcOutputDebugString(Switch Ns, ARegisters Registers, AMemory Memory)
+        {
+            long Position = (long)Registers.X0;
+            long Size     = (long)Registers.X1;
+
+            string Str = AMemoryHelper.ReadAsciiString(Memory, Position, (int)Size);
+
+            Console.WriteLine($"SvcOutputDebugString: {Str}");
+
+            Registers.X0 = (int)SvcResult.Success;
+        }
+
+        private static void SvcGetInfo(Switch Ns, ARegisters Registers, AMemory Memory)
+        {
+            long StackPtr = (long)Registers.X0;
+            int  InfoType =  (int)Registers.X1;
+            long Handle   = (long)Registers.X2;
+            int  InfoId   =  (int)Registers.X3;
+
+            switch (InfoType)
+            {
+                case 6:  Registers.X1 = GetTotalMem(Memory); break;
+                case 7:  Registers.X1 = GetUsedMem(Memory);  break;
+                case 11: Registers.X1 = GetRnd64();          break;
+
+                default: throw new NotImplementedException($"SvcGetInfo: {InfoType} {Handle} {InfoId}");
+            }
+
+            Registers.X0 = (int)SvcResult.Success;
+        }
+
+        private static ulong GetTotalMem(AMemory Memory)
+        {
+            return (ulong)Memory.Manager.GetTotalMemorySize();
+        }
+
+        private static ulong GetUsedMem(AMemory Memory)
+        {
+            return (ulong)Memory.Manager.GetUsedMemorySize();
+        }
+
+        private static ulong GetRnd64()
+        {
+            return (ulong)Rng.Next() + ((ulong)Rng.Next() << 32);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Svc/SvcThread.cs b/Ryujinx/OsHle/Svc/SvcThread.cs
new file mode 100644
index 0000000000..895cb247f5
--- /dev/null
+++ b/Ryujinx/OsHle/Svc/SvcThread.cs
@@ -0,0 +1,79 @@
+using ChocolArm64.Memory;
+using ChocolArm64.State;
+using Ryujinx.OsHle.Handles;
+using System.Threading;
+
+namespace Ryujinx.OsHle.Svc
+{
+    partial class SvcHandler
+    {
+        private static void SvcCreateThread(Switch Ns, ARegisters Registers, AMemory Memory)
+        {
+            long EntryPoint  = (long)Registers.X1;
+            long ArgsPtr     = (long)Registers.X2;
+            long StackTop    = (long)Registers.X3;
+            int  Priority    =  (int)Registers.X4;
+            int  ProcessorId =  (int)Registers.X5;
+
+            if (Ns.Os.TryGetProcess(Registers.ProcessId, out Process Process))
+            {
+                int Handle = Process.MakeThread(
+                    EntryPoint,
+                    StackTop,
+                    ArgsPtr,
+                    Priority,
+                    ProcessorId);                
+
+                Registers.X0 = (int)SvcResult.Success;
+                Registers.X1 = (ulong)Handle;
+            }
+
+            //TODO: Error codes.
+        }
+
+        private static void SvcStartThread(Switch Ns, ARegisters Registers, AMemory Memory)
+        {
+            int Handle = (int)Registers.X0;
+
+            HThread HndData = Ns.Os.Handles.GetData<HThread>(Handle);
+
+            if (HndData != null)
+            {
+                HndData.Thread.Execute();
+
+                Registers.X0 = (int)SvcResult.Success;
+            }
+
+            //TODO: Error codes.
+        }
+
+        private static void SvcSleepThread(Switch Ns, ARegisters Registers, AMemory Memory)
+        {           
+            ulong NanoSecs = Registers.X0;
+
+            if (NanoSecs == 0)
+            {
+                Thread.Yield();
+            }
+            else
+            {
+                Thread.Sleep((int)(NanoSecs / 1000000));
+            }
+        }
+
+        private static void SvcGetThreadPriority(Switch Ns, ARegisters Registers, AMemory Memory)
+        {
+            int Handle = (int)Registers.X1;
+
+            HThread HndData = Ns.Os.Handles.GetData<HThread>(Handle);
+
+            if (HndData != null)
+            {
+                Registers.X1 = (ulong)HndData.Thread.Priority;
+                Registers.X0 = (int)SvcResult.Success;
+            }
+
+            //TODO: Error codes.
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Svc/SvcThreadSync.cs b/Ryujinx/OsHle/Svc/SvcThreadSync.cs
new file mode 100644
index 0000000000..1df75625e6
--- /dev/null
+++ b/Ryujinx/OsHle/Svc/SvcThreadSync.cs
@@ -0,0 +1,87 @@
+using ChocolArm64;
+using ChocolArm64.Memory;
+using ChocolArm64.State;
+using Ryujinx.OsHle.Handles;
+
+namespace Ryujinx.OsHle.Svc
+{
+    partial class SvcHandler
+    {
+        private static void SvcArbitrateLock(Switch Ns, ARegisters Registers, AMemory Memory)
+        {
+            int  OwnerThreadHandle      =  (int)Registers.X0;
+            long MutexAddress           = (long)Registers.X1;
+            int  RequestingThreadHandle =  (int)Registers.X2;
+
+            AThread RequestingThread = Ns.Os.Handles.GetData<HThread>(RequestingThreadHandle).Thread;
+
+            Mutex M = new Mutex(Memory, MutexAddress);
+
+            M = Ns.Os.Mutexes.GetOrAdd(MutexAddress, M);
+
+            //FIXME
+            //M.WaitForLock(RequestingThread, RequestingThreadHandle);
+
+            Memory.WriteInt32(MutexAddress, 0);
+
+            Registers.X0 = (int)SvcResult.Success;
+        }
+
+        private static void SvcArbitrateUnlock(Switch Ns, ARegisters Registers, AMemory Memory)
+        {
+            long MutexAddress = (long)Registers.X0;
+
+            if (Ns.Os.Mutexes.TryGetValue(MutexAddress, out Mutex M))
+            {
+                M.Unlock();
+            }
+
+            Registers.X0 = (int)SvcResult.Success;
+        }
+
+        private static void SvcWaitProcessWideKeyAtomic(Switch Ns, ARegisters Registers, AMemory Memory)
+        {
+            long MutexAddress   = (long)Registers.X0;
+            long CondVarAddress = (long)Registers.X1;
+            int  ThreadHandle   =  (int)Registers.X2;
+            long Timeout        = (long)Registers.X3;
+
+            AThread Thread = Ns.Os.Handles.GetData<HThread>(ThreadHandle).Thread;
+
+            if (Ns.Os.Mutexes.TryGetValue(MutexAddress, out Mutex M))
+            {
+                M.GiveUpLock(ThreadHandle);
+            }
+
+            CondVar Signal = new CondVar(Memory, CondVarAddress, Timeout);
+
+            Signal = Ns.Os.CondVars.GetOrAdd(CondVarAddress, Signal);
+
+            Signal.WaitForSignal(ThreadHandle);
+
+            M = new Mutex(Memory, MutexAddress);
+
+            M = Ns.Os.Mutexes.GetOrAdd(MutexAddress, M);
+
+            //FIXME
+            //M.WaitForLock(Thread, ThreadHandle);
+
+            Memory.WriteInt32(MutexAddress, 0);
+
+            Registers.X0 = (int)SvcResult.Success;
+        }
+
+        private static void SvcSignalProcessWideKey(Switch Ns, ARegisters Registers, AMemory Memory)
+        {
+            long CondVarAddress = (long)Registers.X0;
+            int  Count          =  (int)Registers.X1;
+
+            if (Ns.Os.CondVars.TryGetValue(CondVarAddress, out CondVar Cv))
+            {
+                Cv.SetSignal(Count);
+            }
+
+            Registers.X0 = (int)SvcResult.Success;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Utilities/IdPool.cs b/Ryujinx/OsHle/Utilities/IdPool.cs
new file mode 100644
index 0000000000..836d6310eb
--- /dev/null
+++ b/Ryujinx/OsHle/Utilities/IdPool.cs
@@ -0,0 +1,53 @@
+using System.Collections.Generic;
+
+namespace Ryujinx.OsHle.Utilities
+{
+    class IdPool
+    {
+        private HashSet<int> Ids;
+
+        private int CurrId;
+        private int MinId;
+        private int MaxId;
+
+        public IdPool(int Min, int Max)
+        {
+            Ids = new HashSet<int>();
+
+            CurrId = Min;
+            MinId  = Min;
+            MaxId  = Max;
+        }
+
+        public IdPool() : this(1, int.MaxValue) { }
+
+        public int GenerateId()
+        {
+            lock (Ids)
+            {
+                for (int Cnt = MinId; Cnt < MaxId; Cnt++)
+                {
+                    if (Ids.Add(CurrId))
+                    {
+                        return CurrId;
+                    }
+
+                    if (CurrId++ == MaxId)
+                    {
+                        CurrId = MinId;
+                    }
+                }
+
+                return -1;
+            }
+        }
+
+        public bool DeleteId(int Id)
+        {
+            lock (Ids)
+            {
+                return Ids.Remove(Id);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Utilities/IdPoolWithObj.cs b/Ryujinx/OsHle/Utilities/IdPoolWithObj.cs
new file mode 100644
index 0000000000..621466a823
--- /dev/null
+++ b/Ryujinx/OsHle/Utilities/IdPoolWithObj.cs
@@ -0,0 +1,78 @@
+using System;
+using System.Collections;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+
+namespace Ryujinx.OsHle.Utilities
+{
+    class IdPoolWithObj : IEnumerable<KeyValuePair<int, object>>
+    {
+        private IdPool Ids;
+
+        private ConcurrentDictionary<int, object> Objs;
+
+        public IdPoolWithObj()
+        {
+            Ids = new IdPool();
+
+            Objs = new ConcurrentDictionary<int, object>();
+        }
+
+        public int GenerateId(object Data)
+        {
+            int Id = Ids.GenerateId();
+
+            if (Id == -1 || !Objs.TryAdd(Id, Data))
+            {
+                throw new InvalidOperationException();
+            }
+
+            return Id;
+        }
+
+        public bool ReplaceData(int Id, object Data)
+        {
+            if (Objs.ContainsKey(Id))
+            {
+                Objs[Id] = Data;
+
+                return true;
+            }
+
+            return false;
+        }
+
+        public T GetData<T>(int Id)
+        {
+            if (Objs.TryGetValue(Id, out object Data) && Data is T)
+            {
+                return (T)Data;
+            }
+
+            return default(T);
+        }
+
+        public void Delete(int Id)
+        {
+            if (Objs.TryRemove(Id, out object Obj))
+            {
+                if (Obj is IDisposable DisposableObj)
+                {
+                    DisposableObj.Dispose();
+                }
+
+                Ids.DeleteId(Id);
+            }
+        }
+
+        IEnumerator<KeyValuePair<int, object>> IEnumerable<KeyValuePair<int, object>>.GetEnumerator()
+        {
+            return Objs.GetEnumerator();
+        }
+
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return Objs.GetEnumerator();
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Utilities/MemReader.cs b/Ryujinx/OsHle/Utilities/MemReader.cs
new file mode 100644
index 0000000000..9868293a04
--- /dev/null
+++ b/Ryujinx/OsHle/Utilities/MemReader.cs
@@ -0,0 +1,44 @@
+using ChocolArm64.Memory;
+
+namespace Ryujinx.OsHle.Utilities
+{
+    class MemReader
+    {
+        private AMemory Memory;
+
+        public long Position { get; private set; }
+
+        public MemReader(AMemory Memory, long Position)
+        {
+            this.Memory   = Memory;
+            this.Position = Position;
+        }
+
+        public byte ReadByte()
+        {
+            byte Value = Memory.ReadByte(Position);
+
+            Position++;
+
+            return Value;
+        }
+
+        public int ReadInt32()
+        {
+            int Value = Memory.ReadInt32(Position);
+
+            Position += 4;
+
+            return Value;
+        }
+
+        public long ReadInt64()
+        {
+            long Value = Memory.ReadInt64(Position);
+
+            Position += 8;
+
+            return Value;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/OsHle/Utilities/MemWriter.cs b/Ryujinx/OsHle/Utilities/MemWriter.cs
new file mode 100644
index 0000000000..041b0a970a
--- /dev/null
+++ b/Ryujinx/OsHle/Utilities/MemWriter.cs
@@ -0,0 +1,38 @@
+using ChocolArm64.Memory;
+
+namespace Ryujinx.OsHle.Utilities
+{
+    class MemWriter
+    {
+        private AMemory Memory;
+
+        public long Position { get; private set; }
+
+        public MemWriter(AMemory Memory, long Position)
+        {
+            this.Memory   = Memory;
+            this.Position = Position;
+        }
+
+        public void WriteByte(byte Value)
+        {
+            Memory.WriteByte(Position, Value);
+
+            Position++;
+        }
+
+        public void WriteInt32(int Value)
+        {
+            Memory.WriteInt32(Position, Value);
+
+            Position += 4;
+        }
+
+        public void WriteInt64(long Value)
+        {
+            Memory.WriteInt64(Position, Value);
+
+            Position += 8;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Switch.cs b/Ryujinx/Switch.cs
new file mode 100644
index 0000000000..9e5ea7d06c
--- /dev/null
+++ b/Ryujinx/Switch.cs
@@ -0,0 +1,42 @@
+using ChocolArm64.Memory;
+using Gal;
+using Ryujinx.Gpu;
+using Ryujinx.OsHle;
+using System;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx
+{
+    public class Switch : IDisposable
+    {
+        public IntPtr Ram {get; private set; }
+
+        internal NsGpu     Gpu { get; private set; }
+        internal Horizon   Os  { get; private set; }
+        internal VirtualFs VFs { get; private set; }
+
+        public Switch(IGalRenderer Renderer)
+        {
+            Ram = Marshal.AllocHGlobal((IntPtr)AMemoryMgr.RamSize);
+
+            Gpu = new NsGpu(Renderer);
+            Os  = new Horizon(this);
+            VFs = new VirtualFs();
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+        }
+
+        protected virtual void Dispose(bool disposing)
+        {
+            if (disposing)
+            {
+                VFs.Dispose();
+            }
+
+            Marshal.FreeHGlobal(Ram);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/VirtualFs.cs b/Ryujinx/VirtualFs.cs
new file mode 100644
index 0000000000..03317f4a11
--- /dev/null
+++ b/Ryujinx/VirtualFs.cs
@@ -0,0 +1,65 @@
+using System;
+using System.IO;
+
+namespace Ryujinx
+{
+    class VirtualFs : IDisposable
+    {
+        private const string BasePath  = "Fs";
+        private const string SavesPath = "Saves";
+
+        public Stream RomFs { get; private set; }
+
+        public void LoadRomFs(string FileName)
+        {
+            RomFs = new FileStream(FileName, FileMode.Open, FileAccess.Read);
+        }
+
+        internal string GetFullPath(string BasePath, string FileName)
+        {
+            if (FileName.StartsWith('/'))
+            {
+                FileName = FileName.Substring(1);
+            }
+
+            string FullPath = Path.GetFullPath(Path.Combine(BasePath, FileName));
+
+            if (!FullPath.StartsWith(GetBasePath()))
+            {
+                return null;
+            }
+
+            return FullPath;
+        }
+
+        internal string GetGameSavesPath()
+        {
+            string SavesDir = Path.Combine(GetBasePath(), SavesPath);
+
+            if (!Directory.Exists(SavesDir))
+            {
+                Directory.CreateDirectory(SavesDir);
+            }
+
+            return SavesDir;
+        }
+
+        internal string GetBasePath()
+        {
+            return Path.Combine(Directory.GetCurrentDirectory(), BasePath);
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+        }
+
+        protected virtual void Dispose(bool disposing)
+        {
+            if (disposing && RomFs != null)
+            {
+                RomFs.Dispose();
+            }
+        }
+    }
+}
\ No newline at end of file