diff --git a/ChocolArm64/CpuThread.cs b/ChocolArm64/CpuThread.cs
index 11f41236..dac376a1 100644
--- a/ChocolArm64/CpuThread.cs
+++ b/ChocolArm64/CpuThread.cs
@@ -18,7 +18,7 @@ namespace ChocolArm64
 
         private int _isExecuting;
 
-        public CpuThread(Translator translator, MemoryManager memory, long entryPoint)
+        public CpuThread(Translator translator, MemoryManager memory, long entrypoint)
         {
             _translator = translator;
             Memory      = memory;
@@ -31,7 +31,7 @@ namespace ChocolArm64
 
             Work = new Thread(delegate()
             {
-                translator.ExecuteSubroutine(this, entryPoint);
+                translator.ExecuteSubroutine(this, entrypoint);
 
                 memory.RemoveMonitor(ThreadState.Core);
 
diff --git a/ChocolArm64/Exceptions/VmmOutOfMemoryException.cs b/ChocolArm64/Exceptions/VmmOutOfMemoryException.cs
deleted file mode 100644
index d6ddf752..00000000
--- a/ChocolArm64/Exceptions/VmmOutOfMemoryException.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using System;
-
-namespace ChocolArm64.Exceptions
-{
-    public class VmmAccessException : Exception
-    {
-        private const string ExMsg = "Memory region at 0x{0} with size 0x{1} is not contiguous!";
-
-        public VmmAccessException() { }
-
-        public VmmAccessException(long position, long size) : base(string.Format(ExMsg, position, size)) { }
-    }
-}
\ No newline at end of file
diff --git a/ChocolArm64/Memory/MemoryHelper.cs b/ChocolArm64/Memory/MemoryHelper.cs
index 2e721afa..1c436dd8 100644
--- a/ChocolArm64/Memory/MemoryHelper.cs
+++ b/ChocolArm64/Memory/MemoryHelper.cs
@@ -26,22 +26,26 @@ namespace ChocolArm64.Memory
         {
             long size = Marshal.SizeOf<T>();
 
-            memory.EnsureRangeIsValid(position, size);
+            byte[] data = memory.ReadBytes(position, size);
 
-            IntPtr ptr = (IntPtr)memory.Translate(position);
-
-            return Marshal.PtrToStructure<T>(ptr);
+            fixed (byte* ptr = data)
+            {
+                return Marshal.PtrToStructure<T>((IntPtr)ptr);
+            }
         }
 
         public unsafe static void Write<T>(MemoryManager memory, long position, T value) where T : struct
         {
             long size = Marshal.SizeOf<T>();
 
-            memory.EnsureRangeIsValid(position, size);
+            byte[] data = new byte[size];
 
-            IntPtr ptr = (IntPtr)memory.TranslateWrite(position);
+            fixed (byte* ptr = data)
+            {
+                Marshal.StructureToPtr<T>(value, (IntPtr)ptr, false);
+            }
 
-            Marshal.StructureToPtr<T>(value, ptr, false);
+            memory.WriteBytes(position, data);
         }
 
         public static string ReadAsciiString(MemoryManager memory, long position, long maxSize = -1)
diff --git a/ChocolArm64/Memory/MemoryManager.cs b/ChocolArm64/Memory/MemoryManager.cs
index ef3fb006..68d9100b 100644
--- a/ChocolArm64/Memory/MemoryManager.cs
+++ b/ChocolArm64/Memory/MemoryManager.cs
@@ -1,5 +1,6 @@
 using ChocolArm64.Events;
 using ChocolArm64.Exceptions;
+using ChocolArm64.Instructions;
 using ChocolArm64.State;
 using System;
 using System.Collections.Concurrent;
@@ -197,17 +198,41 @@ namespace ChocolArm64.Memory
 
         public ushort ReadUInt16(long position)
         {
-            return *((ushort*)Translate(position));
+            if ((position & 1) == 0)
+            {
+                return *((ushort*)Translate(position));
+            }
+            else
+            {
+                return (ushort)(ReadByte(position + 0) << 0 |
+                                ReadByte(position + 1) << 8);
+            }
         }
 
         public uint ReadUInt32(long position)
         {
-            return *((uint*)Translate(position));
+            if ((position & 3) == 0)
+            {
+                return *((uint*)Translate(position));
+            }
+            else
+            {
+                return (uint)(ReadUInt16(position + 0) << 0 |
+                              ReadUInt16(position + 2) << 16);
+            }
         }
 
         public ulong ReadUInt64(long position)
         {
-            return *((ulong*)Translate(position));
+            if ((position & 7) == 0)
+            {
+                return *((ulong*)Translate(position));
+            }
+            else
+            {
+                return (ulong)ReadUInt32(position + 0) << 0 |
+                       (ulong)ReadUInt32(position + 4) << 32;
+            }
         }
 
         public Vector128<float> ReadVector8(long position)
@@ -218,74 +243,117 @@ namespace ChocolArm64.Memory
             }
             else
             {
-                throw new PlatformNotSupportedException();
+                Vector128<float> value = VectorHelper.VectorSingleZero();
+
+                value = VectorHelper.VectorInsertInt(ReadByte(position), value, 0, 0);
+
+                return value;
             }
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public Vector128<float> ReadVector16(long position)
         {
-            if (Sse2.IsSupported)
+            if (Sse2.IsSupported && (position & 1) == 0)
             {
                 return Sse.StaticCast<ushort, float>(Sse2.Insert(Sse2.SetZeroVector128<ushort>(), ReadUInt16(position), 0));
             }
             else
             {
-                throw new PlatformNotSupportedException();
+                Vector128<float> value = VectorHelper.VectorSingleZero();
+
+                value = VectorHelper.VectorInsertInt(ReadUInt16(position), value, 0, 1);
+
+                return value;
             }
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public Vector128<float> ReadVector32(long position)
         {
-            if (Sse.IsSupported)
+            if (Sse.IsSupported && (position & 3) == 0)
             {
                 return Sse.LoadScalarVector128((float*)Translate(position));
             }
             else
             {
-                throw new PlatformNotSupportedException();
+                Vector128<float> value = VectorHelper.VectorSingleZero();
+
+                value = VectorHelper.VectorInsertInt(ReadUInt32(position), value, 0, 2);
+
+                return value;
             }
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public Vector128<float> ReadVector64(long position)
         {
-            if (Sse2.IsSupported)
+            if (Sse2.IsSupported && (position & 7) == 0)
             {
                 return Sse.StaticCast<double, float>(Sse2.LoadScalarVector128((double*)Translate(position)));
             }
             else
             {
-                throw new PlatformNotSupportedException();
+                Vector128<float> value = VectorHelper.VectorSingleZero();
+
+                value = VectorHelper.VectorInsertInt(ReadUInt64(position), value, 0, 3);
+
+                return value;
             }
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public Vector128<float> ReadVector128(long position)
         {
-            if (Sse.IsSupported)
+            if (Sse.IsSupported && (position & 15) == 0)
             {
                 return Sse.LoadVector128((float*)Translate(position));
             }
             else
             {
-                throw new PlatformNotSupportedException();
+                Vector128<float> value = VectorHelper.VectorSingleZero();
+
+                value = VectorHelper.VectorInsertInt(ReadUInt64(position + 0), value, 0, 3);
+                value = VectorHelper.VectorInsertInt(ReadUInt64(position + 8), value, 1, 3);
+
+                return value;
             }
         }
 
         public byte[] ReadBytes(long position, long size)
         {
-            if ((uint)size > int.MaxValue)
+            long endAddr = position + size;
+
+            if ((ulong)size > int.MaxValue)
             {
                 throw new ArgumentOutOfRangeException(nameof(size));
             }
 
-            EnsureRangeIsValid(position, size);
+            if ((ulong)endAddr < (ulong)position)
+            {
+                throw new ArgumentOutOfRangeException(nameof(position));
+            }
 
             byte[] data = new byte[size];
 
-            Marshal.Copy((IntPtr)Translate(position), data, 0, (int)size);
+            int offset = 0;
+
+            while ((ulong)position < (ulong)endAddr)
+            {
+                long pageLimit = (position + PageSize) & ~(long)PageMask;
+
+                if ((ulong)pageLimit > (ulong)endAddr)
+                {
+                    pageLimit = endAddr;
+                }
+
+                int copySize = (int)(pageLimit - position);
+
+                Marshal.Copy((IntPtr)Translate(position), data, offset, copySize);
+
+                position += copySize;
+                offset   += copySize;
+            }
 
             return data;
         }
@@ -293,9 +361,36 @@ namespace ChocolArm64.Memory
         public void ReadBytes(long position, byte[] data, int startIndex, int size)
         {
             //Note: This will be moved later.
-            EnsureRangeIsValid(position, (uint)size);
+            long endAddr = position + size;
 
-            Marshal.Copy((IntPtr)Translate(position), data, startIndex, size);
+            if ((ulong)size > int.MaxValue)
+            {
+                throw new ArgumentOutOfRangeException(nameof(size));
+            }
+
+            if ((ulong)endAddr < (ulong)position)
+            {
+                throw new ArgumentOutOfRangeException(nameof(position));
+            }
+
+            int offset = startIndex;
+
+            while ((ulong)position < (ulong)endAddr)
+            {
+                long pageLimit = (position + PageSize) & ~(long)PageMask;
+
+                if ((ulong)pageLimit > (ulong)endAddr)
+                {
+                    pageLimit = endAddr;
+                }
+
+                int copySize = (int)(pageLimit - position);
+
+                Marshal.Copy((IntPtr)Translate(position), data, offset, copySize);
+
+                position += copySize;
+                offset   += copySize;
+            }
         }
 
         public void WriteSByte(long position, sbyte value)
@@ -325,17 +420,41 @@ namespace ChocolArm64.Memory
 
         public void WriteUInt16(long position, ushort value)
         {
-            *((ushort*)TranslateWrite(position)) = value;
+            if ((position & 1) == 0)
+            {
+                *((ushort*)TranslateWrite(position)) = value;
+            }
+            else
+            {
+                WriteByte(position + 0, (byte)(value >> 0));
+                WriteByte(position + 1, (byte)(value >> 8));
+            }
         }
 
         public void WriteUInt32(long position, uint value)
         {
-            *((uint*)TranslateWrite(position)) = value;
+            if ((position & 3) == 0)
+            {
+                *((uint*)TranslateWrite(position)) = value;
+            }
+            else
+            {
+                WriteUInt16(position + 0, (ushort)(value >> 0));
+                WriteUInt16(position + 2, (ushort)(value >> 16));
+            }
         }
 
         public void WriteUInt64(long position, ulong value)
         {
-            *((ulong*)TranslateWrite(position)) = value;
+            if ((position & 7) == 0)
+            {
+                *((ulong*)TranslateWrite(position)) = value;
+            }
+            else
+            {
+                WriteUInt32(position + 0, (uint)(value >> 0));
+                WriteUInt32(position + 4, (uint)(value >> 32));
+            }
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -351,7 +470,7 @@ namespace ChocolArm64.Memory
             }
             else
             {
-                throw new PlatformNotSupportedException();
+                WriteByte(position, (byte)VectorHelper.VectorExtractIntZx(value, 0, 0));
             }
         }
 
@@ -364,46 +483,47 @@ namespace ChocolArm64.Memory
             }
             else
             {
-                throw new PlatformNotSupportedException();
+                WriteUInt16(position, (ushort)VectorHelper.VectorExtractIntZx(value, 0, 1));
             }
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public void WriteVector32(long position, Vector128<float> value)
         {
-            if (Sse.IsSupported)
+            if (Sse.IsSupported && (position & 3) == 0)
             {
                 Sse.StoreScalar((float*)TranslateWrite(position), value);
             }
             else
             {
-                throw new PlatformNotSupportedException();
+                WriteUInt32(position, (uint)VectorHelper.VectorExtractIntZx(value, 0, 2));
             }
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public void WriteVector64(long position, Vector128<float> value)
         {
-            if (Sse2.IsSupported)
+            if (Sse2.IsSupported && (position & 7) == 0)
             {
                 Sse2.StoreScalar((double*)TranslateWrite(position), Sse.StaticCast<float, double>(value));
             }
             else
             {
-                throw new PlatformNotSupportedException();
+                WriteUInt64(position, VectorHelper.VectorExtractIntZx(value, 0, 3));
             }
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public void WriteVector128(long position, Vector128<float> value)
         {
-            if (Sse.IsSupported)
+            if (Sse.IsSupported && (position & 15) == 0)
             {
                 Sse.Store((float*)TranslateWrite(position), value);
             }
             else
             {
-                throw new PlatformNotSupportedException();
+                WriteUInt64(position + 0, VectorHelper.VectorExtractIntZx(value, 0, 3));
+                WriteUInt64(position + 8, VectorHelper.VectorExtractIntZx(value, 1, 3));
             }
         }
 
@@ -439,22 +559,48 @@ namespace ChocolArm64.Memory
         public void WriteBytes(long position, byte[] data, int startIndex, int size)
         {
             //Note: This will be moved later.
-            //Using Translate instead of TranslateWrite is on purpose.
-            EnsureRangeIsValid(position, (uint)size);
+            long endAddr = position + size;
 
-            Marshal.Copy(data, startIndex, (IntPtr)Translate(position), size);
+            if ((ulong)endAddr < (ulong)position)
+            {
+                throw new ArgumentOutOfRangeException(nameof(position));
+            }
+
+            int offset = startIndex;
+
+            while ((ulong)position < (ulong)endAddr)
+            {
+                long pageLimit = (position + PageSize) & ~(long)PageMask;
+
+                if ((ulong)pageLimit > (ulong)endAddr)
+                {
+                    pageLimit = endAddr;
+                }
+
+                int copySize = (int)(pageLimit - position);
+
+                Marshal.Copy(data, offset, (IntPtr)TranslateWrite(position), copySize);
+
+                position += copySize;
+                offset   += copySize;
+            }
         }
 
         public void CopyBytes(long src, long dst, long size)
         {
             //Note: This will be moved later.
-            EnsureRangeIsValid(src, size);
-            EnsureRangeIsValid(dst, size);
+            if (IsContiguous(src, size) &&
+                IsContiguous(dst, size))
+            {
+                byte* srcPtr = Translate(src);
+                byte* dstPtr = TranslateWrite(dst);
 
-            byte* srcPtr = Translate(src);
-            byte* dstPtr = TranslateWrite(dst);
-
-            Buffer.MemoryCopy(srcPtr, dstPtr, size, size);
+                Buffer.MemoryCopy(srcPtr, dstPtr, size, size);
+            }
+            else
+            {
+                WriteBytes(dst, ReadBytes(src, size));
+            }
         }
 
         public void Map(long va, long pa, long size)
@@ -703,14 +849,21 @@ Unmapped:
             }
         }
 
-        public IntPtr GetHostAddress(long position, long size)
+        public bool TryGetHostAddress(long position, long size, out IntPtr ptr)
         {
-            EnsureRangeIsValid(position, size);
+            if (IsContiguous(position, size))
+            {
+                ptr = (IntPtr)Translate(position);
 
-            return (IntPtr)Translate(position);
+                return true;
+            }
+
+            ptr = IntPtr.Zero;
+
+            return false;
         }
 
-        internal void EnsureRangeIsValid(long position, long size)
+        private bool IsContiguous(long position, long size)
         {
             long endPos = position + size;
 
@@ -724,12 +877,14 @@ Unmapped:
 
                 if (pa != expectedPa)
                 {
-                    throw new VmmAccessException(position, size);
+                    return false;
                 }
 
                 position   += PageSize;
                 expectedPa += PageSize;
             }
+
+            return true;
         }
 
         public bool IsValidPosition(long position)
diff --git a/Ryujinx.Common/BitUtils.cs b/Ryujinx.Common/BitUtils.cs
new file mode 100644
index 00000000..5c858029
--- /dev/null
+++ b/Ryujinx.Common/BitUtils.cs
@@ -0,0 +1,104 @@
+namespace Ryujinx.Common
+{
+    public static class BitUtils
+    {
+        public static int AlignUp(int Value, int Size)
+        {
+            return (Value + (Size - 1)) & -Size;
+        }
+
+        public static ulong AlignUp(ulong Value, int Size)
+        {
+            return (ulong)AlignUp((long)Value, Size);
+        }
+
+        public static long AlignUp(long Value, int Size)
+        {
+            return (Value + (Size - 1)) & -(long)Size;
+        }
+
+        public static int AlignDown(int Value, int Size)
+        {
+            return Value & -Size;
+        }
+
+        public static ulong AlignDown(ulong Value, int Size)
+        {
+            return (ulong)AlignDown((long)Value, Size);
+        }
+
+        public static long AlignDown(long Value, int Size)
+        {
+            return Value & -(long)Size;
+        }
+
+        public static ulong DivRoundUp(ulong Value, uint Dividend)
+        {
+            return (Value + Dividend - 1) / Dividend;
+        }
+
+        public static long DivRoundUp(long Value, int Dividend)
+        {
+            return (Value + Dividend - 1) / Dividend;
+        }
+
+        public static bool IsPowerOfTwo32(int Value)
+        {
+            return Value != 0 && (Value & (Value - 1)) == 0;
+        }
+
+        public static bool IsPowerOfTwo64(long Value)
+        {
+            return Value != 0 && (Value & (Value - 1)) == 0;
+        }
+
+        public static int CountLeadingZeros32(int Value)
+        {
+            return (int)CountLeadingZeros((ulong)Value, 32);
+        }
+
+        public static int CountLeadingZeros64(long Value)
+        {
+            return (int)CountLeadingZeros((ulong)Value, 64);
+        }
+
+        private static readonly byte[] ClzNibbleTbl = { 4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+        private static ulong CountLeadingZeros(ulong Value, int Size) // Size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.).
+        {
+            if (Value == 0ul)
+            {
+                return (ulong)Size;
+            }
+
+            int NibbleIdx = Size;
+            int PreCount, Count = 0;
+
+            do
+            {
+                NibbleIdx -= 4;
+                PreCount = ClzNibbleTbl[(Value >> NibbleIdx) & 0b1111];
+                Count += PreCount;
+            }
+            while (PreCount == 4);
+
+            return (ulong)Count;
+        }
+
+        public static long ReverseBits64(long Value)
+        {
+            return (long)ReverseBits64((ulong)Value);
+        }
+
+        private 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);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/GalVertexAttrib.cs b/Ryujinx.Graphics/Gal/GalVertexAttrib.cs
index fa9a391f..31b648df 100644
--- a/Ryujinx.Graphics/Gal/GalVertexAttrib.cs
+++ b/Ryujinx.Graphics/Gal/GalVertexAttrib.cs
@@ -1,5 +1,3 @@
-using System;
-
 namespace Ryujinx.Graphics.Gal
 {
     public struct GalVertexAttrib
@@ -7,7 +5,7 @@ namespace Ryujinx.Graphics.Gal
         public int    Index   { get; private set; }
         public bool   IsConst { get; private set; }
         public int    Offset  { get; private set; }
-        public IntPtr Pointer { get; private set; }
+        public byte[] Data    { get; private set; }
 
         public GalVertexAttribSize Size { get; private set; }
         public GalVertexAttribType Type { get; private set; }
@@ -18,14 +16,14 @@ namespace Ryujinx.Graphics.Gal
             int                 Index,
             bool                IsConst,
             int                 Offset,
-            IntPtr              Pointer,
+            byte[]              Data,
             GalVertexAttribSize Size,
             GalVertexAttribType Type,
             bool                IsBgra)
         {
             this.Index   = Index;
             this.IsConst = IsConst;
-            this.Pointer = Pointer;
+            this.Data    = Data;
             this.Offset  = Offset;
             this.Size    = Size;
             this.Type    = Type;
diff --git a/Ryujinx.Graphics/Gal/IGalConstBuffer.cs b/Ryujinx.Graphics/Gal/IGalConstBuffer.cs
index 37545b2a..0cdcc237 100644
--- a/Ryujinx.Graphics/Gal/IGalConstBuffer.cs
+++ b/Ryujinx.Graphics/Gal/IGalConstBuffer.cs
@@ -12,5 +12,6 @@ namespace Ryujinx.Graphics.Gal
         bool IsCached(long Key, long Size);
 
         void SetData(long Key, long Size, IntPtr HostAddress);
+        void SetData(long Key, byte[] Data);
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/IGalRasterizer.cs b/Ryujinx.Graphics/Gal/IGalRasterizer.cs
index 052e3f35..04f7aae5 100644
--- a/Ryujinx.Graphics/Gal/IGalRasterizer.cs
+++ b/Ryujinx.Graphics/Gal/IGalRasterizer.cs
@@ -22,6 +22,7 @@ namespace Ryujinx.Graphics.Gal
         bool IsIboCached(long Key, long DataSize);
 
         void CreateVbo(long Key, int DataSize, IntPtr HostAddress);
+        void CreateVbo(long Key, byte[] Data);
 
         void CreateIbo(long Key, int DataSize, IntPtr HostAddress);
         void CreateIbo(long Key, int DataSize, byte[] Buffer);
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLConstBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLConstBuffer.cs
index e04190e0..a12681c7 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLConstBuffer.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLConstBuffer.cs
@@ -44,6 +44,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             }
         }
 
+        public void SetData(long Key, byte[] Data)
+        {
+            if (Cache.TryGetValue(Key, out OGLStreamBuffer Buffer))
+            {
+                Buffer.SetData(Data);
+            }
+        }
+
         public bool TryGetUbo(long Key, out int UboHandle)
         {
             if (Cache.TryGetValue(Key, out OGLStreamBuffer Buffer))
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs
index e81cf8a3..ac12314c 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs
@@ -600,122 +600,125 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                 ThrowUnsupportedAttrib(Attrib);
             }
 
-            if (Attrib.Type == GalVertexAttribType.Unorm)
+            fixed (byte* Ptr = Attrib.Data)
             {
-                switch (Attrib.Size)
+                if (Attrib.Type == GalVertexAttribType.Unorm)
                 {
-                    case GalVertexAttribSize._8:
-                    case GalVertexAttribSize._8_8:
-                    case GalVertexAttribSize._8_8_8:
-                    case GalVertexAttribSize._8_8_8_8:
-                        GL.VertexAttrib4N((uint)Attrib.Index, (byte*)Attrib.Pointer);
-                        break;
+                    switch (Attrib.Size)
+                    {
+                        case GalVertexAttribSize._8:
+                        case GalVertexAttribSize._8_8:
+                        case GalVertexAttribSize._8_8_8:
+                        case GalVertexAttribSize._8_8_8_8:
+                            GL.VertexAttrib4N((uint)Attrib.Index, Ptr);
+                            break;
 
-                    case GalVertexAttribSize._16:
-                    case GalVertexAttribSize._16_16:
-                    case GalVertexAttribSize._16_16_16:
-                    case GalVertexAttribSize._16_16_16_16:
-                        GL.VertexAttrib4N((uint)Attrib.Index, (ushort*)Attrib.Pointer);
-                        break;
+                        case GalVertexAttribSize._16:
+                        case GalVertexAttribSize._16_16:
+                        case GalVertexAttribSize._16_16_16:
+                        case GalVertexAttribSize._16_16_16_16:
+                            GL.VertexAttrib4N((uint)Attrib.Index, (ushort*)Ptr);
+                            break;
 
-                    case GalVertexAttribSize._32:
-                    case GalVertexAttribSize._32_32:
-                    case GalVertexAttribSize._32_32_32:
-                    case GalVertexAttribSize._32_32_32_32:
-                        GL.VertexAttrib4N((uint)Attrib.Index, (uint*)Attrib.Pointer);
-                        break;
+                        case GalVertexAttribSize._32:
+                        case GalVertexAttribSize._32_32:
+                        case GalVertexAttribSize._32_32_32:
+                        case GalVertexAttribSize._32_32_32_32:
+                            GL.VertexAttrib4N((uint)Attrib.Index, (uint*)Ptr);
+                            break;
+                    }
                 }
-            }
-            else if (Attrib.Type == GalVertexAttribType.Snorm)
-            {
-                switch (Attrib.Size)
+                else if (Attrib.Type == GalVertexAttribType.Snorm)
                 {
-                    case GalVertexAttribSize._8:
-                    case GalVertexAttribSize._8_8:
-                    case GalVertexAttribSize._8_8_8:
-                    case GalVertexAttribSize._8_8_8_8:
-                        GL.VertexAttrib4N((uint)Attrib.Index, (sbyte*)Attrib.Pointer);
-                        break;
+                    switch (Attrib.Size)
+                    {
+                        case GalVertexAttribSize._8:
+                        case GalVertexAttribSize._8_8:
+                        case GalVertexAttribSize._8_8_8:
+                        case GalVertexAttribSize._8_8_8_8:
+                            GL.VertexAttrib4N((uint)Attrib.Index, (sbyte*)Ptr);
+                            break;
 
-                    case GalVertexAttribSize._16:
-                    case GalVertexAttribSize._16_16:
-                    case GalVertexAttribSize._16_16_16:
-                    case GalVertexAttribSize._16_16_16_16:
-                        GL.VertexAttrib4N((uint)Attrib.Index, (short*)Attrib.Pointer);
-                        break;
+                        case GalVertexAttribSize._16:
+                        case GalVertexAttribSize._16_16:
+                        case GalVertexAttribSize._16_16_16:
+                        case GalVertexAttribSize._16_16_16_16:
+                            GL.VertexAttrib4N((uint)Attrib.Index, (short*)Ptr);
+                            break;
 
-                    case GalVertexAttribSize._32:
-                    case GalVertexAttribSize._32_32:
-                    case GalVertexAttribSize._32_32_32:
-                    case GalVertexAttribSize._32_32_32_32:
-                        GL.VertexAttrib4N((uint)Attrib.Index, (int*)Attrib.Pointer);
-                        break;
+                        case GalVertexAttribSize._32:
+                        case GalVertexAttribSize._32_32:
+                        case GalVertexAttribSize._32_32_32:
+                        case GalVertexAttribSize._32_32_32_32:
+                            GL.VertexAttrib4N((uint)Attrib.Index, (int*)Ptr);
+                            break;
+                    }
                 }
-            }
-            else if (Attrib.Type == GalVertexAttribType.Uint)
-            {
-                switch (Attrib.Size)
+                else if (Attrib.Type == GalVertexAttribType.Uint)
                 {
-                    case GalVertexAttribSize._8:
-                    case GalVertexAttribSize._8_8:
-                    case GalVertexAttribSize._8_8_8:
-                    case GalVertexAttribSize._8_8_8_8:
-                        GL.VertexAttribI4((uint)Attrib.Index, (byte*)Attrib.Pointer);
-                        break;
+                    switch (Attrib.Size)
+                    {
+                        case GalVertexAttribSize._8:
+                        case GalVertexAttribSize._8_8:
+                        case GalVertexAttribSize._8_8_8:
+                        case GalVertexAttribSize._8_8_8_8:
+                            GL.VertexAttribI4((uint)Attrib.Index, Ptr);
+                            break;
 
-                    case GalVertexAttribSize._16:
-                    case GalVertexAttribSize._16_16:
-                    case GalVertexAttribSize._16_16_16:
-                    case GalVertexAttribSize._16_16_16_16:
-                        GL.VertexAttribI4((uint)Attrib.Index, (ushort*)Attrib.Pointer);
-                        break;
+                        case GalVertexAttribSize._16:
+                        case GalVertexAttribSize._16_16:
+                        case GalVertexAttribSize._16_16_16:
+                        case GalVertexAttribSize._16_16_16_16:
+                            GL.VertexAttribI4((uint)Attrib.Index, (ushort*)Ptr);
+                            break;
 
-                    case GalVertexAttribSize._32:
-                    case GalVertexAttribSize._32_32:
-                    case GalVertexAttribSize._32_32_32:
-                    case GalVertexAttribSize._32_32_32_32:
-                        GL.VertexAttribI4((uint)Attrib.Index, (uint*)Attrib.Pointer);
-                        break;
+                        case GalVertexAttribSize._32:
+                        case GalVertexAttribSize._32_32:
+                        case GalVertexAttribSize._32_32_32:
+                        case GalVertexAttribSize._32_32_32_32:
+                            GL.VertexAttribI4((uint)Attrib.Index, (uint*)Ptr);
+                            break;
+                    }
                 }
-            }
-            else if (Attrib.Type == GalVertexAttribType.Sint)
-            {
-                switch (Attrib.Size)
+                else if (Attrib.Type == GalVertexAttribType.Sint)
                 {
-                    case GalVertexAttribSize._8:
-                    case GalVertexAttribSize._8_8:
-                    case GalVertexAttribSize._8_8_8:
-                    case GalVertexAttribSize._8_8_8_8:
-                        GL.VertexAttribI4((uint)Attrib.Index, (sbyte*)Attrib.Pointer);
-                        break;
+                    switch (Attrib.Size)
+                    {
+                        case GalVertexAttribSize._8:
+                        case GalVertexAttribSize._8_8:
+                        case GalVertexAttribSize._8_8_8:
+                        case GalVertexAttribSize._8_8_8_8:
+                            GL.VertexAttribI4((uint)Attrib.Index, (sbyte*)Ptr);
+                            break;
 
-                    case GalVertexAttribSize._16:
-                    case GalVertexAttribSize._16_16:
-                    case GalVertexAttribSize._16_16_16:
-                    case GalVertexAttribSize._16_16_16_16:
-                        GL.VertexAttribI4((uint)Attrib.Index, (short*)Attrib.Pointer);
-                        break;
+                        case GalVertexAttribSize._16:
+                        case GalVertexAttribSize._16_16:
+                        case GalVertexAttribSize._16_16_16:
+                        case GalVertexAttribSize._16_16_16_16:
+                            GL.VertexAttribI4((uint)Attrib.Index, (short*)Ptr);
+                            break;
 
-                    case GalVertexAttribSize._32:
-                    case GalVertexAttribSize._32_32:
-                    case GalVertexAttribSize._32_32_32:
-                    case GalVertexAttribSize._32_32_32_32:
-                        GL.VertexAttribI4((uint)Attrib.Index, (int*)Attrib.Pointer);
-                        break;
+                        case GalVertexAttribSize._32:
+                        case GalVertexAttribSize._32_32:
+                        case GalVertexAttribSize._32_32_32:
+                        case GalVertexAttribSize._32_32_32_32:
+                            GL.VertexAttribI4((uint)Attrib.Index, (int*)Ptr);
+                            break;
+                    }
                 }
-            }
-            else if (Attrib.Type == GalVertexAttribType.Float)
-            {
-                switch (Attrib.Size)
+                else if (Attrib.Type == GalVertexAttribType.Float)
                 {
-                    case GalVertexAttribSize._32:
-                    case GalVertexAttribSize._32_32:
-                    case GalVertexAttribSize._32_32_32:
-                    case GalVertexAttribSize._32_32_32_32:
-                        GL.VertexAttrib4(Attrib.Index, (float*)Attrib.Pointer);
-                        break;
+                    switch (Attrib.Size)
+                    {
+                        case GalVertexAttribSize._32:
+                        case GalVertexAttribSize._32_32:
+                        case GalVertexAttribSize._32_32_32:
+                        case GalVertexAttribSize._32_32_32_32:
+                            GL.VertexAttrib4(Attrib.Index, (float*)Ptr);
+                            break;
 
-                    default: ThrowUnsupportedAttrib(Attrib); break;
+                        default: ThrowUnsupportedAttrib(Attrib); break;
+                    }
                 }
             }
         }
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs
index cd6292f7..c4015d02 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs
@@ -92,7 +92,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
         {
             int Handle = GL.GenBuffer();
 
-            VboCache.AddOrUpdate(Key, Handle, (uint)DataSize);
+            VboCache.AddOrUpdate(Key, Handle, DataSize);
 
             IntPtr Length = new IntPtr(DataSize);
 
@@ -100,6 +100,18 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             GL.BufferData(BufferTarget.ArrayBuffer, Length, HostAddress, BufferUsageHint.StreamDraw);
         }
 
+        public void CreateVbo(long Key, byte[] Data)
+        {
+            int Handle = GL.GenBuffer();
+
+            VboCache.AddOrUpdate(Key, Handle, Data.Length);
+
+            IntPtr Length = new IntPtr(Data.Length);
+
+            GL.BindBuffer(BufferTarget.ArrayBuffer, Handle);
+            GL.BufferData(BufferTarget.ArrayBuffer, Length, Data, BufferUsageHint.StreamDraw);
+        }
+
         public void CreateIbo(long Key, int DataSize, IntPtr HostAddress)
         {
             int Handle = GL.GenBuffer();
@@ -116,7 +128,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
         {
             int Handle = GL.GenBuffer();
 
-            IboCache.AddOrUpdate(Key, Handle, (uint)DataSize);
+            IboCache.AddOrUpdate(Key, Handle, DataSize);
 
             IntPtr Length = new IntPtr(Buffer.Length);
 
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs
index 94639405..411d33aa 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs
@@ -30,6 +30,13 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             GL.BufferSubData(Target, IntPtr.Zero, (IntPtr)Size, HostAddress);
         }
 
+        public void SetData(byte[] Data)
+        {
+            GL.BindBuffer(Target, Handle);
+
+            GL.BufferSubData(Target, IntPtr.Zero, (IntPtr)Data.Length, Data);
+        }
+
         public void Dispose()
         {
             Dispose(true);
diff --git a/Ryujinx.Graphics/Memory/NvGpuVmm.cs b/Ryujinx.Graphics/Memory/NvGpuVmm.cs
index ec660bf0..ccb21950 100644
--- a/Ryujinx.Graphics/Memory/NvGpuVmm.cs
+++ b/Ryujinx.Graphics/Memory/NvGpuVmm.cs
@@ -243,9 +243,9 @@ namespace Ryujinx.Graphics.Memory
             return Cache.IsRegionModified(Memory, BufferType, PA, Size);
         }
 
-        public IntPtr GetHostAddress(long Position, long Size)
+        public bool TryGetHostAddress(long Position, long Size, out IntPtr Ptr)
         {
-            return Memory.GetHostAddress(GetPhysicalAddress(Position), Size);
+            return Memory.TryGetHostAddress(GetPhysicalAddress(Position), Size, out Ptr);
         }
 
         public byte ReadByte(long Position)
diff --git a/Ryujinx.Graphics/NvGpuEngine3d.cs b/Ryujinx.Graphics/NvGpuEngine3d.cs
index 4a0310fb..918409e2 100644
--- a/Ryujinx.Graphics/NvGpuEngine3d.cs
+++ b/Ryujinx.Graphics/NvGpuEngine3d.cs
@@ -615,9 +615,14 @@ namespace Ryujinx.Graphics
 
                     if (Gpu.ResourceManager.MemoryRegionModified(Vmm, Key, Cb.Size, NvGpuBufferType.ConstBuffer))
                     {
-                        IntPtr Source = Vmm.GetHostAddress(Cb.Position, Cb.Size);
-
-                        Gpu.Renderer.Buffer.SetData(Key, Cb.Size, Source);
+                        if (Vmm.TryGetHostAddress(Cb.Position, Cb.Size, out IntPtr CbPtr))
+                        {
+                            Gpu.Renderer.Buffer.SetData(Key, Cb.Size, CbPtr);
+                        }
+                        else
+                        {
+                            Gpu.Renderer.Buffer.SetData(Key, Vmm.ReadBytes(Cb.Position, Cb.Size));
+                        }
                     }
 
                     State.ConstBufferKeys[Stage][DeclInfo.Cbuf] = Key;
@@ -660,9 +665,14 @@ namespace Ryujinx.Graphics
                 {
                     if (!UsesLegacyQuads)
                     {
-                        IntPtr DataAddress = Vmm.GetHostAddress(IbPosition, IbSize);
-
-                        Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, DataAddress);
+                        if (Vmm.TryGetHostAddress(IbPosition, IbSize, out IntPtr IbPtr))
+                        {
+                            Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, IbPtr);
+                        }
+                        else
+                        {
+                            Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, Vmm.ReadBytes(IbPosition, IbSize));
+                        }
                     }
                     else
                     {
@@ -711,22 +721,22 @@ namespace Ryujinx.Graphics
                     Attribs[ArrayIndex] = new List<GalVertexAttrib>();
                 }
 
-                long VertexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + ArrayIndex * 4);
+                long VbPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + ArrayIndex * 4);
+
+                bool IsConst = ((Packed >> 6) & 1) != 0;
 
                 int Offset = (Packed >> 7) & 0x3fff;
 
+                GalVertexAttribSize Size = (GalVertexAttribSize)((Packed >> 21) & 0x3f);
+                GalVertexAttribType Type = (GalVertexAttribType)((Packed >> 27) & 0x7);
+
+                bool IsRgba = ((Packed >> 31) & 1) != 0;
+
                 //Note: 16 is the maximum size of an attribute,
                 //having a component size of 32-bits with 4 elements (a vec4).
-                IntPtr Pointer = Vmm.GetHostAddress(VertexPosition + Offset, 16);
+                byte[] Data = Vmm.ReadBytes(VbPosition + Offset, 16);
 
-                Attribs[ArrayIndex].Add(new GalVertexAttrib(
-                                           Attr,
-                                         ((Packed >>  6) & 0x1) != 0,
-                                           Offset,
-                                           Pointer,
-                    (GalVertexAttribSize)((Packed >> 21) & 0x3f),
-                    (GalVertexAttribType)((Packed >> 27) & 0x7),
-                                         ((Packed >> 31) & 0x1) != 0));
+                Attribs[ArrayIndex].Add(new GalVertexAttrib(Attr, IsConst, Offset, Data, Size, Type, IsRgba));
             }
 
             State.VertexBindings = new GalVertexBinding[32];
@@ -747,8 +757,8 @@ namespace Ryujinx.Graphics
                     continue;
                 }
 
-                long VertexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4);
-                long VertexEndPos   = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + Index * 2);
+                long VbPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4);
+                long VbEndPos   = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + Index * 2);
 
                 int VertexDivisor = ReadRegister(NvGpuEngine3dReg.VertexArrayNDivisor + Index * 4);
 
@@ -758,26 +768,31 @@ namespace Ryujinx.Graphics
 
                 if (Instanced && VertexDivisor != 0)
                 {
-                    VertexPosition += Stride * (CurrentInstance / VertexDivisor);
+                    VbPosition += Stride * (CurrentInstance / VertexDivisor);
                 }
 
-                if (VertexPosition > VertexEndPos)
+                if (VbPosition > VbEndPos)
                 {
                     //Instance is invalid, ignore the draw call
                     continue;
                 }
 
-                long VboKey = Vmm.GetPhysicalAddress(VertexPosition);
+                long VboKey = Vmm.GetPhysicalAddress(VbPosition);
 
-                long VbSize = (VertexEndPos - VertexPosition) + 1;
+                long VbSize = (VbEndPos - VbPosition) + 1;
 
                 bool VboCached = Gpu.Renderer.Rasterizer.IsVboCached(VboKey, VbSize);
 
                 if (!VboCached || Gpu.ResourceManager.MemoryRegionModified(Vmm, VboKey, VbSize, NvGpuBufferType.Vertex))
                 {
-                    IntPtr DataAddress = Vmm.GetHostAddress(VertexPosition, VbSize);
-
-                    Gpu.Renderer.Rasterizer.CreateVbo(VboKey, (int)VbSize, DataAddress);
+                    if (Vmm.TryGetHostAddress(VbPosition, VbSize, out IntPtr VbPtr))
+                    {
+                        Gpu.Renderer.Rasterizer.CreateVbo(VboKey, (int)VbSize, VbPtr);
+                    }
+                    else
+                    {
+                        Gpu.Renderer.Rasterizer.CreateVbo(VboKey, Vmm.ReadBytes(VbPosition, VbSize));
+                    }
                 }
 
                 State.VertexBindings[Index].Enabled   = true;
diff --git a/Ryujinx.HLE/Memory/DeviceMemory.cs b/Ryujinx.HLE/DeviceMemory.cs
similarity index 95%
rename from Ryujinx.HLE/Memory/DeviceMemory.cs
rename to Ryujinx.HLE/DeviceMemory.cs
index 3c5f2e5f..edc70911 100644
--- a/Ryujinx.HLE/Memory/DeviceMemory.cs
+++ b/Ryujinx.HLE/DeviceMemory.cs
@@ -1,22 +1,18 @@
 using System;
 using System.Runtime.InteropServices;
 
-namespace Ryujinx.HLE.Memory
+namespace Ryujinx.HLE
 {
     class DeviceMemory : IDisposable
     {
         public const long RamSize = 4L * 1024 * 1024 * 1024;
 
-        public ArenaAllocator Allocator { get; private set; }
-
         public IntPtr RamPointer { get; private set; }
 
         private unsafe byte* RamPtr;
 
         public unsafe DeviceMemory()
         {
-            Allocator = new ArenaAllocator(RamSize);
-
             RamPointer = Marshal.AllocHGlobal(new IntPtr(RamSize));
 
             RamPtr = (byte*)RamPointer;
diff --git a/Ryujinx.HLE/FileSystem/SaveHelper.cs b/Ryujinx.HLE/FileSystem/SaveHelper.cs
index b74d853c..20138c8c 100644
--- a/Ryujinx.HLE/FileSystem/SaveHelper.cs
+++ b/Ryujinx.HLE/FileSystem/SaveHelper.cs
@@ -29,10 +29,7 @@ namespace Ryujinx.HLE.FileSystem
 
             if (SaveMetaData.TitleId == 0 && SaveMetaData.SaveDataType == SaveDataType.SaveData)
             {
-                if (Context.Process.MetaData != null)
-                {
-                    CurrentTitleId = Context.Process.MetaData.ACI0.TitleId;
-                }
+                CurrentTitleId = Context.Process.TitleId;
             }
 
             string SaveAccount = SaveMetaData.UserId.IsZero() ? "savecommon" : SaveMetaData.UserId.ToString();
diff --git a/Ryujinx.HLE/HOS/Font/SharedFontManager.cs b/Ryujinx.HLE/HOS/Font/SharedFontManager.cs
index 313db345..55adf46a 100644
--- a/Ryujinx.HLE/HOS/Font/SharedFontManager.cs
+++ b/Ryujinx.HLE/HOS/Font/SharedFontManager.cs
@@ -1,4 +1,4 @@
-using LibHac;
+using LibHac;
 using Ryujinx.HLE.FileSystem;
 using Ryujinx.HLE.FileSystem.Content;
 using Ryujinx.HLE.Resource;
diff --git a/Ryujinx.HLE/HOS/GlobalStateTable.cs b/Ryujinx.HLE/HOS/GlobalStateTable.cs
index faf47b2e..d7d83453 100644
--- a/Ryujinx.HLE/HOS/GlobalStateTable.cs
+++ b/Ryujinx.HLE/HOS/GlobalStateTable.cs
@@ -1,3 +1,4 @@
+using Ryujinx.HLE.HOS.Kernel;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
 
@@ -5,28 +6,28 @@ namespace Ryujinx.HLE.HOS
 {
     class GlobalStateTable
     {
-        private ConcurrentDictionary<Process, IdDictionary> DictByProcess;
+        private ConcurrentDictionary<KProcess, IdDictionary> DictByProcess;
 
         public GlobalStateTable()
         {
-            DictByProcess = new ConcurrentDictionary<Process, IdDictionary>();
+            DictByProcess = new ConcurrentDictionary<KProcess, IdDictionary>();
         }
 
-        public bool Add(Process Process, int Id, object Data)
+        public bool Add(KProcess Process, int Id, object Data)
         {
             IdDictionary Dict = DictByProcess.GetOrAdd(Process, (Key) => new IdDictionary());
 
             return Dict.Add(Id, Data);
         }
 
-        public int Add(Process Process, object Data)
+        public int Add(KProcess Process, object Data)
         {
             IdDictionary Dict = DictByProcess.GetOrAdd(Process, (Key) => new IdDictionary());
 
             return Dict.Add(Data);
         }
 
-        public object GetData(Process Process, int Id)
+        public object GetData(KProcess Process, int Id)
         {
             if (DictByProcess.TryGetValue(Process, out IdDictionary Dict))
             {
@@ -36,7 +37,7 @@ namespace Ryujinx.HLE.HOS
             return null;
         }
 
-        public T GetData<T>(Process Process, int Id)
+        public T GetData<T>(KProcess Process, int Id)
         {
             if (DictByProcess.TryGetValue(Process, out IdDictionary Dict))
             {
@@ -46,7 +47,7 @@ namespace Ryujinx.HLE.HOS
             return default(T);
         }
 
-        public object Delete(Process Process, int Id)
+        public object Delete(KProcess Process, int Id)
         {
             if (DictByProcess.TryGetValue(Process, out IdDictionary Dict))
             {
@@ -56,7 +57,7 @@ namespace Ryujinx.HLE.HOS
             return null;
         }
 
-        public ICollection<object> DeleteProcess(Process Process)
+        public ICollection<object> DeleteProcess(KProcess Process)
         {
             if (DictByProcess.TryRemove(Process, out IdDictionary Dict))
             {
diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs
index d967c896..46c27649 100644
--- a/Ryujinx.HLE/HOS/Horizon.cs
+++ b/Ryujinx.HLE/HOS/Horizon.cs
@@ -11,32 +11,68 @@ using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
-using Nso = Ryujinx.HLE.Loaders.Executables.Nso;
+using System.Reflection;
+using System.Threading;
+
+using NxStaticObject = Ryujinx.HLE.Loaders.Executables.NxStaticObject;
 
 namespace Ryujinx.HLE.HOS
 {
     public class Horizon : IDisposable
     {
+        internal const int InitialKipId     = 1;
+        internal const int InitialProcessId = 0x51;
+
         internal const int HidSize  = 0x40000;
         internal const int FontSize = 0x1100000;
 
-        private Switch Device;
+        private const int MemoryBlockAllocatorSize = 0x2710;
 
-        private ConcurrentDictionary<int, Process> Processes;
+        private const ulong UserSlabHeapBase     = DramMemoryMap.SlabHeapBase;
+        private const ulong UserSlabHeapItemSize = KMemoryManager.PageSize;
+        private const ulong UserSlabHeapSize     = 0x3de000;
+
+        internal long PrivilegedProcessLowestId  { get; set; } = 1;
+        internal long PrivilegedProcessHighestId { get; set; } = 8;
+
+        internal Switch Device { get; private set; }
 
         public SystemStateMgr State { get; private set; }
 
-        internal KRecursiveLock CriticalSectionLock { get; private set; }
+        internal bool KernelInitialized { get; private set; }
+
+        internal KResourceLimit ResourceLimit { get; private set; }
+
+        internal KMemoryRegionManager[] MemoryRegions { get; private set; }
+
+        internal KMemoryBlockAllocator LargeMemoryBlockAllocator { get; private set; }
+        internal KMemoryBlockAllocator SmallMemoryBlockAllocator { get; private set; }
+
+        internal KSlabHeap UserSlabHeapPages { get; private set; }
+
+        internal KCriticalSection CriticalSection { get; private set; }
 
         internal KScheduler Scheduler { get; private set; }
 
         internal KTimeManager TimeManager { get; private set; }
 
-        internal KAddressArbiter AddressArbiter { get; private set; }
-
         internal KSynchronization Synchronization { get; private set; }
 
-        internal LinkedList<KThread> Withholders { get; private set; }
+        internal KContextIdManager ContextIdManager { get; private set; }
+
+        private long KipId;
+        private long ProcessId;
+        private long ThreadUid;
+
+        internal CountdownEvent ThreadCounter;
+
+        internal SortedDictionary<long, KProcess> Processes;
+
+        internal ConcurrentDictionary<string, KAutoObject> AutoObjectNames;
+
+        internal bool EnableVersionChecks { get; private set; }
+
+        internal AppletStateMgr AppletState { get; private set; }
 
         internal KSharedMemory HidSharedMem  { get; private set; }
         internal KSharedMemory FontSharedMem { get; private set; }
@@ -57,38 +93,74 @@ namespace Ryujinx.HLE.HOS
 
         public IntegrityCheckLevel FsIntegrityCheckLevel { get; set; }
 
+        internal long HidBaseAddress { get; private set; }
+
         public Horizon(Switch Device)
         {
             this.Device = Device;
 
-            Processes = new ConcurrentDictionary<int, Process>();
-
             State = new SystemStateMgr();
 
-            CriticalSectionLock = new KRecursiveLock(this);
+            ResourceLimit = new KResourceLimit(this);
+
+            KernelInit.InitializeResourceLimit(ResourceLimit);
+
+            MemoryRegions = KernelInit.GetMemoryRegions();
+
+            LargeMemoryBlockAllocator = new KMemoryBlockAllocator(MemoryBlockAllocatorSize * 2);
+            SmallMemoryBlockAllocator = new KMemoryBlockAllocator(MemoryBlockAllocatorSize);
+
+            UserSlabHeapPages = new KSlabHeap(
+                UserSlabHeapBase,
+                UserSlabHeapItemSize,
+                UserSlabHeapSize);
+
+            CriticalSection = new KCriticalSection(this);
 
             Scheduler = new KScheduler(this);
 
             TimeManager = new KTimeManager();
 
-            AddressArbiter = new KAddressArbiter(this);
-
             Synchronization = new KSynchronization(this);
 
-            Withholders = new LinkedList<KThread>();
+            ContextIdManager = new KContextIdManager();
+
+            KipId     = InitialKipId;
+            ProcessId = InitialProcessId;
 
             Scheduler.StartAutoPreemptionThread();
 
-            if (!Device.Memory.Allocator.TryAllocate(HidSize,  out long HidPA) ||
-                !Device.Memory.Allocator.TryAllocate(FontSize, out long FontPA))
-            {
-                throw new InvalidOperationException();
-            }
+            KernelInitialized = true;
 
-            HidSharedMem  = new KSharedMemory(HidPA, HidSize);
-            FontSharedMem = new KSharedMemory(FontPA, FontSize);
+            ThreadCounter = new CountdownEvent(1);
 
-            Font = new SharedFontManager(Device, FontSharedMem.PA);
+            Processes = new SortedDictionary<long, KProcess>();
+
+            AutoObjectNames = new ConcurrentDictionary<string, KAutoObject>();
+
+            //Note: This is not really correct, but with HLE of services, the only memory
+            //region used that is used is Application, so we can use the other ones for anything.
+            KMemoryRegionManager Region = MemoryRegions[(int)MemoryRegion.NvServices];
+
+            ulong HidPa  = Region.Address;
+            ulong FontPa = Region.Address + HidSize;
+
+            HidBaseAddress = (long)(HidPa - DramMemoryMap.DramBase);
+
+            KPageList HidPageList  = new KPageList();
+            KPageList FontPageList = new KPageList();
+
+            HidPageList .AddRange(HidPa,  HidSize  / KMemoryManager.PageSize);
+            FontPageList.AddRange(FontPa, FontSize / KMemoryManager.PageSize);
+
+            HidSharedMem  = new KSharedMemory(HidPageList,  0, 0, MemoryPermission.Read);
+            FontSharedMem = new KSharedMemory(FontPageList, 0, 0, MemoryPermission.Read);
+
+            AppletState = new AppletStateMgr(this);
+
+            AppletState.SetFocus(true);
+
+            Font = new SharedFontManager(Device, (long)(FontPa - DramMemoryMap.DramBase));
 
             VsyncEvent = new KEvent(this);
 
@@ -120,13 +192,15 @@ namespace Ryujinx.HLE.HOS
             else
             {
                 Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!");
+
+                MetaData = GetDefaultNpdm();
             }
 
-            Process MainProcess = MakeProcess(MetaData);
+            List<IExecutable> StaticObjects = new List<IExecutable>();
 
-            void LoadNso(string FileName)
+            void LoadNso(string SearchPattern)
             {
-                foreach (string File in Directory.GetFiles(ExeFsDir, FileName))
+                foreach (string File in Directory.GetFiles(ExeFsDir, SearchPattern))
                 {
                     if (Path.GetExtension(File) != string.Empty)
                     {
@@ -137,33 +211,28 @@ namespace Ryujinx.HLE.HOS
 
                     using (FileStream Input = new FileStream(File, FileMode.Open))
                     {
-                        string Name = Path.GetFileNameWithoutExtension(File);
+                        NxStaticObject StaticObject = new NxStaticObject(Input);
 
-                        Nso Program = new Nso(Input, Name);
-
-                        MainProcess.LoadProgram(Program);
+                        StaticObjects.Add(StaticObject);
                     }
                 }
             }
 
-            if (!(MainProcess.MetaData?.Is64Bits ?? true))
+            if (!MetaData.Is64Bits)
             {
                 throw new NotImplementedException("32-bit titles are unsupported!");
             }
 
-            CurrentTitle = MainProcess.MetaData.ACI0.TitleId.ToString("x16");
+            CurrentTitle = MetaData.ACI0.TitleId.ToString("x16");
 
             LoadNso("rtld");
-
-            MainProcess.SetEmptyArgs();
-
             LoadNso("main");
             LoadNso("subsdk*");
             LoadNso("sdk");
 
             ContentManager.LoadEntries();
 
-            MainProcess.Run();
+            ProgramLoader.LoadStaticObjects(this, MetaData, StaticObjects.ToArray());
         }
 
         public void LoadXci(string XciFile)
@@ -356,9 +425,11 @@ namespace Ryujinx.HLE.HOS
             else
             {
                 Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!");
+
+                MetaData = GetDefaultNpdm();
             }
 
-            Process MainProcess = MakeProcess(MetaData);
+            List<IExecutable> StaticObjects = new List<IExecutable>();
 
             void LoadNso(string Filename)
             {
@@ -371,11 +442,9 @@ namespace Ryujinx.HLE.HOS
 
                     Logger.PrintInfo(LogClass.Loader, $"Loading {Filename}...");
 
-                    string Name = Path.GetFileNameWithoutExtension(File.Name);
+                    NxStaticObject StaticObject = new NxStaticObject(Exefs.OpenFile(File));
 
-                    Nso Program = new Nso(Exefs.OpenFile(File), Name);
-
-                    MainProcess.LoadProgram(Program);
+                    StaticObjects.Add(StaticObject);
                 }
             }
 
@@ -401,69 +470,52 @@ namespace Ryujinx.HLE.HOS
 
             if (ControlNca != null)
             {
-                MainProcess.ControlData = ReadControlData();
+                ReadControlData();
             }
             else
             {
-                CurrentTitle = MainProcess.MetaData.ACI0.TitleId.ToString("x16");
+                CurrentTitle = MetaData.ACI0.TitleId.ToString("x16");
             }
 
-            if (!MainProcess.MetaData.Is64Bits)
+            if (!MetaData.Is64Bits)
             {
-                throw new NotImplementedException("32-bit titles are unsupported!");
+                throw new NotImplementedException("32-bit titles are not supported!");
             }
 
             LoadNso("rtld");
-
-            MainProcess.SetEmptyArgs();
-
             LoadNso("main");
             LoadNso("subsdk");
             LoadNso("sdk");
 
             ContentManager.LoadEntries();
 
-            MainProcess.Run();
+            ProgramLoader.LoadStaticObjects(this, MetaData, StaticObjects.ToArray());
         }
 
         public void LoadProgram(string FilePath)
         {
+            Npdm MetaData = GetDefaultNpdm();
+
             bool IsNro = Path.GetExtension(FilePath).ToLower() == ".nro";
 
-            string Name           = Path.GetFileNameWithoutExtension(FilePath);
-            string SwitchFilePath = Device.FileSystem.SystemPathToSwitchPath(FilePath);
-
-            if (IsNro && (SwitchFilePath == null || !SwitchFilePath.StartsWith("sdmc:/")))
-            {
-                string SwitchPath = $"sdmc:/switch/{Name}{Homebrew.TemporaryNroSuffix}";
-                string TempPath   = Device.FileSystem.SwitchPathToSystemPath(SwitchPath);
-
-                string SwitchDir = Path.GetDirectoryName(TempPath);
-
-                if (!Directory.Exists(SwitchDir))
-                {
-                    Directory.CreateDirectory(SwitchDir);
-                }
-
-                File.Copy(FilePath, TempPath, true);
-
-                FilePath = TempPath;
-            }
-
-            Process MainProcess = MakeProcess();
-
             using (FileStream Input = new FileStream(FilePath, FileMode.Open))
             {
-                MainProcess.LoadProgram(IsNro
-                    ? (IExecutable)new Nro(Input, FilePath)
-                    : (IExecutable)new Nso(Input, FilePath));
+                IExecutable StaticObject = IsNro
+                    ? (IExecutable)new NxRelocatableObject(Input)
+                    : (IExecutable)new NxStaticObject(Input);
+
+                ProgramLoader.LoadStaticObjects(this, MetaData, new IExecutable[] { StaticObject });
             }
+        }
 
-            MainProcess.SetEmptyArgs();
+        private Npdm GetDefaultNpdm()
+        {
+            Assembly Asm = Assembly.GetCallingAssembly();
 
-            ContentManager.LoadEntries();
-
-            MainProcess.Run(IsNro);
+            using (Stream NpdmStream = Asm.GetManifestResourceStream("Ryujinx.HLE.Homebrew.npdm"))
+            {
+                return new Npdm(NpdmStream);
+            }
         }
 
         public void LoadKeySet()
@@ -507,51 +559,19 @@ namespace Ryujinx.HLE.HOS
             VsyncEvent.ReadableEvent.Signal();
         }
 
-        private Process MakeProcess(Npdm MetaData = null)
+        internal long GetThreadUid()
         {
-            HasStarted = true;
-
-            Process Process;
-
-            lock (Processes)
-            {
-                int ProcessId = 0;
-
-                while (Processes.ContainsKey(ProcessId))
-                {
-                    ProcessId++;
-                }
-
-                Process = new Process(Device, ProcessId, MetaData);
-
-                Processes.TryAdd(ProcessId, Process);
-            }
-
-            InitializeProcess(Process);
-
-            return Process;
+            return Interlocked.Increment(ref ThreadUid) - 1;
         }
 
-        private void InitializeProcess(Process Process)
+        internal long GetKipId()
         {
-            Process.AppletState.SetFocus(true);
+            return Interlocked.Increment(ref KipId) - 1;
         }
 
-        internal void ExitProcess(int ProcessId)
+        internal long GetProcessId()
         {
-            if (Processes.TryRemove(ProcessId, out Process Process))
-            {
-                Process.Dispose();
-
-                if (Processes.Count == 0)
-                {
-                    Scheduler.Dispose();
-
-                    TimeManager.Dispose();
-
-                    Device.Unload();
-                }
-            }
+            return Interlocked.Increment(ref ProcessId) - 1;
         }
 
         public void EnableMultiCoreScheduling()
@@ -579,10 +599,25 @@ namespace Ryujinx.HLE.HOS
         {
             if (Disposing)
             {
-                foreach (Process Process in Processes.Values)
+                //Force all threads to exit.
+                lock (Processes)
                 {
-                    Process.Dispose();
+                    foreach (KProcess Process in Processes.Values)
+                    {
+                        Process.StopAllThreads();
+                    }
                 }
+
+                //It's only safe to release resources once all threads
+                //have exited.
+                ThreadCounter.Signal();
+                ThreadCounter.Wait();
+
+                Scheduler.Dispose();
+
+                TimeManager.Dispose();
+
+                Device.Unload();
             }
         }
     }
diff --git a/Ryujinx.HLE/HOS/Ipc/IpcHandler.cs b/Ryujinx.HLE/HOS/Ipc/IpcHandler.cs
index ec27a6ea..860c8242 100644
--- a/Ryujinx.HLE/HOS/Ipc/IpcHandler.cs
+++ b/Ryujinx.HLE/HOS/Ipc/IpcHandler.cs
@@ -8,8 +8,8 @@ namespace Ryujinx.HLE.HOS.Ipc
     static class IpcHandler
     {
         public static long IpcCall(
-            Switch        Ns,
-            Process       Process,
+            Switch        Device,
+            KProcess      Process,
             MemoryManager Memory,
             KSession      Session,
             IpcMessage    Request,
@@ -31,7 +31,7 @@ namespace Ryujinx.HLE.HOS.Ipc
                         BinaryWriter ResWriter = new BinaryWriter(ResMS);
 
                         ServiceCtx Context = new ServiceCtx(
-                            Ns,
+                            Device,
                             Process,
                             Memory,
                             Session,
diff --git a/Ryujinx.HLE/HOS/Kernel/AddressSpaceType.cs b/Ryujinx.HLE/HOS/Kernel/AddressSpaceType.cs
index c97caf42..6f7b230e 100644
--- a/Ryujinx.HLE/HOS/Kernel/AddressSpaceType.cs
+++ b/Ryujinx.HLE/HOS/Kernel/AddressSpaceType.cs
@@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Kernel
     {
         Addr32Bits      = 0,
         Addr36Bits      = 1,
-        Addr36BitsNoMap = 2,
+        Addr32BitsNoMap = 2,
         Addr39Bits      = 3
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/ArbitrationType.cs b/Ryujinx.HLE/HOS/Kernel/ArbitrationType.cs
index 8a2d47f7..b584d719 100644
--- a/Ryujinx.HLE/HOS/Kernel/ArbitrationType.cs
+++ b/Ryujinx.HLE/HOS/Kernel/ArbitrationType.cs
@@ -6,4 +6,4 @@ namespace Ryujinx.HLE.HOS.Kernel
         DecrementAndWaitIfLessThan = 1,
         WaitIfEqual                = 2
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/DramMemoryMap.cs b/Ryujinx.HLE/HOS/Kernel/DramMemoryMap.cs
new file mode 100644
index 00000000..b20a83e2
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/DramMemoryMap.cs
@@ -0,0 +1,15 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+    static class DramMemoryMap
+    {
+        public const ulong DramBase = 0x80000000;
+        public const ulong DramSize = 0x100000000;
+        public const ulong DramEnd  = DramBase + DramSize;
+
+        public const ulong KernelReserveBase = DramBase + 0x60000;
+
+        public const ulong SlabHeapBase = KernelReserveBase + 0x85000;
+        public const ulong SlapHeapSize = 0xa21000;
+        public const ulong SlabHeapEnd  = SlabHeapBase + SlapHeapSize;
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/HleCoreManager.cs b/Ryujinx.HLE/HOS/Kernel/HleCoreManager.cs
index 0bfa2710..6a424cf2 100644
--- a/Ryujinx.HLE/HOS/Kernel/HleCoreManager.cs
+++ b/Ryujinx.HLE/HOS/Kernel/HleCoreManager.cs
@@ -5,24 +5,61 @@ namespace Ryujinx.HLE.HOS.Kernel
 {
     class HleCoreManager
     {
-        private ConcurrentDictionary<Thread, ManualResetEvent> Threads;
+        private class PausableThread
+        {
+            public ManualResetEvent Event { get; private set; }
+
+            public bool IsExiting { get; set; }
+
+            public PausableThread()
+            {
+                Event = new ManualResetEvent(false);
+            }
+        }
+
+        private ConcurrentDictionary<Thread, PausableThread> Threads;
 
         public HleCoreManager()
         {
-            Threads = new ConcurrentDictionary<Thread, ManualResetEvent>();
+            Threads = new ConcurrentDictionary<Thread, PausableThread>();
         }
 
-        public ManualResetEvent GetThread(Thread Thread)
+        public void Set(Thread Thread)
         {
-            return Threads.GetOrAdd(Thread, (Key) => new ManualResetEvent(false));
+            GetThread(Thread).Event.Set();
+        }
+
+        public void Reset(Thread Thread)
+        {
+            GetThread(Thread).Event.Reset();
+        }
+
+        public void Wait(Thread Thread)
+        {
+            PausableThread PausableThread = GetThread(Thread);
+
+            if (!PausableThread.IsExiting)
+            {
+                PausableThread.Event.WaitOne();
+            }
+        }
+
+        public void Exit(Thread Thread)
+        {
+            GetThread(Thread).IsExiting = true;
+        }
+
+        private PausableThread GetThread(Thread Thread)
+        {
+            return Threads.GetOrAdd(Thread, (Key) => new PausableThread());
         }
 
         public void RemoveThread(Thread Thread)
         {
-            if (Threads.TryRemove(Thread, out ManualResetEvent Event))
+            if (Threads.TryRemove(Thread, out PausableThread PausableThread))
             {
-                Event.Set();
-                Event.Dispose();
+                PausableThread.Event.Set();
+                PausableThread.Event.Dispose();
             }
         }
     }
diff --git a/Ryujinx.HLE/HOS/Kernel/HleProcessDebugger.cs b/Ryujinx.HLE/HOS/Kernel/HleProcessDebugger.cs
new file mode 100644
index 00000000..a6053b1b
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/HleProcessDebugger.cs
@@ -0,0 +1,310 @@
+using ChocolArm64.Memory;
+using ChocolArm64.State;
+using Ryujinx.Common.Logging;
+using Ryujinx.HLE.HOS.Diagnostics.Demangler;
+using Ryujinx.HLE.Loaders.Elf;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+    class HleProcessDebugger
+    {
+        private const int Mod0 = 'M' << 0 | 'O' << 8 | 'D' << 16 | '0' << 24;
+
+        private KProcess Owner;
+
+        private class Image
+        {
+            public long BaseAddress { get; private set; }
+
+            public ElfSymbol[] Symbols { get; private set; }
+
+            public Image(long BaseAddress, ElfSymbol[] Symbols)
+            {
+                this.BaseAddress = BaseAddress;
+                this.Symbols     = Symbols;
+            }
+        }
+
+        private List<Image> Images;
+
+        private int Loaded;
+
+        public HleProcessDebugger(KProcess Owner)
+        {
+            this.Owner = Owner;
+
+            Images = new List<Image>();
+        }
+
+        public void PrintGuestStackTrace(CpuThreadState ThreadState)
+        {
+            EnsureLoaded();
+
+            StringBuilder Trace = new StringBuilder();
+
+            Trace.AppendLine("Guest stack trace:");
+
+            void AppendTrace(long Address)
+            {
+                Image Image = GetImage(Address, out int ImageIndex);
+
+                if (Image == null || !TryGetSubName(Image, Address, out string SubName))
+                {
+                    SubName = $"Sub{Address:x16}";
+                }
+                else if (SubName.StartsWith("_Z"))
+                {
+                    SubName = Demangler.Parse(SubName);
+                }
+
+                if (Image != null)
+                {
+                    long Offset = Address - Image.BaseAddress;
+
+                    string ImageName = GetGuessedNsoNameFromIndex(ImageIndex);
+
+                    string ImageNameAndOffset = $"[{Owner.Name}] {ImageName}:0x{Offset:x8}";
+
+                    Trace.AppendLine($" {ImageNameAndOffset} {SubName}");
+                }
+                else
+                {
+                    Trace.AppendLine($" [{Owner.Name}] ??? {SubName}");
+                }
+            }
+
+            long FramePointer = (long)ThreadState.X29;
+
+            while (FramePointer != 0)
+            {
+                if ((FramePointer & 7) != 0                 ||
+                    !Owner.CpuMemory.IsMapped(FramePointer) ||
+                    !Owner.CpuMemory.IsMapped(FramePointer + 8))
+                {
+                    break;
+                }
+
+                //Note: This is the return address, we need to subtract one instruction
+                //worth of bytes to get the branch instruction address.
+                AppendTrace(Owner.CpuMemory.ReadInt64(FramePointer + 8) - 4);
+
+                FramePointer = Owner.CpuMemory.ReadInt64(FramePointer);
+            }
+
+            Logger.PrintInfo(LogClass.Cpu, Trace.ToString());
+        }
+
+        private bool TryGetSubName(Image Image, long Address, out string Name)
+        {
+            Address -= Image.BaseAddress;
+
+            int Left  = 0;
+            int Right = Image.Symbols.Length - 1;
+
+            while (Left <= Right)
+            {
+                int Size = Right - Left;
+
+                int Middle = Left + (Size >> 1);
+
+                ElfSymbol Symbol = Image.Symbols[Middle];
+
+                long EndAddr = Symbol.Value + Symbol.Size;
+
+                if ((ulong)Address >= (ulong)Symbol.Value && (ulong)Address < (ulong)EndAddr)
+                {
+                    Name = Symbol.Name;
+
+                    return true;
+                }
+
+                if ((ulong)Address < (ulong)Symbol.Value)
+                {
+                    Right = Middle - 1;
+                }
+                else
+                {
+                    Left = Middle + 1;
+                }
+            }
+
+            Name = null;
+
+            return false;
+        }
+
+        private Image GetImage(long Address, out int Index)
+        {
+            lock (Images)
+            {
+                for (Index = Images.Count - 1; Index >= 0; Index--)
+                {
+                    if ((ulong)Address >= (ulong)Images[Index].BaseAddress)
+                    {
+                        return Images[Index];
+                    }
+                }
+            }
+
+            return null;
+        }
+
+        private string GetGuessedNsoNameFromIndex(int Index)
+        {
+            if ((uint)Index > 11)
+            {
+                return "???";
+            }
+
+            if (Index == 0)
+            {
+                return "rtld";
+            }
+            else if (Index == 1)
+            {
+                return "main";
+            }
+            else if (Index == GetImagesCount() - 1)
+            {
+                return "sdk";
+            }
+            else
+            {
+                return "subsdk" + (Index - 2);
+            }
+        }
+
+        private int GetImagesCount()
+        {
+            lock (Images)
+            {
+                return Images.Count;
+            }
+        }
+
+        private void EnsureLoaded()
+        {
+            if (Interlocked.CompareExchange(ref Loaded, 1, 0) == 0)
+            {
+                ScanMemoryForTextSegments();
+            }
+        }
+
+        private void ScanMemoryForTextSegments()
+        {
+            ulong OldAddress = 0;
+            ulong Address    = 0;
+
+            while (Address >= OldAddress)
+            {
+                KMemoryInfo Info = Owner.MemoryManager.QueryMemory(Address);
+
+                if (Info.State == MemoryState.Reserved)
+                {
+                    break;
+                }
+
+                if (Info.State == MemoryState.CodeStatic && Info.Permission == MemoryPermission.ReadAndExecute)
+                {
+                    LoadMod0Symbols(Owner.CpuMemory, (long)Info.Address);
+                }
+
+                OldAddress = Address;
+
+                Address = Info.Address + Info.Size;
+            }
+        }
+
+        private void LoadMod0Symbols(MemoryManager Memory, long TextOffset)
+        {
+            long Mod0Offset = TextOffset + Memory.ReadUInt32(TextOffset + 4);
+
+            if (Mod0Offset < TextOffset || !Memory.IsMapped(Mod0Offset) || (Mod0Offset & 3) != 0)
+            {
+                return;
+            }
+
+            Dictionary<ElfDynamicTag, long> Dynamic = new Dictionary<ElfDynamicTag, long>();
+
+            int Mod0Magic = Memory.ReadInt32(Mod0Offset + 0x0);
+
+            if (Mod0Magic != Mod0)
+            {
+                return;
+            }
+
+            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;
+
+            while (true)
+            {
+                long TagVal = Memory.ReadInt64(DynamicOffset + 0);
+                long Value  = Memory.ReadInt64(DynamicOffset + 8);
+
+                DynamicOffset += 0x10;
+
+                ElfDynamicTag Tag = (ElfDynamicTag)TagVal;
+
+                if (Tag == ElfDynamicTag.DT_NULL)
+                {
+                    break;
+                }
+
+                Dynamic[Tag] = Value;
+            }
+
+            if (!Dynamic.TryGetValue(ElfDynamicTag.DT_STRTAB, out long StrTab) ||
+                !Dynamic.TryGetValue(ElfDynamicTag.DT_SYMTAB, out long SymTab) ||
+                !Dynamic.TryGetValue(ElfDynamicTag.DT_SYMENT, out long SymEntSize))
+            {
+                return;
+            }
+
+            long StrTblAddr = TextOffset + StrTab;
+            long SymTblAddr = TextOffset + SymTab;
+
+            List<ElfSymbol> Symbols = new List<ElfSymbol>();
+
+            while ((ulong)SymTblAddr < (ulong)StrTblAddr)
+            {
+                ElfSymbol Sym = GetSymbol(Memory, SymTblAddr, StrTblAddr);
+
+                Symbols.Add(Sym);
+
+                SymTblAddr += SymEntSize;
+            }
+
+            lock (Images)
+            {
+                Images.Add(new Image(TextOffset, Symbols.OrderBy(x => x.Value).ToArray()));
+            }
+        }
+
+        private ElfSymbol GetSymbol(MemoryManager Memory, long Address, long StrTblAddr)
+        {
+            int  NameIndex = Memory.ReadInt32(Address + 0);
+            int  Info      = Memory.ReadByte (Address + 4);
+            int  Other     = Memory.ReadByte (Address + 5);
+            int  SHIdx     = Memory.ReadInt16(Address + 6);
+            long Value     = Memory.ReadInt64(Address + 8);
+            long Size      = Memory.ReadInt64(Address + 16);
+
+            string Name = string.Empty;
+
+            for (int Chr; (Chr = Memory.ReadByte(StrTblAddr + NameIndex++)) != 0;)
+            {
+                Name += (char)Chr;
+            }
+
+            return new ElfSymbol(Name, Info, Other, SHIdx, Value, Size);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/HleScheduler.cs b/Ryujinx.HLE/HOS/Kernel/HleScheduler.cs
index e0cb158c..87dbe553 100644
--- a/Ryujinx.HLE/HOS/Kernel/HleScheduler.cs
+++ b/Ryujinx.HLE/HOS/Kernel/HleScheduler.cs
@@ -11,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Kernel
 
         public bool MultiCoreScheduling { get; set; }
 
-        private HleCoreManager CoreManager;
+        public HleCoreManager CoreManager { get; private set; }
 
         private bool KeepPreempting;
 
@@ -49,11 +49,11 @@ namespace Ryujinx.HLE.HOS.Kernel
 
                     if (SelectedCount == 0)
                     {
-                        CoreManager.GetThread(Thread.CurrentThread).Reset();
+                        CoreManager.Reset(Thread.CurrentThread);
                     }
                     else if (SelectedCount == 1)
                     {
-                        CoreManager.GetThread(Thread.CurrentThread).Set();
+                        CoreManager.Set(Thread.CurrentThread);
                     }
                     else
                     {
@@ -77,7 +77,7 @@ namespace Ryujinx.HLE.HOS.Kernel
                             return;
                         }
 
-                        CoreManager.GetThread(CurrentThread.Context.Work).Reset();
+                        CoreManager.Reset(CurrentThread.Context.Work);
                     }
 
                     //Advance current core and try picking a thread,
@@ -94,7 +94,7 @@ namespace Ryujinx.HLE.HOS.Kernel
                         {
                             CoreContext.CurrentThread.ClearExclusive();
 
-                            CoreManager.GetThread(CoreContext.CurrentThread.Context.Work).Set();
+                            CoreManager.Set(CoreContext.CurrentThread.Context.Work);
 
                             CoreContext.CurrentThread.Context.Execute();
 
@@ -111,7 +111,7 @@ namespace Ryujinx.HLE.HOS.Kernel
                 }
             }
 
-            CoreManager.GetThread(Thread.CurrentThread).WaitOne();
+            CoreManager.Wait(Thread.CurrentThread);
         }
 
         private void PreemptCurrentThread()
@@ -134,11 +134,11 @@ namespace Ryujinx.HLE.HOS.Kernel
             }
         }
 
-        public void StopThread(KThread Thread)
+        public void ExitThread(KThread Thread)
         {
             Thread.Context.StopExecution();
 
-            CoreManager.GetThread(Thread.Context.Work).Set();
+            CoreManager.Exit(Thread.Context.Work);
         }
 
         public void RemoveThread(KThread Thread)
diff --git a/Ryujinx.HLE/HOS/Kernel/KAddressArbiter.cs b/Ryujinx.HLE/HOS/Kernel/KAddressArbiter.cs
index 4a0f955f..cc637be0 100644
--- a/Ryujinx.HLE/HOS/Kernel/KAddressArbiter.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KAddressArbiter.cs
@@ -1,4 +1,3 @@
-using ChocolArm64.Memory;
 using System.Collections.Generic;
 using System.Linq;
 
@@ -23,39 +22,36 @@ namespace Ryujinx.HLE.HOS.Kernel
             ArbiterThreads = new List<KThread>();
         }
 
-        public long ArbitrateLock(
-            Process       Process,
-            MemoryManager Memory,
-            int           OwnerHandle,
-            long          MutexAddress,
-            int           RequesterHandle)
+        public long ArbitrateLock(int OwnerHandle, long MutexAddress, int RequesterHandle)
         {
-            System.CriticalSectionLock.Lock();
-
             KThread CurrentThread = System.Scheduler.GetCurrentThread();
 
+            System.CriticalSection.Enter();
+
             CurrentThread.SignaledObj   = null;
             CurrentThread.ObjSyncResult = 0;
 
-            if (!UserToKernelInt32(Memory, MutexAddress, out int MutexValue))
+            KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+            if (!KernelTransfer.UserToKernelInt32(System, MutexAddress, out int MutexValue))
             {
-                System.CriticalSectionLock.Unlock();
+                System.CriticalSection.Leave();
 
                 return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);;
             }
 
             if (MutexValue != (OwnerHandle | HasListenersMask))
             {
-                System.CriticalSectionLock.Unlock();
+                System.CriticalSection.Leave();
 
                 return 0;
             }
 
-            KThread MutexOwner = Process.HandleTable.GetObject<KThread>(OwnerHandle);
+            KThread MutexOwner = CurrentProcess.HandleTable.GetObject<KThread>(OwnerHandle);
 
             if (MutexOwner == null)
             {
-                System.CriticalSectionLock.Unlock();
+                System.CriticalSection.Leave();
 
                 return MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
             }
@@ -67,26 +63,26 @@ namespace Ryujinx.HLE.HOS.Kernel
 
             CurrentThread.Reschedule(ThreadSchedState.Paused);
 
-            System.CriticalSectionLock.Unlock();
-            System.CriticalSectionLock.Lock();
+            System.CriticalSection.Leave();
+            System.CriticalSection.Enter();
 
             if (CurrentThread.MutexOwner != null)
             {
                 CurrentThread.MutexOwner.RemoveMutexWaiter(CurrentThread);
             }
 
-            System.CriticalSectionLock.Unlock();
+            System.CriticalSection.Leave();
 
             return (uint)CurrentThread.ObjSyncResult;
         }
 
-        public long ArbitrateUnlock(MemoryManager Memory, long MutexAddress)
+        public long ArbitrateUnlock(long MutexAddress)
         {
-            System.CriticalSectionLock.Lock();
+            System.CriticalSection.Enter();
 
             KThread CurrentThread = System.Scheduler.GetCurrentThread();
 
-            (long Result, KThread NewOwnerThread) = MutexUnlock(Memory, CurrentThread, MutexAddress);
+            (long Result, KThread NewOwnerThread) = MutexUnlock(CurrentThread, MutexAddress);
 
             if (Result != 0 && NewOwnerThread != null)
             {
@@ -94,19 +90,18 @@ namespace Ryujinx.HLE.HOS.Kernel
                 NewOwnerThread.ObjSyncResult = (int)Result;
             }
 
-            System.CriticalSectionLock.Unlock();
+            System.CriticalSection.Leave();
 
             return Result;
         }
 
         public long WaitProcessWideKeyAtomic(
-            MemoryManager Memory,
-            long          MutexAddress,
-            long          CondVarAddress,
-            int           ThreadHandle,
-            long          Timeout)
+            long MutexAddress,
+            long CondVarAddress,
+            int  ThreadHandle,
+            long Timeout)
         {
-            System.CriticalSectionLock.Lock();
+            System.CriticalSection.Enter();
 
             KThread CurrentThread = System.Scheduler.GetCurrentThread();
 
@@ -116,16 +111,16 @@ namespace Ryujinx.HLE.HOS.Kernel
             if (CurrentThread.ShallBeTerminated ||
                 CurrentThread.SchedFlags == ThreadSchedState.TerminationPending)
             {
-                System.CriticalSectionLock.Unlock();
+                System.CriticalSection.Leave();
 
                 return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
             }
 
-            (long Result, _) = MutexUnlock(Memory, CurrentThread, MutexAddress);
+            (long Result, _) = MutexUnlock(CurrentThread, MutexAddress);
 
             if (Result != 0)
             {
-                System.CriticalSectionLock.Unlock();
+                System.CriticalSection.Leave();
 
                 return Result;
             }
@@ -146,14 +141,14 @@ namespace Ryujinx.HLE.HOS.Kernel
                 }
             }
 
-            System.CriticalSectionLock.Unlock();
+            System.CriticalSection.Leave();
 
             if (Timeout > 0)
             {
                 System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
             }
 
-            System.CriticalSectionLock.Lock();
+            System.CriticalSection.Enter();
 
             if (CurrentThread.MutexOwner != null)
             {
@@ -162,12 +157,12 @@ namespace Ryujinx.HLE.HOS.Kernel
 
             CondVarThreads.Remove(CurrentThread);
 
-            System.CriticalSectionLock.Unlock();
+            System.CriticalSection.Leave();
 
             return (uint)CurrentThread.ObjSyncResult;
         }
 
-        private (long, KThread) MutexUnlock(MemoryManager Memory, KThread CurrentThread, long MutexAddress)
+        private (long, KThread) MutexUnlock(KThread CurrentThread, long MutexAddress)
         {
             KThread NewOwnerThread = CurrentThread.RelinquishMutex(MutexAddress, out int Count);
 
@@ -190,7 +185,7 @@ namespace Ryujinx.HLE.HOS.Kernel
 
             long Result = 0;
 
-            if (!KernelToUserInt32(Memory, MutexAddress, MutexValue))
+            if (!KernelTransfer.KernelToUserInt32(System, MutexAddress, MutexValue))
             {
                 Result = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
             }
@@ -198,17 +193,17 @@ namespace Ryujinx.HLE.HOS.Kernel
             return (Result, NewOwnerThread);
         }
 
-        public void SignalProcessWideKey(Process Process, MemoryManager Memory, long Address, int Count)
+        public void SignalProcessWideKey(long Address, int Count)
         {
             Queue<KThread> SignaledThreads = new Queue<KThread>();
 
-            System.CriticalSectionLock.Lock();
+            System.CriticalSection.Enter();
 
             IOrderedEnumerable<KThread> SortedThreads = CondVarThreads.OrderBy(x => x.DynamicPriority);
 
             foreach (KThread Thread in SortedThreads.Where(x => x.CondVarAddress == Address))
             {
-                TryAcquireMutex(Process, Memory, Thread);
+                TryAcquireMutex(Thread);
 
                 SignaledThreads.Enqueue(Thread);
 
@@ -224,19 +219,21 @@ namespace Ryujinx.HLE.HOS.Kernel
                 CondVarThreads.Remove(Thread);
             }
 
-            System.CriticalSectionLock.Unlock();
+            System.CriticalSection.Leave();
         }
 
-        private KThread TryAcquireMutex(Process Process, MemoryManager Memory, KThread Requester)
+        private KThread TryAcquireMutex(KThread Requester)
         {
             long Address = Requester.MutexAddress;
 
-            Memory.SetExclusive(0, Address);
+            KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
 
-            if (!UserToKernelInt32(Memory, Address, out int MutexValue))
+            CurrentProcess.CpuMemory.SetExclusive(0, Address);
+
+            if (!KernelTransfer.UserToKernelInt32(System, Address, out int MutexValue))
             {
                 //Invalid address.
-                Memory.ClearExclusive(0);
+                CurrentProcess.CpuMemory.ClearExclusive(0);
 
                 Requester.SignaledObj   = null;
                 Requester.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
@@ -246,27 +243,27 @@ namespace Ryujinx.HLE.HOS.Kernel
 
             while (true)
             {
-                if (Memory.TestExclusive(0, Address))
+                if (CurrentProcess.CpuMemory.TestExclusive(0, Address))
                 {
                     if (MutexValue != 0)
                     {
                         //Update value to indicate there is a mutex waiter now.
-                        Memory.WriteInt32(Address, MutexValue | HasListenersMask);
+                        CurrentProcess.CpuMemory.WriteInt32(Address, MutexValue | HasListenersMask);
                     }
                     else
                     {
                         //No thread owning the mutex, assign to requesting thread.
-                        Memory.WriteInt32(Address, Requester.ThreadHandleForUserMutex);
+                        CurrentProcess.CpuMemory.WriteInt32(Address, Requester.ThreadHandleForUserMutex);
                     }
 
-                    Memory.ClearExclusiveForStore(0);
+                    CurrentProcess.CpuMemory.ClearExclusiveForStore(0);
 
                     break;
                 }
 
-                Memory.SetExclusive(0, Address);
+                CurrentProcess.CpuMemory.SetExclusive(0, Address);
 
-                MutexValue = Memory.ReadInt32(Address);
+                MutexValue = CurrentProcess.CpuMemory.ReadInt32(Address);
             }
 
             if (MutexValue == 0)
@@ -282,7 +279,7 @@ namespace Ryujinx.HLE.HOS.Kernel
 
             MutexValue &= ~HasListenersMask;
 
-            KThread MutexOwner = Process.HandleTable.GetObject<KThread>(MutexValue);
+            KThread MutexOwner = CurrentProcess.HandleTable.GetObject<KThread>(MutexValue);
 
             if (MutexOwner != null)
             {
@@ -301,16 +298,16 @@ namespace Ryujinx.HLE.HOS.Kernel
             return MutexOwner;
         }
 
-        public long WaitForAddressIfEqual(MemoryManager Memory, long Address, int Value, long Timeout)
+        public long WaitForAddressIfEqual(long Address, int Value, long Timeout)
         {
             KThread CurrentThread = System.Scheduler.GetCurrentThread();
 
-            System.CriticalSectionLock.Lock();
+            System.CriticalSection.Enter();
 
             if (CurrentThread.ShallBeTerminated ||
                 CurrentThread.SchedFlags == ThreadSchedState.TerminationPending)
             {
-                System.CriticalSectionLock.Unlock();
+                System.CriticalSection.Leave();
 
                 return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
             }
@@ -318,9 +315,9 @@ namespace Ryujinx.HLE.HOS.Kernel
             CurrentThread.SignaledObj   = null;
             CurrentThread.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.Timeout);
 
-            if (!UserToKernelInt32(Memory, Address, out int CurrentValue))
+            if (!KernelTransfer.UserToKernelInt32(System, Address, out int CurrentValue))
             {
-                System.CriticalSectionLock.Unlock();
+                System.CriticalSection.Leave();
 
                 return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
             }
@@ -329,7 +326,7 @@ namespace Ryujinx.HLE.HOS.Kernel
             {
                 if (Timeout == 0)
                 {
-                    System.CriticalSectionLock.Unlock();
+                    System.CriticalSection.Leave();
 
                     return MakeError(ErrorModule.Kernel, KernelErr.Timeout);
                 }
@@ -346,14 +343,14 @@ namespace Ryujinx.HLE.HOS.Kernel
                     System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout);
                 }
 
-                System.CriticalSectionLock.Unlock();
+                System.CriticalSection.Leave();
 
                 if (Timeout > 0)
                 {
                     System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
                 }
 
-                System.CriticalSectionLock.Lock();
+                System.CriticalSection.Enter();
 
                 if (CurrentThread.WaitingInArbitration)
                 {
@@ -362,31 +359,26 @@ namespace Ryujinx.HLE.HOS.Kernel
                     CurrentThread.WaitingInArbitration = false;
                 }
 
-                System.CriticalSectionLock.Unlock();
+                System.CriticalSection.Leave();
 
                 return CurrentThread.ObjSyncResult;
             }
 
-            System.CriticalSectionLock.Unlock();
+            System.CriticalSection.Leave();
 
             return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
         }
 
-        public long WaitForAddressIfLessThan(
-            MemoryManager Memory,
-            long          Address,
-            int           Value,
-            bool          ShouldDecrement,
-            long          Timeout)
+        public long WaitForAddressIfLessThan(long Address, int Value, bool ShouldDecrement, long Timeout)
         {
             KThread CurrentThread = System.Scheduler.GetCurrentThread();
 
-            System.CriticalSectionLock.Lock();
+            System.CriticalSection.Enter();
 
             if (CurrentThread.ShallBeTerminated ||
                 CurrentThread.SchedFlags == ThreadSchedState.TerminationPending)
             {
-                System.CriticalSectionLock.Unlock();
+                System.CriticalSection.Leave();
 
                 return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
             }
@@ -394,12 +386,14 @@ namespace Ryujinx.HLE.HOS.Kernel
             CurrentThread.SignaledObj   = null;
             CurrentThread.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.Timeout);
 
-            //If ShouldDecrement is true, do atomic decrement of the value at Address.
-            Memory.SetExclusive(0, Address);
+            KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
 
-            if (!UserToKernelInt32(Memory, Address, out int CurrentValue))
+            //If ShouldDecrement is true, do atomic decrement of the value at Address.
+            CurrentProcess.CpuMemory.SetExclusive(0, Address);
+
+            if (!KernelTransfer.UserToKernelInt32(System, Address, out int CurrentValue))
             {
-                System.CriticalSectionLock.Unlock();
+                System.CriticalSection.Leave();
 
                 return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
             }
@@ -408,28 +402,28 @@ namespace Ryujinx.HLE.HOS.Kernel
             {
                 while (CurrentValue < Value)
                 {
-                    if (Memory.TestExclusive(0, Address))
+                    if (CurrentProcess.CpuMemory.TestExclusive(0, Address))
                     {
-                        Memory.WriteInt32(Address, CurrentValue - 1);
+                        CurrentProcess.CpuMemory.WriteInt32(Address, CurrentValue - 1);
 
-                        Memory.ClearExclusiveForStore(0);
+                        CurrentProcess.CpuMemory.ClearExclusiveForStore(0);
 
                         break;
                     }
 
-                    Memory.SetExclusive(0, Address);
+                    CurrentProcess.CpuMemory.SetExclusive(0, Address);
 
-                    CurrentValue = Memory.ReadInt32(Address);
+                    CurrentValue = CurrentProcess.CpuMemory.ReadInt32(Address);
                 }
             }
 
-            Memory.ClearExclusive(0);
+            CurrentProcess.CpuMemory.ClearExclusive(0);
 
             if (CurrentValue < Value)
             {
                 if (Timeout == 0)
                 {
-                    System.CriticalSectionLock.Unlock();
+                    System.CriticalSection.Leave();
 
                     return MakeError(ErrorModule.Kernel, KernelErr.Timeout);
                 }
@@ -446,14 +440,14 @@ namespace Ryujinx.HLE.HOS.Kernel
                     System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout);
                 }
 
-                System.CriticalSectionLock.Unlock();
+                System.CriticalSection.Leave();
 
                 if (Timeout > 0)
                 {
                     System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
                 }
 
-                System.CriticalSectionLock.Lock();
+                System.CriticalSection.Enter();
 
                 if (CurrentThread.WaitingInArbitration)
                 {
@@ -462,12 +456,12 @@ namespace Ryujinx.HLE.HOS.Kernel
                     CurrentThread.WaitingInArbitration = false;
                 }
 
-                System.CriticalSectionLock.Unlock();
+                System.CriticalSection.Leave();
 
                 return CurrentThread.ObjSyncResult;
             }
 
-            System.CriticalSectionLock.Unlock();
+            System.CriticalSection.Leave();
 
             return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
         }
@@ -498,63 +492,65 @@ namespace Ryujinx.HLE.HOS.Kernel
 
         public long Signal(long Address, int Count)
         {
-            System.CriticalSectionLock.Lock();
+            System.CriticalSection.Enter();
 
             WakeArbiterThreads(Address, Count);
 
-            System.CriticalSectionLock.Unlock();
+            System.CriticalSection.Leave();
 
             return 0;
         }
 
-        public long SignalAndIncrementIfEqual(MemoryManager Memory, long Address, int Value, int Count)
+        public long SignalAndIncrementIfEqual(long Address, int Value, int Count)
         {
-            System.CriticalSectionLock.Lock();
+            System.CriticalSection.Enter();
 
-            Memory.SetExclusive(0, Address);
+            KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
 
-            if (!UserToKernelInt32(Memory, Address, out int CurrentValue))
+            CurrentProcess.CpuMemory.SetExclusive(0, Address);
+
+            if (!KernelTransfer.UserToKernelInt32(System, Address, out int CurrentValue))
             {
-                System.CriticalSectionLock.Unlock();
+                System.CriticalSection.Leave();
 
                 return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
             }
 
             while (CurrentValue == Value)
             {
-                if (Memory.TestExclusive(0, Address))
+                if (CurrentProcess.CpuMemory.TestExclusive(0, Address))
                 {
-                    Memory.WriteInt32(Address, CurrentValue + 1);
+                    CurrentProcess.CpuMemory.WriteInt32(Address, CurrentValue + 1);
 
-                    Memory.ClearExclusiveForStore(0);
+                    CurrentProcess.CpuMemory.ClearExclusiveForStore(0);
 
                     break;
                 }
 
-                Memory.SetExclusive(0, Address);
+                CurrentProcess.CpuMemory.SetExclusive(0, Address);
 
-                CurrentValue = Memory.ReadInt32(Address);
+                CurrentValue = CurrentProcess.CpuMemory.ReadInt32(Address);
             }
 
-            Memory.ClearExclusive(0);
+            CurrentProcess.CpuMemory.ClearExclusive(0);
 
             if (CurrentValue != Value)
             {
-                System.CriticalSectionLock.Unlock();
+                System.CriticalSection.Leave();
 
                 return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
             }
 
             WakeArbiterThreads(Address, Count);
 
-            System.CriticalSectionLock.Unlock();
+            System.CriticalSection.Leave();
 
             return 0;
         }
 
-        public long SignalAndModifyIfEqual(MemoryManager Memory, long Address, int Value, int Count)
+        public long SignalAndModifyIfEqual(long Address, int Value, int Count)
         {
-            System.CriticalSectionLock.Lock();
+            System.CriticalSection.Enter();
 
             int Offset;
 
@@ -580,43 +576,45 @@ namespace Ryujinx.HLE.HOS.Kernel
                 Offset = 1;
             }
 
-            Memory.SetExclusive(0, Address);
+            KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
 
-            if (!UserToKernelInt32(Memory, Address, out int CurrentValue))
+            CurrentProcess.CpuMemory.SetExclusive(0, Address);
+
+            if (!KernelTransfer.UserToKernelInt32(System, Address, out int CurrentValue))
             {
-                System.CriticalSectionLock.Unlock();
+                System.CriticalSection.Leave();
 
                 return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
             }
 
             while (CurrentValue == Value)
             {
-                if (Memory.TestExclusive(0, Address))
+                if (CurrentProcess.CpuMemory.TestExclusive(0, Address))
                 {
-                    Memory.WriteInt32(Address, CurrentValue + Offset);
+                    CurrentProcess.CpuMemory.WriteInt32(Address, CurrentValue + Offset);
 
-                    Memory.ClearExclusiveForStore(0);
+                    CurrentProcess.CpuMemory.ClearExclusiveForStore(0);
 
                     break;
                 }
 
-                Memory.SetExclusive(0, Address);
+                CurrentProcess.CpuMemory.SetExclusive(0, Address);
 
-                CurrentValue = Memory.ReadInt32(Address);
+                CurrentValue = CurrentProcess.CpuMemory.ReadInt32(Address);
             }
 
-            Memory.ClearExclusive(0);
+            CurrentProcess.CpuMemory.ClearExclusive(0);
 
             if (CurrentValue != Value)
             {
-                System.CriticalSectionLock.Unlock();
+                System.CriticalSection.Leave();
 
                 return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
             }
 
             WakeArbiterThreads(Address, Count);
 
-            System.CriticalSectionLock.Unlock();
+            System.CriticalSection.Leave();
 
             return 0;
         }
@@ -648,31 +646,5 @@ namespace Ryujinx.HLE.HOS.Kernel
                 ArbiterThreads.Remove(Thread);
             }
         }
-
-        private bool UserToKernelInt32(MemoryManager Memory, long Address, out int Value)
-        {
-            if (Memory.IsMapped(Address))
-            {
-                Value = Memory.ReadInt32(Address);
-
-                return true;
-            }
-
-            Value = 0;
-
-            return false;
-        }
-
-        private bool KernelToUserInt32(MemoryManager Memory, long Address, int Value)
-        {
-            if (Memory.IsMapped(Address))
-            {
-                Memory.WriteInt32ToSharedAddr(Address, Value);
-
-                return true;
-            }
-
-            return false;
-        }
     }
 }
diff --git a/Ryujinx.HLE/HOS/Kernel/KAutoObject.cs b/Ryujinx.HLE/HOS/Kernel/KAutoObject.cs
new file mode 100644
index 00000000..a91bf9a8
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KAutoObject.cs
@@ -0,0 +1,42 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+    class KAutoObject
+    {
+        protected Horizon System;
+
+        public KAutoObject(Horizon System)
+        {
+            this.System = System;
+        }
+
+        public virtual KernelResult SetName(string Name)
+        {
+            if (!System.AutoObjectNames.TryAdd(Name, this))
+            {
+                return KernelResult.InvalidState;
+            }
+
+            return KernelResult.Success;
+        }
+
+        public static KernelResult RemoveName(Horizon System, string Name)
+        {
+            if (!System.AutoObjectNames.TryRemove(Name, out _))
+            {
+                return KernelResult.NotFound;
+            }
+
+            return KernelResult.Success;
+        }
+
+        public static KAutoObject FindNamedObject(Horizon System, string Name)
+        {
+            if (System.AutoObjectNames.TryGetValue(Name, out KAutoObject Obj))
+            {
+                return Obj;
+            }
+
+            return null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KClientPort.cs b/Ryujinx.HLE/HOS/Kernel/KClientPort.cs
new file mode 100644
index 00000000..e3f8128b
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KClientPort.cs
@@ -0,0 +1,31 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+    class KClientPort : KSynchronizationObject
+    {
+        private int SessionsCount;
+        private int CurrentCapacity;
+        private int MaxSessions;
+
+        private KPort Parent;
+
+        public KClientPort(Horizon System) : base(System) { }
+
+        public void Initialize(KPort Parent, int MaxSessions)
+        {
+            this.MaxSessions = MaxSessions;
+            this.Parent      = Parent;
+        }
+
+        public new static KernelResult RemoveName(Horizon System, string Name)
+        {
+            KAutoObject FoundObj = KAutoObject.FindNamedObject(System, Name);
+
+            if (!(FoundObj is KClientPort))
+            {
+                return KernelResult.NotFound;
+            }
+
+            return KAutoObject.RemoveName(System, Name);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KConditionVariable.cs b/Ryujinx.HLE/HOS/Kernel/KConditionVariable.cs
new file mode 100644
index 00000000..1c95f811
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KConditionVariable.cs
@@ -0,0 +1,71 @@
+using System.Collections.Generic;
+using System.Threading;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+    static class KConditionVariable
+    {
+        public static void Wait(Horizon System, LinkedList<KThread> ThreadList, object Mutex, long Timeout)
+        {
+            KThread CurrentThread = System.Scheduler.GetCurrentThread();
+
+            System.CriticalSection.Enter();
+
+            Monitor.Exit(Mutex);
+
+            CurrentThread.Withholder = ThreadList;
+
+            CurrentThread.Reschedule(ThreadSchedState.Paused);
+
+            CurrentThread.WithholderNode = ThreadList.AddLast(CurrentThread);
+
+            if (CurrentThread.ShallBeTerminated ||
+                CurrentThread.SchedFlags == ThreadSchedState.TerminationPending)
+            {
+                ThreadList.Remove(CurrentThread.WithholderNode);
+
+                CurrentThread.Reschedule(ThreadSchedState.Running);
+
+                CurrentThread.Withholder = null;
+
+                System.CriticalSection.Leave();
+            }
+            else
+            {
+                if (Timeout > 0)
+                {
+                    System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout);
+                }
+
+                System.CriticalSection.Leave();
+
+                if (Timeout > 0)
+                {
+                    System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
+                }
+            }
+
+            Monitor.Enter(Mutex);
+        }
+
+        public static void NotifyAll(Horizon System, LinkedList<KThread> ThreadList)
+        {
+            System.CriticalSection.Enter();
+
+            LinkedListNode<KThread> Node = ThreadList.First;
+
+            for (; Node != null; Node = ThreadList.First)
+            {
+                KThread Thread = Node.Value;
+
+                ThreadList.Remove(Thread.WithholderNode);
+
+                Thread.Withholder = null;
+
+                Thread.Reschedule(ThreadSchedState.Running);
+            }
+
+            System.CriticalSection.Leave();
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KContextIdManager.cs b/Ryujinx.HLE/HOS/Kernel/KContextIdManager.cs
new file mode 100644
index 00000000..03e7dddf
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KContextIdManager.cs
@@ -0,0 +1,83 @@
+using Ryujinx.Common;
+using System;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+    class KContextIdManager
+    {
+        private const int IdMasksCount = 8;
+
+        private int[] IdMasks;
+
+        private int NextFreeBitHint;
+
+        public KContextIdManager()
+        {
+            IdMasks = new int[IdMasksCount];
+        }
+
+        public int GetId()
+        {
+            lock (IdMasks)
+            {
+                int Id = 0;
+
+                if (!TestBit(NextFreeBitHint))
+                {
+                    Id = NextFreeBitHint;
+                }
+                else
+                {
+                    for (int Index = 0; Index < IdMasksCount; Index++)
+                    {
+                        int Mask = IdMasks[Index];
+
+                        int FirstFreeBit = BitUtils.CountLeadingZeros32((Mask + 1) & ~Mask);
+
+                        if (FirstFreeBit < 32)
+                        {
+                            int BaseBit = Index * 32 + 31;
+
+                            Id = BaseBit - FirstFreeBit;
+
+                            break;
+                        }
+                        else if (Index == IdMasksCount - 1)
+                        {
+                            throw new InvalidOperationException("Maximum number of Ids reached!");
+                        }
+                    }
+                }
+
+                NextFreeBitHint = Id + 1;
+
+                SetBit(Id);
+
+                return Id;
+            }
+        }
+
+        public void PutId(int Id)
+        {
+            lock (IdMasks)
+            {
+                ClearBit(Id);
+            }
+        }
+
+        private bool TestBit(int Bit)
+        {
+            return (IdMasks[NextFreeBitHint / 32] & (1 << (NextFreeBitHint & 31))) != 0;
+        }
+
+        private void SetBit(int Bit)
+        {
+            IdMasks[NextFreeBitHint / 32] |= (1 << (NextFreeBitHint & 31));
+        }
+
+        private void ClearBit(int Bit)
+        {
+            IdMasks[NextFreeBitHint / 32] &= ~(1 << (NextFreeBitHint & 31));
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KCoreContext.cs b/Ryujinx.HLE/HOS/Kernel/KCoreContext.cs
index 02354e16..638dde9e 100644
--- a/Ryujinx.HLE/HOS/Kernel/KCoreContext.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KCoreContext.cs
@@ -1,5 +1,4 @@
 using Ryujinx.Common;
-using System;
 
 namespace Ryujinx.HLE.HOS.Kernel
 {
@@ -11,6 +10,10 @@ namespace Ryujinx.HLE.HOS.Kernel
 
         public bool ContextSwitchNeeded { get; private set; }
 
+        public long LastContextSwitchTime { get; private set; }
+
+        public long TotalIdleTimeTicks { get; private set; } //TODO
+
         public KThread CurrentThread  { get; private set; }
         public KThread SelectedThread { get; private set; }
 
@@ -24,11 +27,6 @@ namespace Ryujinx.HLE.HOS.Kernel
         {
             SelectedThread = Thread;
 
-            if (Thread != null)
-            {
-                Thread.LastScheduledTicks = PerformanceCounter.ElapsedMilliseconds;
-            }
-
             if (SelectedThread != CurrentThread)
             {
                 ContextSwitchNeeded = true;
@@ -39,25 +37,42 @@ namespace Ryujinx.HLE.HOS.Kernel
         {
             ContextSwitchNeeded = false;
 
+            LastContextSwitchTime = PerformanceCounter.ElapsedMilliseconds;
+
             CurrentThread = SelectedThread;
+
+            if (CurrentThread != null)
+            {
+                long CurrentTime = PerformanceCounter.ElapsedMilliseconds;
+
+                CurrentThread.TotalTimeRunning += CurrentTime - CurrentThread.LastScheduledTime;
+                CurrentThread.LastScheduledTime = CurrentTime;
+            }
         }
 
         public void ContextSwitch()
         {
             ContextSwitchNeeded = false;
 
+            LastContextSwitchTime = PerformanceCounter.ElapsedMilliseconds;
+
             if (CurrentThread != null)
             {
-                CoreManager.GetThread(CurrentThread.Context.Work).Reset();
+                CoreManager.Reset(CurrentThread.Context.Work);
             }
 
             CurrentThread = SelectedThread;
 
             if (CurrentThread != null)
             {
+                long CurrentTime = PerformanceCounter.ElapsedMilliseconds;
+
+                CurrentThread.TotalTimeRunning += CurrentTime - CurrentThread.LastScheduledTime;
+                CurrentThread.LastScheduledTime = CurrentTime;
+
                 CurrentThread.ClearExclusive();
 
-                CoreManager.GetThread(CurrentThread.Context.Work).Set();
+                CoreManager.Set(CurrentThread.Context.Work);
 
                 CurrentThread.Context.Execute();
             }
diff --git a/Ryujinx.HLE/HOS/Kernel/KRecursiveLock.cs b/Ryujinx.HLE/HOS/Kernel/KCriticalSection.cs
similarity index 95%
rename from Ryujinx.HLE/HOS/Kernel/KRecursiveLock.cs
rename to Ryujinx.HLE/HOS/Kernel/KCriticalSection.cs
index 30c1a880..b02a1195 100644
--- a/Ryujinx.HLE/HOS/Kernel/KRecursiveLock.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KCriticalSection.cs
@@ -3,7 +3,7 @@ using System.Threading;
 
 namespace Ryujinx.HLE.HOS.Kernel
 {
-    class KRecursiveLock
+    class KCriticalSection
     {
         private Horizon System;
 
@@ -11,21 +11,21 @@ namespace Ryujinx.HLE.HOS.Kernel
 
         private int RecursionCount;
 
-        public KRecursiveLock(Horizon System)
+        public KCriticalSection(Horizon System)
         {
             this.System = System;
 
             LockObj = new object();
         }
 
-        public void Lock()
+        public void Enter()
         {
             Monitor.Enter(LockObj);
 
             RecursionCount++;
         }
 
-        public void Unlock()
+        public void Leave()
         {
             if (RecursionCount == 0)
             {
diff --git a/Ryujinx.HLE/HOS/Kernel/KProcessHandleTable.cs b/Ryujinx.HLE/HOS/Kernel/KHandleTable.cs
similarity index 81%
rename from Ryujinx.HLE/HOS/Kernel/KProcessHandleTable.cs
rename to Ryujinx.HLE/HOS/Kernel/KHandleTable.cs
index 682f08d4..e39dfb67 100644
--- a/Ryujinx.HLE/HOS/Kernel/KProcessHandleTable.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KHandleTable.cs
@@ -2,7 +2,7 @@ using System;
 
 namespace Ryujinx.HLE.HOS.Kernel
 {
-    class KProcessHandleTable
+    class KHandleTable
     {
         private const int SelfThreadHandle  = (0x1ffff << 15) | 0;
         private const int SelfProcessHandle = (0x1ffff << 15) | 1;
@@ -20,12 +20,24 @@ namespace Ryujinx.HLE.HOS.Kernel
 
         private ushort IdCounter;
 
-        private object LockObj;
-
-        public KProcessHandleTable(Horizon System, int Size = 1024)
+        public KHandleTable(Horizon System)
         {
             this.System = System;
-            this.Size   = Size;
+        }
+
+        public KernelResult Initialize(int Size)
+        {
+            if ((uint)Size > 1024)
+            {
+                return KernelResult.OutOfMemory;
+            }
+
+            if (Size < 1)
+            {
+                Size = 1024;
+            }
+
+            this.Size = Size;
 
             IdCounter = 1;
 
@@ -48,14 +60,14 @@ namespace Ryujinx.HLE.HOS.Kernel
 
             NextFreeEntry = TableHead;
 
-            LockObj = new object();
+            return KernelResult.Success;
         }
 
         public KernelResult GenerateHandle(object Obj, out int Handle)
         {
             Handle = 0;
 
-            lock (LockObj)
+            lock (Table)
             {
                 if (ActiveSlotsCount >= Size)
                 {
@@ -95,12 +107,12 @@ namespace Ryujinx.HLE.HOS.Kernel
                 return false;
             }
 
-            int Index    = (Handle >>  0) & 0x7fff;
+            int Index    = (Handle >> 0) & 0x7fff;
             int HandleId = (Handle >> 15);
 
             bool Result = false;
 
-            lock (LockObj)
+            lock (Table)
             {
                 if (HandleId != 0 && Index < Size)
                 {
@@ -125,10 +137,10 @@ namespace Ryujinx.HLE.HOS.Kernel
 
         public T GetObject<T>(int Handle)
         {
-            int Index    = (Handle >>  0) & 0x7fff;
+            int Index    = (Handle >> 0) & 0x7fff;
             int HandleId = (Handle >> 15);
 
-            lock (LockObj)
+            lock (Table)
             {
                 if ((Handle >> 30) == 0 && HandleId != 0)
                 {
@@ -156,9 +168,21 @@ namespace Ryujinx.HLE.HOS.Kernel
             }
         }
 
+        public KProcess GetKProcess(int Handle)
+        {
+            if (Handle == SelfProcessHandle)
+            {
+                return System.Scheduler.GetCurrentProcess();
+            }
+            else
+            {
+                return GetObject<KProcess>(Handle);
+            }
+        }
+
         public void Destroy()
         {
-            lock (LockObj)
+            lock (Table)
             {
                 for (int Index = 0; Index < Size; Index++)
                 {
diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryArrange.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryArrange.cs
new file mode 100644
index 00000000..af393b68
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KMemoryArrange.cs
@@ -0,0 +1,22 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+    class KMemoryArrange
+    {
+        public KMemoryArrangeRegion Service     { get; private set; }
+        public KMemoryArrangeRegion NvServices  { get; private set; }
+        public KMemoryArrangeRegion Applet      { get; private set; }
+        public KMemoryArrangeRegion Application { get; private set; }
+
+        public KMemoryArrange(
+            KMemoryArrangeRegion Service,
+            KMemoryArrangeRegion NvServices,
+            KMemoryArrangeRegion Applet,
+            KMemoryArrangeRegion Application)
+        {
+            this.Service     = Service;
+            this.NvServices  = NvServices;
+            this.Applet      = Applet;
+            this.Application = Application;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryArrangeRegion.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryArrangeRegion.cs
new file mode 100644
index 00000000..7d66e291
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KMemoryArrangeRegion.cs
@@ -0,0 +1,16 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+    struct KMemoryArrangeRegion
+    {
+        public ulong Address { get; private set; }
+        public ulong Size    { get; private set; }
+
+        public ulong EndAddr => Address + Size;
+
+        public KMemoryArrangeRegion(ulong Address, ulong Size)
+        {
+            this.Address = Address;
+            this.Size    = Size;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryBlock.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryBlock.cs
index 6100741b..08190236 100644
--- a/Ryujinx.HLE/HOS/Kernel/KMemoryBlock.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KMemoryBlock.cs
@@ -2,8 +2,8 @@ namespace Ryujinx.HLE.HOS.Kernel
 {
     class KMemoryBlock
     {
-        public long BasePosition { get; set; }
-        public long PagesCount   { get; set; }
+        public ulong BaseAddress { get; set; }
+        public ulong PagesCount  { get; set; }
 
         public MemoryState      State      { get; set; }
         public MemoryPermission Permission { get; set; }
@@ -13,25 +13,25 @@ namespace Ryujinx.HLE.HOS.Kernel
         public int DeviceRefCount { get; set; }
 
         public KMemoryBlock(
-            long             BasePosition,
-            long             PagesCount,
+            ulong            BaseAddress,
+            ulong            PagesCount,
             MemoryState      State,
             MemoryPermission Permission,
             MemoryAttribute  Attribute)
         {
-            this.BasePosition = BasePosition;
-            this.PagesCount   = PagesCount;
-            this.State        = State;
-            this.Attribute    = Attribute;
-            this.Permission   = Permission;
+            this.BaseAddress = BaseAddress;
+            this.PagesCount  = PagesCount;
+            this.State       = State;
+            this.Attribute   = Attribute;
+            this.Permission  = Permission;
         }
 
         public KMemoryInfo GetInfo()
         {
-            long Size = PagesCount * KMemoryManager.PageSize;
+            ulong Size = PagesCount * KMemoryManager.PageSize;
 
             return new KMemoryInfo(
-                BasePosition,
+                BaseAddress,
                 Size,
                 State,
                 Permission,
diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryBlockAllocator.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryBlockAllocator.cs
new file mode 100644
index 00000000..08512e12
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KMemoryBlockAllocator.cs
@@ -0,0 +1,19 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+    class KMemoryBlockAllocator
+    {
+        private ulong CapacityElements;
+
+        public int Count { get; set; }
+
+        public KMemoryBlockAllocator(ulong CapacityElements)
+        {
+            this.CapacityElements = CapacityElements;
+        }
+
+        public bool CanAllocate(int Count)
+        {
+            return (ulong)(this.Count + Count) <= CapacityElements;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryInfo.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryInfo.cs
index 9b73b32b..09ba88f2 100644
--- a/Ryujinx.HLE/HOS/Kernel/KMemoryInfo.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KMemoryInfo.cs
@@ -2,8 +2,8 @@ namespace Ryujinx.HLE.HOS.Kernel
 {
     class KMemoryInfo
     {
-        public long Position { get; private set; }
-        public long Size     { get; private set; }
+        public ulong Address { get; private set; }
+        public ulong Size    { get; private set; }
 
         public MemoryState      State      { get; private set; }
         public MemoryPermission Permission { get; private set; }
@@ -13,15 +13,15 @@ namespace Ryujinx.HLE.HOS.Kernel
         public int DeviceRefCount { get; private set; }
 
         public KMemoryInfo(
-            long             Position,
-            long             Size,
+            ulong            Address,
+            ulong            Size,
             MemoryState      State,
             MemoryPermission Permission,
             MemoryAttribute  Attribute,
             int              IpcRefCount,
             int              DeviceRefCount)
         {
-            this.Position       = Position;
+            this.Address        = Address;
             this.Size           = Size;
             this.State          = State;
             this.Attribute      = Attribute;
diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs
index 4cfb2aa2..0aa21e3f 100644
--- a/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs
@@ -1,331 +1,858 @@
 using ChocolArm64.Memory;
-using Ryujinx.HLE.Memory;
+using Ryujinx.Common;
 using System;
 using System.Collections.Generic;
 
-using static Ryujinx.HLE.HOS.ErrorCode;
-
 namespace Ryujinx.HLE.HOS.Kernel
 {
     class KMemoryManager
     {
         public const int PageSize = 0x1000;
 
+        private const int KMemoryBlockSize = 0x40;
+
+        //We need 2 blocks for the case where a big block
+        //needs to be split in 2, plus one block that will be the new one inserted.
+        private const int MaxBlocksNeededForInsertion = 2;
+
         private LinkedList<KMemoryBlock> Blocks;
 
         private MemoryManager CpuMemory;
 
-        private ArenaAllocator Allocator;
+        private Horizon System;
 
-        public long AddrSpaceStart { get; private set; }
-        public long AddrSpaceEnd   { get; private set; }
+        public ulong AddrSpaceStart { get; private set; }
+        public ulong AddrSpaceEnd   { get; private set; }
 
-        public long CodeRegionStart { get; private set; }
-        public long CodeRegionEnd   { get; private set; }
+        public ulong CodeRegionStart { get; private set; }
+        public ulong CodeRegionEnd   { get; private set; }
 
-        public long MapRegionStart { get; private set; }
-        public long MapRegionEnd   { get; private set; }
+        public ulong HeapRegionStart { get; private set; }
+        public ulong HeapRegionEnd   { get; private set; }
 
-        public long HeapRegionStart { get; private set; }
-        public long HeapRegionEnd   { get; private set; }
+        private ulong CurrentHeapAddr;
 
-        public long NewMapRegionStart { get; private set; }
-        public long NewMapRegionEnd   { get; private set; }
+        public ulong AliasRegionStart { get; private set; }
+        public ulong AliasRegionEnd   { get; private set; }
 
-        public long TlsIoRegionStart { get; private set; }
-        public long TlsIoRegionEnd   { get; private set; }
+        public ulong StackRegionStart { get; private set; }
+        public ulong StackRegionEnd   { get; private set; }
 
-        public long PersonalMmHeapUsage { get; private set; }
+        public ulong TlsIoRegionStart { get; private set; }
+        public ulong TlsIoRegionEnd   { get; private set; }
 
-        private long CurrentHeapAddr;
+        private ulong HeapCapacity;
 
-        public KMemoryManager(Process Process)
+        public ulong PhysicalMemoryUsage { get; private set; }
+
+        private MemoryRegion MemRegion;
+
+        private bool AslrDisabled;
+
+        public int AddrSpaceWidth { get; private set; }
+
+        private bool IsKernel;
+        private bool AslrEnabled;
+
+        private KMemoryBlockAllocator BlockAllocator;
+
+        private int ContextId;
+
+        private MersenneTwister RandomNumberGenerator;
+
+        public KMemoryManager(Horizon System, MemoryManager CpuMemory)
         {
-            CpuMemory = Process.Memory;
-            Allocator = Process.Device.Memory.Allocator;
+            this.System    = System;
+            this.CpuMemory = CpuMemory;
 
-            long CodeRegionSize;
-            long MapRegionSize;
-            long HeapRegionSize;
-            long NewMapRegionSize;
-            long TlsIoRegionSize;
-            int  AddrSpaceWidth;
+            Blocks = new LinkedList<KMemoryBlock>();
+        }
 
-            AddressSpaceType AddrType = AddressSpaceType.Addr39Bits;
+        private static readonly int[] AddrSpaceSizes = new int[] { 32, 36, 32, 39 };
 
-            if (Process.MetaData != null)
+        public KernelResult InitializeForProcess(
+            AddressSpaceType      AddrSpaceType,
+            bool                  AslrEnabled,
+            bool                  AslrDisabled,
+            MemoryRegion          MemRegion,
+            ulong                 Address,
+            ulong                 Size,
+            KMemoryBlockAllocator BlockAllocator)
+        {
+            if ((uint)AddrSpaceType > (uint)AddressSpaceType.Addr39Bits)
             {
-                AddrType = (AddressSpaceType)Process.MetaData.AddressSpaceWidth;
+                throw new ArgumentException(nameof(AddrSpaceType));
             }
 
-            switch (AddrType)
+            ContextId = System.ContextIdManager.GetId();
+
+            ulong AddrSpaceBase = 0;
+            ulong AddrSpaceSize = 1UL << AddrSpaceSizes[(int)AddrSpaceType];
+
+            KernelResult Result = CreateUserAddressSpace(
+                AddrSpaceType,
+                AslrEnabled,
+                AslrDisabled,
+                AddrSpaceBase,
+                AddrSpaceSize,
+                MemRegion,
+                Address,
+                Size,
+                BlockAllocator);
+
+            if (Result != KernelResult.Success)
+            {
+                System.ContextIdManager.PutId(ContextId);
+            }
+
+            return Result;
+        }
+
+        private class Region
+        {
+            public ulong Start;
+            public ulong End;
+            public ulong Size;
+            public ulong AslrOffset;
+        }
+
+        private KernelResult CreateUserAddressSpace(
+            AddressSpaceType      AddrSpaceType,
+            bool                  AslrEnabled,
+            bool                  AslrDisabled,
+            ulong                 AddrSpaceStart,
+            ulong                 AddrSpaceEnd,
+            MemoryRegion          MemRegion,
+            ulong                 Address,
+            ulong                 Size,
+            KMemoryBlockAllocator BlockAllocator)
+        {
+            ulong EndAddr = Address + Size;
+
+            Region AliasRegion = new Region();
+            Region HeapRegion  = new Region();
+            Region StackRegion = new Region();
+            Region TlsIoRegion = new Region();
+
+            ulong CodeRegionSize;
+            ulong StackAndTlsIoStart;
+            ulong StackAndTlsIoEnd;
+            ulong BaseAddress;
+
+            switch (AddrSpaceType)
             {
                 case AddressSpaceType.Addr32Bits:
-                    CodeRegionStart  = 0x200000;
-                    CodeRegionSize   = 0x3fe00000;
-                    MapRegionSize    = 0x40000000;
-                    HeapRegionSize   = 0x40000000;
-                    NewMapRegionSize = 0;
-                    TlsIoRegionSize  = 0;
-                    AddrSpaceWidth   = 32;
+                    AliasRegion.Size   = 0x40000000;
+                    HeapRegion.Size    = 0x40000000;
+                    StackRegion.Size   = 0;
+                    TlsIoRegion.Size   = 0;
+                    CodeRegionStart    = 0x200000;
+                    CodeRegionSize     = 0x3fe00000;
+                    StackAndTlsIoStart = 0x200000;
+                    StackAndTlsIoEnd   = 0x40000000;
+                    BaseAddress        = 0x200000;
+                    AddrSpaceWidth     = 32;
                     break;
 
                 case AddressSpaceType.Addr36Bits:
-                    CodeRegionStart  = 0x8000000;
-                    CodeRegionSize   = 0x78000000;
-                    MapRegionSize    = 0x180000000;
-                    HeapRegionSize   = 0x180000000;
-                    NewMapRegionSize = 0;
-                    TlsIoRegionSize  = 0;
-                    AddrSpaceWidth   = 36;
+                    AliasRegion.Size   = 0x180000000;
+                    HeapRegion.Size    = 0x180000000;
+                    StackRegion.Size   = 0;
+                    TlsIoRegion.Size   = 0;
+                    CodeRegionStart    = 0x8000000;
+                    CodeRegionSize     = 0x78000000;
+                    StackAndTlsIoStart = 0x8000000;
+                    StackAndTlsIoEnd   = 0x80000000;
+                    BaseAddress        = 0x8000000;
+                    AddrSpaceWidth     = 36;
                     break;
 
-                case AddressSpaceType.Addr36BitsNoMap:
-                    CodeRegionStart  = 0x200000;
-                    CodeRegionSize   = 0x3fe00000;
-                    MapRegionSize    = 0;
-                    HeapRegionSize   = 0x80000000;
-                    NewMapRegionSize = 0;
-                    TlsIoRegionSize  = 0;
-                    AddrSpaceWidth   = 36;
+                case AddressSpaceType.Addr32BitsNoMap:
+                    AliasRegion.Size   = 0;
+                    HeapRegion.Size    = 0x80000000;
+                    StackRegion.Size   = 0;
+                    TlsIoRegion.Size   = 0;
+                    CodeRegionStart    = 0x200000;
+                    CodeRegionSize     = 0x3fe00000;
+                    StackAndTlsIoStart = 0x200000;
+                    StackAndTlsIoEnd   = 0x40000000;
+                    BaseAddress        = 0x200000;
+                    AddrSpaceWidth     = 32;
                     break;
 
                 case AddressSpaceType.Addr39Bits:
-                    CodeRegionStart  = 0x8000000;
-                    CodeRegionSize   = 0x80000000;
-                    MapRegionSize    = 0x1000000000;
-                    HeapRegionSize   = 0x180000000;
-                    NewMapRegionSize = 0x80000000;
-                    TlsIoRegionSize  = 0x1000000000;
-                    AddrSpaceWidth   = 39;
+                    AliasRegion.Size   = 0x1000000000;
+                    HeapRegion.Size    = 0x180000000;
+                    StackRegion.Size   = 0x80000000;
+                    TlsIoRegion.Size   = 0x1000000000;
+                    CodeRegionStart    = BitUtils.AlignDown(Address, 0x200000);
+                    CodeRegionSize     = BitUtils.AlignUp  (EndAddr, 0x200000) - CodeRegionStart;
+                    StackAndTlsIoStart = 0;
+                    StackAndTlsIoEnd   = 0;
+                    BaseAddress        = 0x8000000;
+                    AddrSpaceWidth     = 39;
                     break;
 
-                default: throw new InvalidOperationException();
+                default: throw new ArgumentException(nameof(AddrSpaceType));
             }
 
-            AddrSpaceStart = 0;
-            AddrSpaceEnd   = 1L << AddrSpaceWidth;
+            CodeRegionEnd = CodeRegionStart + CodeRegionSize;
 
-            CodeRegionEnd     = CodeRegionStart + CodeRegionSize;
-            MapRegionStart    = CodeRegionEnd;
-            MapRegionEnd      = CodeRegionEnd   + MapRegionSize;
-            HeapRegionStart   = MapRegionEnd;
-            HeapRegionEnd     = MapRegionEnd    + HeapRegionSize;
-            NewMapRegionStart = HeapRegionEnd;
-            NewMapRegionEnd   = HeapRegionEnd   + NewMapRegionSize;
-            TlsIoRegionStart  = NewMapRegionEnd;
-            TlsIoRegionEnd    = NewMapRegionEnd + TlsIoRegionSize;
+            ulong MapBaseAddress;
+            ulong MapAvailableSize;
 
-            CurrentHeapAddr = HeapRegionStart;
-
-            if (NewMapRegionSize == 0)
+            if (CodeRegionStart - BaseAddress >= AddrSpaceEnd - CodeRegionEnd)
             {
-                NewMapRegionStart = AddrSpaceStart;
-                NewMapRegionEnd   = AddrSpaceEnd;
+                //Has more space before the start of the code region.
+                MapBaseAddress   = BaseAddress;
+                MapAvailableSize = CodeRegionStart - BaseAddress;
+            }
+            else
+            {
+                //Has more space after the end of the code region.
+                MapBaseAddress   = CodeRegionEnd;
+                MapAvailableSize = AddrSpaceEnd - CodeRegionEnd;
             }
 
-            Blocks = new LinkedList<KMemoryBlock>();
+            ulong MapTotalSize = AliasRegion.Size + HeapRegion.Size + StackRegion.Size + TlsIoRegion.Size;
 
-            long AddrSpacePagesCount = (AddrSpaceEnd - AddrSpaceStart) / PageSize;
+            ulong AslrMaxOffset = MapAvailableSize - MapTotalSize;
+
+            this.AslrEnabled = AslrEnabled;
+
+            this.AddrSpaceStart = AddrSpaceStart;
+            this.AddrSpaceEnd   = AddrSpaceEnd;
+
+            this.BlockAllocator = BlockAllocator;
+
+            if (MapAvailableSize < MapTotalSize)
+            {
+                return KernelResult.OutOfMemory;
+            }
+
+            if (AslrEnabled)
+            {
+                AliasRegion.AslrOffset = GetRandomValue(0, AslrMaxOffset >> 21) << 21;
+                HeapRegion.AslrOffset  = GetRandomValue(0, AslrMaxOffset >> 21) << 21;
+                StackRegion.AslrOffset = GetRandomValue(0, AslrMaxOffset >> 21) << 21;
+                TlsIoRegion.AslrOffset = GetRandomValue(0, AslrMaxOffset >> 21) << 21;
+            }
+
+            //Regions are sorted based on ASLR offset.
+            //When ASLR is disabled, the order is Map, Heap, NewMap and TlsIo.
+            AliasRegion.Start = MapBaseAddress    + AliasRegion.AslrOffset;
+            AliasRegion.End   = AliasRegion.Start + AliasRegion.Size;
+            HeapRegion.Start  = MapBaseAddress    + HeapRegion.AslrOffset;
+            HeapRegion.End    = HeapRegion.Start  + HeapRegion.Size;
+            StackRegion.Start = MapBaseAddress    + StackRegion.AslrOffset;
+            StackRegion.End   = StackRegion.Start + StackRegion.Size;
+            TlsIoRegion.Start = MapBaseAddress    + TlsIoRegion.AslrOffset;
+            TlsIoRegion.End   = TlsIoRegion.Start + TlsIoRegion.Size;
+
+            SortRegion(HeapRegion, AliasRegion);
+
+            if (StackRegion.Size != 0)
+            {
+                SortRegion(StackRegion, AliasRegion);
+                SortRegion(StackRegion, HeapRegion);
+            }
+            else
+            {
+                StackRegion.Start = StackAndTlsIoStart;
+                StackRegion.End   = StackAndTlsIoEnd;
+            }
+
+            if (TlsIoRegion.Size != 0)
+            {
+                SortRegion(TlsIoRegion, AliasRegion);
+                SortRegion(TlsIoRegion, HeapRegion);
+                SortRegion(TlsIoRegion, StackRegion);
+            }
+            else
+            {
+                TlsIoRegion.Start = StackAndTlsIoStart;
+                TlsIoRegion.End   = StackAndTlsIoEnd;
+            }
+
+            AliasRegionStart = AliasRegion.Start;
+            AliasRegionEnd   = AliasRegion.End;
+            HeapRegionStart  = HeapRegion.Start;
+            HeapRegionEnd    = HeapRegion.End;
+            StackRegionStart = StackRegion.Start;
+            StackRegionEnd   = StackRegion.End;
+            TlsIoRegionStart = TlsIoRegion.Start;
+            TlsIoRegionEnd   = TlsIoRegion.End;
+
+            CurrentHeapAddr     = HeapRegionStart;
+            HeapCapacity        = 0;
+            PhysicalMemoryUsage = 0;
+
+            this.MemRegion    = MemRegion;
+            this.AslrDisabled = AslrDisabled;
+
+            return InitializeBlocks(AddrSpaceStart, AddrSpaceEnd);
+        }
+
+        private ulong GetRandomValue(ulong Min, ulong Max)
+        {
+            return (ulong)GetRandomValue((long)Min, (long)Max);
+        }
+
+        private long GetRandomValue(long Min, long Max)
+        {
+            if (RandomNumberGenerator == null)
+            {
+                RandomNumberGenerator = new MersenneTwister(0);
+            }
+
+            return RandomNumberGenerator.GenRandomNumber(Min, Max);
+        }
+
+        private static void SortRegion(Region Lhs, Region Rhs)
+        {
+            if (Lhs.AslrOffset < Rhs.AslrOffset)
+            {
+                Rhs.Start += Lhs.Size;
+                Rhs.End   += Lhs.Size;
+            }
+            else
+            {
+                Lhs.Start += Rhs.Size;
+                Lhs.End   += Rhs.Size;
+            }
+        }
+
+        private KernelResult InitializeBlocks(ulong AddrSpaceStart, ulong AddrSpaceEnd)
+        {
+            //First insertion will always need only a single block,
+            //because there's nothing else to split.
+            if (!BlockAllocator.CanAllocate(1))
+            {
+                return KernelResult.OutOfResource;
+            }
+
+            ulong AddrSpacePagesCount = (AddrSpaceEnd - AddrSpaceStart) / PageSize;
 
             InsertBlock(AddrSpaceStart, AddrSpacePagesCount, MemoryState.Unmapped);
+
+            return KernelResult.Success;
         }
 
-        public void HleMapProcessCode(long Position, long Size)
+        public KernelResult MapPages(
+            ulong            Address,
+            KPageList        PageList,
+            MemoryState      State,
+            MemoryPermission Permission)
         {
-            long PagesCount = Size / PageSize;
+            ulong PagesCount = PageList.GetPagesCount();
 
-            if (!Allocator.TryAllocate(Size, out long PA))
+            ulong Size = PagesCount * PageSize;
+
+            if (!ValidateRegionForState(Address, Size, State))
             {
-                throw new InvalidOperationException();
+                return KernelResult.InvalidMemState;
             }
 
             lock (Blocks)
             {
-                InsertBlock(Position, PagesCount, MemoryState.CodeStatic, MemoryPermission.ReadAndExecute);
-
-                CpuMemory.Map(Position, PA, Size);
-            }
-        }
-
-        public long MapProcessCodeMemory(long Dst, long Src, long Size)
-        {
-            lock (Blocks)
-            {
-                long PagesCount = Size / PageSize;
-
-                bool Success = IsUnmapped(Dst, Size);
-
-                Success &= CheckRange(
-                            Src,
-                            Size,
-                            MemoryState.Mask,
-                            MemoryState.Heap,
-                            MemoryPermission.Mask,
-                            MemoryPermission.ReadAndWrite,
-                            MemoryAttribute.Mask,
-                            MemoryAttribute.None,
-                            MemoryAttribute.IpcAndDeviceMapped,
-                            out _,
-                            out _,
-                            out _);
-
-                if (Success)
+                if (!IsUnmapped(Address, PagesCount * PageSize))
                 {
-                    long PA = CpuMemory.GetPhysicalAddress(Src);
+                    return KernelResult.InvalidMemState;
+                }
 
-                    InsertBlock(Dst, PagesCount, MemoryState.CodeStatic, MemoryPermission.ReadAndExecute);
-                    InsertBlock(Src, PagesCount, MemoryState.Heap, MemoryPermission.None);
+                if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+                {
+                    return KernelResult.OutOfResource;
+                }
 
-                    CpuMemory.Map(Dst, PA, Size);
+                KernelResult Result = MapPages(Address, PageList, Permission);
 
-                    return 0;
+                if (Result == KernelResult.Success)
+                {
+                    InsertBlock(Address, PagesCount, State, Permission);
+                }
+
+                return Result;
+            }
+        }
+
+        public KernelResult UnmapPages(ulong Address, KPageList PageList, MemoryState StateExpected)
+        {
+            ulong PagesCount = PageList.GetPagesCount();
+
+            ulong Size = PagesCount * PageSize;
+
+            ulong EndAddr = Address + Size;
+
+            ulong AddrSpacePagesCount = (AddrSpaceEnd - AddrSpaceStart) / PageSize;
+
+            if (AddrSpaceStart > Address)
+            {
+                return KernelResult.InvalidMemState;
+            }
+
+            if (AddrSpacePagesCount < PagesCount)
+            {
+                return KernelResult.InvalidMemState;
+            }
+
+            if (EndAddr - 1 > AddrSpaceEnd - 1)
+            {
+                return KernelResult.InvalidMemState;
+            }
+
+            lock (Blocks)
+            {
+                KPageList CurrentPageList = new KPageList();
+
+                AddVaRangeToPageList(CurrentPageList, Address, PagesCount);
+
+                if (!CurrentPageList.IsEqual(PageList))
+                {
+                    return KernelResult.InvalidMemRange;
+                }
+
+                if (CheckRange(
+                    Address,
+                    Size,
+                    MemoryState.Mask,
+                    StateExpected,
+                    MemoryPermission.None,
+                    MemoryPermission.None,
+                    MemoryAttribute.Mask,
+                    MemoryAttribute.None,
+                    MemoryAttribute.IpcAndDeviceMapped,
+                    out MemoryState State,
+                    out _,
+                    out _))
+                {
+                    if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+                    {
+                        return KernelResult.OutOfResource;
+                    }
+
+                    KernelResult Result = MmuUnmap(Address, PagesCount);
+
+                    if (Result == KernelResult.Success)
+                    {
+                        InsertBlock(Address, PagesCount, MemoryState.Unmapped);
+                    }
+
+                    return Result;
+                }
+                else
+                {
+                    return KernelResult.InvalidMemState;
                 }
             }
-
-            return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
         }
 
-        public long UnmapProcessCodeMemory(long Dst, long Src, long Size)
+        public KernelResult MapNormalMemory(long Address, long Size, MemoryPermission Permission)
         {
+            //TODO.
+            return KernelResult.Success;
+        }
+
+        public KernelResult MapIoMemory(long Address, long Size, MemoryPermission Permission)
+        {
+            //TODO.
+            return KernelResult.Success;
+        }
+
+        public KernelResult AllocateOrMapPa(
+            ulong            NeededPagesCount,
+            int              Alignment,
+            ulong            SrcPa,
+            bool             Map,
+            ulong            RegionStart,
+            ulong            RegionPagesCount,
+            MemoryState      State,
+            MemoryPermission Permission,
+            out ulong        Address)
+        {
+            Address = 0;
+
+            ulong RegionSize = RegionPagesCount * PageSize;
+
+            ulong RegionEndAddr = RegionStart + RegionSize;
+
+            if (!ValidateRegionForState(RegionStart, RegionSize, State))
+            {
+                return KernelResult.InvalidMemState;
+            }
+
+            if (RegionPagesCount <= NeededPagesCount)
+            {
+                return KernelResult.OutOfMemory;
+            }
+
+            ulong ReservedPagesCount = IsKernel ? 1UL : 4UL;
+
             lock (Blocks)
             {
-                long PagesCount = Size / PageSize;
+                if (AslrEnabled)
+                {
+                    ulong TotalNeededSize = (ReservedPagesCount + NeededPagesCount) * PageSize;
 
+                    ulong RemainingPages = RegionPagesCount - NeededPagesCount;
+
+                    ulong AslrMaxOffset = ((RemainingPages + ReservedPagesCount) * PageSize) / (ulong)Alignment;
+
+                    for (int Attempt = 0; Attempt < 8; Attempt++)
+                    {
+                        Address = BitUtils.AlignDown(RegionStart + GetRandomValue(0, AslrMaxOffset) * (ulong)Alignment, Alignment);
+
+                        ulong EndAddr = Address + TotalNeededSize;
+
+                        KMemoryInfo Info = FindBlock(Address).GetInfo();
+
+                        if (Info.State != MemoryState.Unmapped)
+                        {
+                            continue;
+                        }
+
+                        ulong CurrBaseAddr = Info.Address + ReservedPagesCount * PageSize;
+                        ulong CurrEndAddr  = Info.Address + Info.Size;
+
+                        if (Address     >= RegionStart       &&
+                            Address     >= CurrBaseAddr      &&
+                            EndAddr - 1 <= RegionEndAddr - 1 &&
+                            EndAddr - 1 <= CurrEndAddr   - 1)
+                        {
+                            break;
+                        }
+                    }
+
+                    if (Address == 0)
+                    {
+                        ulong AslrPage = GetRandomValue(0, AslrMaxOffset);
+
+                        Address = FindFirstFit(
+                            RegionStart      + AslrPage * PageSize,
+                            RegionPagesCount - AslrPage,
+                            NeededPagesCount,
+                            Alignment,
+                            0,
+                            ReservedPagesCount);
+                    }
+                }
+
+                if (Address == 0)
+                {
+                    Address = FindFirstFit(
+                        RegionStart,
+                        RegionPagesCount,
+                        NeededPagesCount,
+                        Alignment,
+                        0,
+                        ReservedPagesCount);
+                }
+
+                if (Address == 0)
+                {
+                    return KernelResult.OutOfMemory;
+                }
+
+                if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+                {
+                    return KernelResult.OutOfResource;
+                }
+
+                MemoryOperation Operation = Map
+                    ? MemoryOperation.MapPa
+                    : MemoryOperation.Allocate;
+
+                KernelResult Result = DoMmuOperation(
+                    Address,
+                    NeededPagesCount,
+                    SrcPa,
+                    Map,
+                    Permission,
+                    Operation);
+
+                if (Result != KernelResult.Success)
+                {
+                    return Result;
+                }
+
+                InsertBlock(Address, NeededPagesCount, State, Permission);
+            }
+
+            return KernelResult.Success;
+        }
+
+        public KernelResult MapNewProcessCode(
+            ulong            Address,
+            ulong            PagesCount,
+            MemoryState      State,
+            MemoryPermission Permission)
+        {
+            ulong Size = PagesCount * PageSize;
+
+            if (!ValidateRegionForState(Address, Size, State))
+            {
+                return KernelResult.InvalidMemState;
+            }
+
+            lock (Blocks)
+            {
+                if (!IsUnmapped(Address, Size))
+                {
+                    return KernelResult.InvalidMemState;
+                }
+
+                if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+                {
+                    return KernelResult.OutOfResource;
+                }
+
+                KernelResult Result = DoMmuOperation(
+                    Address,
+                    PagesCount,
+                    0,
+                    false,
+                    Permission,
+                    MemoryOperation.Allocate);
+
+                if (Result == KernelResult.Success)
+                {
+                    InsertBlock(Address, PagesCount, State, Permission);
+                }
+
+                return Result;
+            }
+        }
+
+        public KernelResult MapProcessCodeMemory(ulong Dst, ulong Src, ulong Size)
+        {
+            ulong PagesCount = Size / PageSize;
+
+            lock (Blocks)
+            {
                 bool Success = CheckRange(
-                            Dst,
-                            Size,
-                            MemoryState.Mask,
-                            MemoryState.CodeStatic,
-                            MemoryPermission.None,
-                            MemoryPermission.None,
-                            MemoryAttribute.Mask,
-                            MemoryAttribute.None,
-                            MemoryAttribute.IpcAndDeviceMapped,
-                            out _,
-                            out _,
-                            out _);
+                    Src,
+                    Size,
+                    MemoryState.Mask,
+                    MemoryState.Heap,
+                    MemoryPermission.Mask,
+                    MemoryPermission.ReadAndWrite,
+                    MemoryAttribute.Mask,
+                    MemoryAttribute.None,
+                    MemoryAttribute.IpcAndDeviceMapped,
+                    out MemoryState      State,
+                    out MemoryPermission Permission,
+                    out _);
 
-                Success &= CheckRange(
-                            Src,
-                            Size,
-                            MemoryState.Mask,
-                            MemoryState.Heap,
-                            MemoryPermission.Mask,
-                            MemoryPermission.None,
-                            MemoryAttribute.Mask,
-                            MemoryAttribute.None,
-                            MemoryAttribute.IpcAndDeviceMapped,
-                            out _,
-                            out _,
-                            out _);
+                Success &= IsUnmapped(Dst, Size);
 
                 if (Success)
                 {
+                    if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion * 2))
+                    {
+                        return KernelResult.OutOfResource;
+                    }
+
+                    KPageList PageList = new KPageList();
+
+                    AddVaRangeToPageList(PageList, Src, PagesCount);
+
+                    KernelResult Result = MmuChangePermission(Src, PagesCount, MemoryPermission.None);
+
+                    if (Result != KernelResult.Success)
+                    {
+                        return Result;
+                    }
+
+                    Result = MapPages(Dst, PageList, MemoryPermission.None);
+
+                    if (Result != KernelResult.Success)
+                    {
+                        MmuChangePermission(Src, PagesCount, Permission);
+
+                        return Result;
+                    }
+
+                    InsertBlock(Src, PagesCount, State, MemoryPermission.None, MemoryAttribute.Borrowed);
+                    InsertBlock(Dst, PagesCount, MemoryState.ModCodeStatic);
+
+                    return KernelResult.Success;
+                }
+                else
+                {
+                    return KernelResult.InvalidMemState;
+                }
+            }
+        }
+
+        public KernelResult UnmapProcessCodeMemory(ulong Dst, ulong Src, ulong Size)
+        {
+            ulong PagesCount = Size / PageSize;
+
+            lock (Blocks)
+            {
+                bool Success = CheckRange(
+                    Src,
+                    Size,
+                    MemoryState.Mask,
+                    MemoryState.Heap,
+                    MemoryPermission.None,
+                    MemoryPermission.None,
+                    MemoryAttribute.Mask,
+                    MemoryAttribute.Borrowed,
+                    MemoryAttribute.IpcAndDeviceMapped,
+                    out _,
+                    out _,
+                    out _);
+
+                Success &= CheckRange(
+                    Dst,
+                    PageSize,
+                    MemoryState.UnmapProcessCodeMemoryAllowed,
+                    MemoryState.UnmapProcessCodeMemoryAllowed,
+                    MemoryPermission.None,
+                    MemoryPermission.None,
+                    MemoryAttribute.Mask,
+                    MemoryAttribute.None,
+                    MemoryAttribute.IpcAndDeviceMapped,
+                    out MemoryState State,
+                    out _,
+                    out _);
+
+                Success &= CheckRange(
+                    Dst,
+                    Size,
+                    MemoryState.Mask,
+                    State,
+                    MemoryPermission.None,
+                    MemoryPermission.None,
+                    MemoryAttribute.Mask,
+                    MemoryAttribute.None);
+
+                if (Success)
+                {
+                    KernelResult Result = MmuUnmap(Dst, PagesCount);
+
+                    if (Result != KernelResult.Success)
+                    {
+                        return Result;
+                    }
+
+                    //TODO: Missing some checks here.
+
+                    if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion * 2))
+                    {
+                        return KernelResult.OutOfResource;
+                    }
+
                     InsertBlock(Dst, PagesCount, MemoryState.Unmapped);
                     InsertBlock(Src, PagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite);
 
-                    CpuMemory.Unmap(Dst, Size);
-
-                    return 0;
+                    return KernelResult.Success;
                 }
-            }
-
-            return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
-        }
-
-        public void HleMapCustom(long Position, long Size, MemoryState State, MemoryPermission Permission)
-        {
-            long PagesCount = Size / PageSize;
-
-            if (!Allocator.TryAllocate(Size, out long PA))
-            {
-                throw new InvalidOperationException();
-            }
-
-            lock (Blocks)
-            {
-                InsertBlock(Position, PagesCount, State, Permission);
-
-                CpuMemory.Map(Position, PA, Size);
-            }
-        }
-
-        public long HleMapTlsPage()
-        {
-            bool HasTlsIoRegion = TlsIoRegionStart != TlsIoRegionEnd;
-
-            long Position = HasTlsIoRegion ? TlsIoRegionStart : CodeRegionStart;
-
-            lock (Blocks)
-            {
-                while (Position < (HasTlsIoRegion ? TlsIoRegionEnd : CodeRegionEnd))
+                else
                 {
-                    if (FindBlock(Position).State == MemoryState.Unmapped)
-                    {
-                        InsertBlock(Position, 1, MemoryState.ThreadLocal, MemoryPermission.ReadAndWrite);
-
-                        if (!Allocator.TryAllocate(PageSize, out long PA))
-                        {
-                            throw new InvalidOperationException();
-                        }
-
-                        CpuMemory.Map(Position, PA, PageSize);
-
-                        return Position;
-                    }
-
-                    Position += PageSize;
+                    return KernelResult.InvalidMemState;
                 }
-
-                throw new InvalidOperationException();
             }
         }
 
-        public long TrySetHeapSize(long Size, out long Position)
+        public KernelResult SetHeapSize(ulong Size, out ulong Address)
         {
-            Position = 0;
+            Address = 0;
 
-            if ((ulong)Size > (ulong)(HeapRegionEnd - HeapRegionStart))
+            if (Size > HeapRegionEnd - HeapRegionStart)
             {
-                return MakeError(ErrorModule.Kernel, KernelErr.OutOfMemory);
+                return KernelResult.OutOfMemory;
             }
 
-            bool Success = false;
+            KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
 
-            long CurrentHeapSize = GetHeapSize();
+            ulong CurrentHeapSize = GetHeapSize();
 
-            if ((ulong)CurrentHeapSize <= (ulong)Size)
+            if (CurrentHeapSize <= Size)
             {
                 //Expand.
-                long DiffSize = Size - CurrentHeapSize;
+                ulong DiffSize = Size - CurrentHeapSize;
 
                 lock (Blocks)
                 {
-                    if (Success = IsUnmapped(CurrentHeapAddr, DiffSize))
+                    if (CurrentProcess.ResourceLimit != null && DiffSize != 0 &&
+                       !CurrentProcess.ResourceLimit.Reserve(LimitableResource.Memory, DiffSize))
                     {
-                        if (!Allocator.TryAllocate(DiffSize, out long PA))
+                        return KernelResult.ResLimitExceeded;
+                    }
+
+                    ulong PagesCount = DiffSize / PageSize;
+
+                    KMemoryRegionManager Region = GetMemoryRegionManager();
+
+                    KernelResult Result = Region.AllocatePages(PagesCount, AslrDisabled, out KPageList PageList);
+
+                    void CleanUpForError()
+                    {
+                        if (PageList != null)
                         {
-                            return MakeError(ErrorModule.Kernel, KernelErr.OutOfMemory);
+                            Region.FreePages(PageList);
                         }
 
-                        long PagesCount = DiffSize / PageSize;
-
-                        InsertBlock(CurrentHeapAddr, PagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite);
-
-                        CpuMemory.Map(CurrentHeapAddr, PA, DiffSize);
+                        if (CurrentProcess.ResourceLimit != null && DiffSize != 0)
+                        {
+                            CurrentProcess.ResourceLimit.Release(LimitableResource.Memory, DiffSize);
+                        }
                     }
+
+                    if (Result != KernelResult.Success)
+                    {
+                        CleanUpForError();
+
+                        return Result;
+                    }
+
+                    if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+                    {
+                        CleanUpForError();
+
+                        return KernelResult.OutOfResource;
+                    }
+
+                    if (!IsUnmapped(CurrentHeapAddr, DiffSize))
+                    {
+                        CleanUpForError();
+
+                        return KernelResult.InvalidMemState;
+                    }
+
+                    Result = DoMmuOperation(
+                        CurrentHeapAddr,
+                        PagesCount,
+                        PageList,
+                        MemoryPermission.ReadAndWrite,
+                        MemoryOperation.MapVa);
+
+                    if (Result != KernelResult.Success)
+                    {
+                        CleanUpForError();
+
+                        return Result;
+                    }
+
+                    InsertBlock(CurrentHeapAddr, PagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite);
                 }
             }
             else
             {
                 //Shrink.
-                long FreeAddr = HeapRegionStart + Size;
-                long DiffSize = CurrentHeapSize - Size;
+                ulong FreeAddr = HeapRegionStart + Size;
+                ulong DiffSize = CurrentHeapSize - Size;
 
                 lock (Blocks)
                 {
-                    Success = CheckRange(
+                    if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+                    {
+                        return KernelResult.OutOfResource;
+                    }
+
+                    if (!CheckRange(
                         FreeAddr,
                         DiffSize,
                         MemoryState.Mask,
@@ -337,48 +864,66 @@ namespace Ryujinx.HLE.HOS.Kernel
                         MemoryAttribute.IpcAndDeviceMapped,
                         out _,
                         out _,
-                        out _);
-
-                    if (Success)
+                        out _))
                     {
-                        long PagesCount = DiffSize / PageSize;
-
-                        InsertBlock(FreeAddr, PagesCount, MemoryState.Unmapped);
-
-                        FreePages(FreeAddr, PagesCount);
-
-                        CpuMemory.Unmap(FreeAddr, DiffSize);
+                        return KernelResult.InvalidMemState;
                     }
+
+                    ulong PagesCount = DiffSize / PageSize;
+
+                    KernelResult Result = MmuUnmap(FreeAddr, PagesCount);
+
+                    if (Result != KernelResult.Success)
+                    {
+                        return Result;
+                    }
+
+                    CurrentProcess.ResourceLimit?.Release(LimitableResource.Memory, BitUtils.AlignDown(DiffSize, PageSize));
+
+                    InsertBlock(FreeAddr, PagesCount, MemoryState.Unmapped);
                 }
             }
 
             CurrentHeapAddr = HeapRegionStart + Size;
 
-            if (Success)
-            {
-                Position = HeapRegionStart;
+            Address = HeapRegionStart;
 
-                return 0;
-            }
-
-            return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
+            return KernelResult.Success;
         }
 
-        public long GetHeapSize()
+        public ulong GetTotalHeapSize()
+        {
+            lock (Blocks)
+            {
+                return GetHeapSize() + PhysicalMemoryUsage;
+            }
+        }
+
+        private ulong GetHeapSize()
         {
             return CurrentHeapAddr - HeapRegionStart;
         }
 
-        public long SetMemoryAttribute(
-            long            Position,
-            long            Size,
+        public KernelResult SetHeapCapacity(ulong Capacity)
+        {
+            lock (Blocks)
+            {
+                HeapCapacity = Capacity;
+            }
+
+            return KernelResult.Success;
+        }
+
+        public KernelResult SetMemoryAttribute(
+            ulong           Address,
+            ulong           Size,
             MemoryAttribute AttributeMask,
             MemoryAttribute AttributeValue)
         {
             lock (Blocks)
             {
                 if (CheckRange(
-                    Position,
+                    Address,
                     Size,
                     MemoryState.AttributeChangeAllowed,
                     MemoryState.AttributeChangeAllowed,
@@ -391,35 +936,42 @@ namespace Ryujinx.HLE.HOS.Kernel
                     out MemoryPermission Permission,
                     out MemoryAttribute  Attribute))
                 {
-                    long PagesCount = Size / PageSize;
+                    if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+                    {
+                        return KernelResult.OutOfResource;
+                    }
+
+                    ulong PagesCount = Size / PageSize;
 
                     Attribute &= ~AttributeMask;
                     Attribute |=  AttributeMask & AttributeValue;
 
-                    InsertBlock(Position, PagesCount, State, Permission, Attribute);
+                    InsertBlock(Address, PagesCount, State, Permission, Attribute);
 
-                    return 0;
+                    return KernelResult.Success;
+                }
+                else
+                {
+                    return KernelResult.InvalidMemState;
                 }
             }
-
-            return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
         }
 
-        public KMemoryInfo QueryMemory(long Position)
+        public KMemoryInfo QueryMemory(ulong Address)
         {
-            if ((ulong)Position >= (ulong)AddrSpaceStart &&
-                (ulong)Position <  (ulong)AddrSpaceEnd)
+            if (Address >= AddrSpaceStart &&
+                Address <  AddrSpaceEnd)
             {
                 lock (Blocks)
                 {
-                    return FindBlock(Position).GetInfo();
+                    return FindBlock(Address).GetInfo();
                 }
             }
             else
             {
                 return new KMemoryInfo(
                     AddrSpaceEnd,
-                    -AddrSpaceEnd,
+                    ~AddrSpaceEnd + 1,
                     MemoryState.Reserved,
                     MemoryPermission.None,
                     MemoryAttribute.None,
@@ -428,7 +980,7 @@ namespace Ryujinx.HLE.HOS.Kernel
             }
         }
 
-        public long Map(long Src, long Dst, long Size)
+        public KernelResult Map(ulong Dst, ulong Src, ulong Size)
         {
             bool Success;
 
@@ -452,22 +1004,90 @@ namespace Ryujinx.HLE.HOS.Kernel
 
                 if (Success)
                 {
-                    long PagesCount = Size / PageSize;
+                    if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion * 2))
+                    {
+                        return KernelResult.OutOfResource;
+                    }
+
+                    ulong PagesCount = Size / PageSize;
+
+                    KPageList PageList = new KPageList();
+
+                    AddVaRangeToPageList(PageList, Src, PagesCount);
+
+                    KernelResult Result = MmuChangePermission(Src, PagesCount, MemoryPermission.None);
+
+                    if (Result != KernelResult.Success)
+                    {
+                        return Result;
+                    }
+
+                    Result = MapPages(Dst, PageList, MemoryPermission.ReadAndWrite);
+
+                    if (Result != KernelResult.Success)
+                    {
+                        if (MmuChangePermission(Src, PagesCount, MemoryPermission.ReadAndWrite) != KernelResult.Success)
+                        {
+                            throw new InvalidOperationException("Unexpected failure reverting memory permission.");
+                        }
+
+                        return Result;
+                    }
 
                     InsertBlock(Src, PagesCount, SrcState, MemoryPermission.None, MemoryAttribute.Borrowed);
+                    InsertBlock(Dst, PagesCount, MemoryState.Stack, MemoryPermission.ReadAndWrite);
 
-                    InsertBlock(Dst, PagesCount, MemoryState.MappedMemory, MemoryPermission.ReadAndWrite);
-
-                    long PA = CpuMemory.GetPhysicalAddress(Src);
-
-                    CpuMemory.Map(Dst, PA, Size);
+                    return KernelResult.Success;
+                }
+                else
+                {
+                    return KernelResult.InvalidMemState;
                 }
             }
-
-            return Success ? 0 : MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
         }
 
-        public long Unmap(long Src, long Dst, long Size)
+        public KernelResult UnmapForKernel(ulong Address, ulong PagesCount, MemoryState StateExpected)
+        {
+            ulong Size = PagesCount * PageSize;
+
+            lock (Blocks)
+            {
+                if (CheckRange(
+                    Address,
+                    Size,
+                    MemoryState.Mask,
+                    StateExpected,
+                    MemoryPermission.None,
+                    MemoryPermission.None,
+                    MemoryAttribute.Mask,
+                    MemoryAttribute.None,
+                    MemoryAttribute.IpcAndDeviceMapped,
+                    out _,
+                    out _,
+                    out _))
+                {
+                    if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+                    {
+                        return KernelResult.OutOfResource;
+                    }
+
+                    KernelResult Result = MmuUnmap(Address, PagesCount);
+
+                    if (Result == KernelResult.Success)
+                    {
+                        InsertBlock(Address, PagesCount, MemoryState.Unmapped);
+                    }
+
+                    return KernelResult.Success;
+                }
+                else
+                {
+                    return KernelResult.InvalidMemState;
+                }
+            }
+        }
+
+        public KernelResult Unmap(ulong Dst, ulong Src, ulong Size)
         {
             bool Success;
 
@@ -491,87 +1111,70 @@ namespace Ryujinx.HLE.HOS.Kernel
                     Dst,
                     Size,
                     MemoryState.Mask,
-                    MemoryState.MappedMemory,
+                    MemoryState.Stack,
                     MemoryPermission.None,
                     MemoryPermission.None,
                     MemoryAttribute.Mask,
                     MemoryAttribute.None,
                     MemoryAttribute.IpcAndDeviceMapped,
                     out _,
-                    out _,
+                    out MemoryPermission DstPermission,
                     out _);
 
                 if (Success)
                 {
-                    long PagesCount = Size / PageSize;
+                    if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion * 2))
+                    {
+                        return KernelResult.OutOfResource;
+                    }
+
+                    ulong PagesCount = Size / PageSize;
+
+                    KPageList SrcPageList = new KPageList();
+                    KPageList DstPageList = new KPageList();
+
+                    AddVaRangeToPageList(SrcPageList, Src, PagesCount);
+                    AddVaRangeToPageList(DstPageList, Dst, PagesCount);
+
+                    if (!DstPageList.IsEqual(SrcPageList))
+                    {
+                        return KernelResult.InvalidMemRange;
+                    }
+
+                    KernelResult Result = MmuUnmap(Dst, PagesCount);
+
+                    if (Result != KernelResult.Success)
+                    {
+                        return Result;
+                    }
+
+                    Result = MmuChangePermission(Src, PagesCount, MemoryPermission.ReadAndWrite);
+
+                    if (Result != KernelResult.Success)
+                    {
+                        MapPages(Dst, DstPageList, DstPermission);
+
+                        return Result;
+                    }
 
                     InsertBlock(Src, PagesCount, SrcState, MemoryPermission.ReadAndWrite);
-
                     InsertBlock(Dst, PagesCount, MemoryState.Unmapped);
 
-                    CpuMemory.Unmap(Dst, Size);
+                    return KernelResult.Success;
                 }
-            }
-
-            return Success ? 0 : MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
-        }
-
-        public long MapSharedMemory(KSharedMemory SharedMemory, MemoryPermission Permission, long Position)
-        {
-            lock (Blocks)
-            {
-                if (IsUnmapped(Position, SharedMemory.Size))
+                else
                 {
-                    long PagesCount = SharedMemory.Size / PageSize;
-
-                    InsertBlock(Position, PagesCount, MemoryState.SharedMemory, Permission);
-
-                    CpuMemory.Map(Position, SharedMemory.PA, SharedMemory.Size);
-
-                    return 0;
+                    return KernelResult.InvalidMemState;
                 }
             }
-
-            return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
         }
 
-        public long UnmapSharedMemory(long Position, long Size)
+        public KernelResult ReserveTransferMemory(ulong Address, ulong Size, MemoryPermission Permission)
         {
             lock (Blocks)
             {
                 if (CheckRange(
-                    Position,
-                    Size,
-                    MemoryState.Mask,
-                    MemoryState.SharedMemory,
-                    MemoryPermission.None,
-                    MemoryPermission.None,
-                    MemoryAttribute.Mask,
-                    MemoryAttribute.None,
-                    MemoryAttribute.IpcAndDeviceMapped,
-                    out MemoryState State,
-                    out _,
-                    out _))
-                {
-                    long PagesCount = Size / PageSize;
-
-                    InsertBlock(Position, PagesCount, MemoryState.Unmapped);
-
-                    CpuMemory.Unmap(Position, Size);
-
-                    return 0;
-                }
-            }
-
-            return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
-        }
-
-        public long ReserveTransferMemory(long Position, long Size, MemoryPermission Permission)
-        {
-            lock (Blocks)
-            {
-                if (CheckRange(
-                    Position,
+                    Address,
                     Size,
                     MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated,
                     MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated,
@@ -584,25 +1187,34 @@ namespace Ryujinx.HLE.HOS.Kernel
                     out _,
                     out MemoryAttribute Attribute))
                 {
-                    long PagesCount = Size / PageSize;
+                    //TODO: Missing checks.
+
+                    if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+                    {
+                        return KernelResult.OutOfResource;
+                    }
+
+                    ulong PagesCount = Size / PageSize;
 
                     Attribute |= MemoryAttribute.Borrowed;
 
-                    InsertBlock(Position, PagesCount, State, Permission, Attribute);
+                    InsertBlock(Address, PagesCount, State, Permission, Attribute);
 
-                    return 0;
+                    return KernelResult.Success;
+                }
+                else
+                {
+                    return KernelResult.InvalidMemState;
                 }
             }
-
-            return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
         }
 
-        public long ResetTransferMemory(long Position, long Size)
+        public KernelResult ResetTransferMemory(ulong Address, ulong Size)
         {
             lock (Blocks)
             {
                 if (CheckRange(
-                    Position,
+                    Address,
                     Size,
                     MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated,
                     MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated,
@@ -615,23 +1227,30 @@ namespace Ryujinx.HLE.HOS.Kernel
                     out _,
                     out _))
                 {
-                    long PagesCount = Size / PageSize;
+                    if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+                    {
+                        return KernelResult.OutOfResource;
+                    }
 
-                    InsertBlock(Position, PagesCount, State, MemoryPermission.ReadAndWrite);
+                    ulong PagesCount = Size / PageSize;
 
-                    return 0;
+                    InsertBlock(Address, PagesCount, State, MemoryPermission.ReadAndWrite);
+
+                    return KernelResult.Success;
+                }
+                else
+                {
+                    return KernelResult.InvalidMemState;
                 }
             }
-
-            return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
         }
 
-        public long SetProcessMemoryPermission(long Position, long Size, MemoryPermission Permission)
+        public KernelResult SetProcessMemoryPermission(ulong Address, ulong Size, MemoryPermission Permission)
         {
             lock (Blocks)
             {
                 if (CheckRange(
-                    Position,
+                    Address,
                     Size,
                     MemoryState.ProcessPermissionChangeAllowed,
                     MemoryState.ProcessPermissionChangeAllowed,
@@ -640,47 +1259,73 @@ namespace Ryujinx.HLE.HOS.Kernel
                     MemoryAttribute.Mask,
                     MemoryAttribute.None,
                     MemoryAttribute.IpcAndDeviceMapped,
-                    out MemoryState State,
-                    out _,
+                    out MemoryState      OldState,
+                    out MemoryPermission OldPermission,
                     out _))
                 {
-                    if (State == MemoryState.CodeStatic)
+                    MemoryState NewState = OldState;
+
+                    //If writing into the code region is allowed, then we need
+                    //to change it to mutable.
+                    if ((Permission & MemoryPermission.Write) != 0)
                     {
-                        State = MemoryState.CodeMutable;
-                    }
-                    else if (State == MemoryState.ModCodeStatic)
-                    {
-                        State = MemoryState.ModCodeMutable;
-                    }
-                    else
-                    {
-                        throw new InvalidOperationException();
+                        if (OldState == MemoryState.CodeStatic)
+                        {
+                            NewState = MemoryState.CodeMutable;
+                        }
+                        else if (OldState == MemoryState.ModCodeStatic)
+                        {
+                            NewState = MemoryState.ModCodeMutable;
+                        }
+                        else
+                        {
+                            throw new InvalidOperationException($"Memory state \"{OldState}\" not valid for this operation.");
+                        }
                     }
 
-                    long PagesCount = Size / PageSize;
+                    if (NewState != OldState || Permission != OldPermission)
+                    {
+                        if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+                        {
+                            return KernelResult.OutOfResource;
+                        }
 
-                    InsertBlock(Position, PagesCount, State, Permission);
+                        ulong PagesCount = Size / PageSize;
 
-                    return 0;
+                        MemoryOperation Operation = (Permission & MemoryPermission.Execute) != 0
+                            ? MemoryOperation.ChangePermsAndAttributes
+                            : MemoryOperation.ChangePermRw;
+
+                        KernelResult Result = DoMmuOperation(Address, PagesCount, 0, false, Permission, Operation);
+
+                        if (Result != KernelResult.Success)
+                        {
+                            return Result;
+                        }
+
+                        InsertBlock(Address, PagesCount, NewState, Permission);
+                    }
+
+                    return KernelResult.Success;
+                }
+                else
+                {
+                    return KernelResult.InvalidMemState;
                 }
             }
-
-            return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
         }
 
-        public long MapPhysicalMemory(long Position, long Size)
+        public KernelResult MapPhysicalMemory(ulong Address, ulong Size)
         {
-            long End = Position + Size;
+            ulong EndAddr = Address + Size;
 
             lock (Blocks)
             {
-                long MappedSize = 0;
+                ulong MappedSize = 0;
 
                 KMemoryInfo Info;
 
-                LinkedListNode<KMemoryBlock> BaseNode = FindBlockNode(Position);
-
-                LinkedListNode<KMemoryBlock> Node = BaseNode;
+                LinkedListNode<KMemoryBlock> Node = FindBlockNode(Address);
 
                 do
                 {
@@ -688,57 +1333,66 @@ namespace Ryujinx.HLE.HOS.Kernel
 
                     if (Info.State != MemoryState.Unmapped)
                     {
-                        MappedSize += GetSizeInRange(Info, Position, End);
+                        MappedSize += GetSizeInRange(Info, Address, EndAddr);
                     }
 
                     Node = Node.Next;
                 }
-                while ((ulong)(Info.Position + Info.Size) < (ulong)End && Node != null);
+                while (Info.Address + Info.Size < EndAddr && Node != null);
 
                 if (MappedSize == Size)
                 {
-                    return 0;
+                    return KernelResult.Success;
                 }
 
-                long RemainingSize = Size - MappedSize;
+                ulong RemainingSize = Size - MappedSize;
 
-                if (!Allocator.TryAllocate(RemainingSize, out long PA))
+                ulong RemainingPages = RemainingSize / PageSize;
+
+                KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+                if (CurrentProcess.ResourceLimit != null &&
+                   !CurrentProcess.ResourceLimit.Reserve(LimitableResource.Memory, RemainingSize))
                 {
-                    return MakeError(ErrorModule.Kernel, KernelErr.OutOfMemory);
+                    return KernelResult.ResLimitExceeded;
                 }
 
-                Node = BaseNode;
+                KMemoryRegionManager Region = GetMemoryRegionManager();
 
-                do
+                KernelResult Result = Region.AllocatePages(RemainingPages, AslrDisabled, out KPageList PageList);
+
+                void CleanUpForError()
                 {
-                    Info = Node.Value.GetInfo();
-
-                    if (Info.State == MemoryState.Unmapped)
+                    if (PageList != null)
                     {
-                        long CurrSize = GetSizeInRange(Info, Position, End);
-
-                        long MapPosition = Info.Position;
-
-                        if ((ulong)MapPosition < (ulong)Position)
-                        {
-                            MapPosition = Position;
-                        }
-
-                        CpuMemory.Map(MapPosition, PA, CurrSize);
-
-                        PA += CurrSize;
+                        Region.FreePages(PageList);
                     }
 
-                    Node = Node.Next;
+                    CurrentProcess.ResourceLimit?.Release(LimitableResource.Memory, RemainingSize);
                 }
-                while ((ulong)(Info.Position + Info.Size) < (ulong)End && Node != null);
 
-                PersonalMmHeapUsage += RemainingSize;
+                if (Result != KernelResult.Success)
+                {
+                    CleanUpForError();
 
-                long PagesCount = Size / PageSize;
+                    return Result;
+                }
+
+                if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+                {
+                    CleanUpForError();
+
+                    return KernelResult.OutOfResource;
+                }
+
+                MapPhysicalMemory(PageList, Address, EndAddr);
+
+                PhysicalMemoryUsage += RemainingSize;
+
+                ulong PagesCount = Size / PageSize;
 
                 InsertBlock(
-                    Position,
+                    Address,
                     PagesCount,
                     MemoryState.Unmapped,
                     MemoryPermission.None,
@@ -748,22 +1402,26 @@ namespace Ryujinx.HLE.HOS.Kernel
                     MemoryAttribute.None);
             }
 
-            return 0;
+            return KernelResult.Success;
         }
 
-        public long UnmapPhysicalMemory(long Position, long Size)
+        public KernelResult UnmapPhysicalMemory(ulong Address, ulong Size)
         {
-            long End = Position + Size;
+            ulong EndAddr = Address + Size;
 
             lock (Blocks)
             {
-                long HeapMappedSize = 0;
+                //Scan, ensure that the region can be unmapped (all blocks are heap or
+                //already unmapped), fill pages list for freeing memory.
+                ulong HeapMappedSize = 0;
 
-                long CurrPosition = Position;
+                KPageList PageList = new KPageList();
 
                 KMemoryInfo Info;
 
-                LinkedListNode<KMemoryBlock> Node = FindBlockNode(CurrPosition);
+                LinkedListNode<KMemoryBlock> BaseNode = FindBlockNode(Address);
+
+                LinkedListNode<KMemoryBlock> Node = BaseNode;
 
                 do
                 {
@@ -773,90 +1431,200 @@ namespace Ryujinx.HLE.HOS.Kernel
                     {
                         if (Info.Attribute != MemoryAttribute.None)
                         {
-                            return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
+                            return KernelResult.InvalidMemState;
                         }
 
-                        HeapMappedSize += GetSizeInRange(Info, Position, End);
+                        ulong BlockSize    = GetSizeInRange(Info, Address, EndAddr);
+                        ulong BlockAddress = GetAddrInRange(Info, Address);
+
+                        AddVaRangeToPageList(PageList, BlockAddress, BlockSize / PageSize);
+
+                        HeapMappedSize += BlockSize;
                     }
                     else if (Info.State != MemoryState.Unmapped)
                     {
-                        return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
+                        return KernelResult.InvalidMemState;
                     }
 
                     Node = Node.Next;
                 }
-                while ((ulong)(Info.Position + Info.Size) < (ulong)End && Node != null);
+                while (Info.Address + Info.Size < EndAddr && Node != null);
 
                 if (HeapMappedSize == 0)
                 {
-                    return 0;
+                    return KernelResult.Success;
                 }
 
-                PersonalMmHeapUsage -= HeapMappedSize;
-
-                long PagesCount = Size / PageSize;
-
-                InsertBlock(Position, PagesCount, MemoryState.Unmapped);
-
-                FreePages(Position, PagesCount);
-
-                CpuMemory.Unmap(Position, Size);
-
-                return 0;
-            }
-        }
-
-        private long GetSizeInRange(KMemoryInfo Info, long Start, long End)
-        {
-            long CurrEnd  = Info.Size + Info.Position;
-            long CurrSize = Info.Size;
-
-            if ((ulong)Info.Position < (ulong)Start)
-            {
-                CurrSize -= Start - Info.Position;
-            }
-
-            if ((ulong)CurrEnd > (ulong)End)
-            {
-                CurrSize -= CurrEnd - End;
-            }
-
-            return CurrSize;
-        }
-
-        private void FreePages(long Position, long PagesCount)
-        {
-            for (long Page = 0; Page < PagesCount; Page++)
-            {
-                long VA = Position + Page * PageSize;
-
-                if (!CpuMemory.IsMapped(VA))
+                if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
                 {
-                    continue;
+                    return KernelResult.OutOfResource;
                 }
 
-                long PA = CpuMemory.GetPhysicalAddress(VA);
+                //Try to unmap all the heap mapped memory inside range.
+                KernelResult Result = KernelResult.Success;
 
-                Allocator.Free(PA, PageSize);
+                Node = BaseNode;
+
+                do
+                {
+                    Info = Node.Value.GetInfo();
+
+                    if (Info.State == MemoryState.Heap)
+                    {
+                        ulong BlockSize    = GetSizeInRange(Info, Address, EndAddr);
+                        ulong BlockAddress = GetAddrInRange(Info, Address);
+
+                        ulong BlockPagesCount = BlockSize / PageSize;
+
+                        Result = MmuUnmap(BlockAddress, BlockPagesCount);
+
+                        if (Result != KernelResult.Success)
+                        {
+                            //If we failed to unmap, we need to remap everything back again.
+                            MapPhysicalMemory(PageList, Address, BlockAddress + BlockSize);
+
+                            break;
+                        }
+                    }
+
+                    Node = Node.Next;
+                }
+                while (Info.Address + Info.Size < EndAddr && Node != null);
+
+                if (Result == KernelResult.Success)
+                {
+                    GetMemoryRegionManager().FreePages(PageList);
+
+                    PhysicalMemoryUsage -= HeapMappedSize;
+
+                    KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+                    CurrentProcess.ResourceLimit?.Release(LimitableResource.Memory, HeapMappedSize);
+
+                    ulong PagesCount = Size / PageSize;
+
+                    InsertBlock(Address, PagesCount, MemoryState.Unmapped);
+                }
+
+                return Result;
             }
         }
 
-        public bool HleIsUnmapped(long Position, long Size)
+        private void MapPhysicalMemory(KPageList PageList, ulong Address, ulong EndAddr)
         {
-            bool Result = false;
+            KMemoryInfo Info;
 
-            lock (Blocks)
+            LinkedListNode<KMemoryBlock> Node = FindBlockNode(Address);
+
+            LinkedListNode<KPageNode> PageListNode = PageList.Nodes.First;
+
+            KPageNode PageNode = PageListNode.Value;
+
+            ulong SrcPa      = PageNode.Address;
+            ulong SrcPaPages = PageNode.PagesCount;
+
+            do
             {
-                Result = IsUnmapped(Position, Size);
-            }
+                Info = Node.Value.GetInfo();
 
-            return Result;
+                if (Info.State == MemoryState.Unmapped)
+                {
+                    ulong BlockSize = GetSizeInRange(Info, Address, EndAddr);
+
+                    ulong DstVaPages = BlockSize / PageSize;
+
+                    ulong DstVa = GetAddrInRange(Info, Address);
+
+                    while (DstVaPages > 0)
+                    {
+                        if (SrcPaPages == 0)
+                        {
+                            PageListNode = PageListNode.Next;
+
+                            PageNode = PageListNode.Value;
+
+                            SrcPa      = PageNode.Address;
+                            SrcPaPages = PageNode.PagesCount;
+                        }
+
+                        ulong PagesCount = SrcPaPages;
+
+                        if (PagesCount > DstVaPages)
+                        {
+                            PagesCount = DstVaPages;
+                        }
+
+                        DoMmuOperation(
+                            DstVa,
+                            PagesCount,
+                            SrcPa,
+                            true,
+                            MemoryPermission.ReadAndWrite,
+                            MemoryOperation.MapPa);
+
+                        DstVa      += PagesCount * PageSize;
+                        SrcPa      += PagesCount * PageSize;
+                        SrcPaPages -= PagesCount;
+                        DstVaPages -= PagesCount;
+                    }
+                }
+
+                Node = Node.Next;
+            }
+            while (Info.Address + Info.Size < EndAddr && Node != null);
         }
 
-        private bool IsUnmapped(long Position, long Size)
+        private static ulong GetSizeInRange(KMemoryInfo Info, ulong Start, ulong End)
+        {
+            ulong EndAddr = Info.Size + Info.Address;
+            ulong Size    = Info.Size;
+
+            if (Info.Address < Start)
+            {
+                Size -= Start - Info.Address;
+            }
+
+            if (EndAddr > End)
+            {
+                Size -= EndAddr - End;
+            }
+
+            return Size;
+        }
+
+        private static ulong GetAddrInRange(KMemoryInfo Info, ulong Start)
+        {
+            if (Info.Address < Start)
+            {
+                return Start;
+            }
+
+            return Info.Address;
+        }
+
+        private void AddVaRangeToPageList(KPageList PageList, ulong Start, ulong PagesCount)
+        {
+            ulong Address = Start;
+
+            while (Address < Start + PagesCount * PageSize)
+            {
+                KernelResult Result = ConvertVaToPa(Address, out ulong Pa);
+
+                if (Result != KernelResult.Success)
+                {
+                    throw new InvalidOperationException("Unexpected failure translating virtual address.");
+                }
+
+                PageList.AddRange(Pa, 1);
+
+                Address += PageSize;
+            }
+        }
+
+        private bool IsUnmapped(ulong Address, ulong Size)
         {
             return CheckRange(
-                Position,
+                Address,
                 Size,
                 MemoryState.Mask,
                 MemoryState.Unmapped,
@@ -871,8 +1639,8 @@ namespace Ryujinx.HLE.HOS.Kernel
         }
 
         private bool CheckRange(
-            long                 Position,
-            long                 Size,
+            ulong                Address,
+            ulong                Size,
             MemoryState          StateMask,
             MemoryState          StateExpected,
             MemoryPermission     PermissionMask,
@@ -884,24 +1652,44 @@ namespace Ryujinx.HLE.HOS.Kernel
             out MemoryPermission OutPermission,
             out MemoryAttribute  OutAttribute)
         {
-            KMemoryInfo BlkInfo = FindBlock(Position).GetInfo();
+            ulong EndAddr = Address + Size - 1;
 
-            ulong Start = (ulong)Position;
-            ulong End   = (ulong)Size + Start;
+            LinkedListNode<KMemoryBlock> Node = FindBlockNode(Address);
 
-            if (End <= (ulong)(BlkInfo.Position + BlkInfo.Size))
+            KMemoryInfo Info = Node.Value.GetInfo();
+
+            MemoryState      FirstState      = Info.State;
+            MemoryPermission FirstPermission = Info.Permission;
+            MemoryAttribute  FirstAttribute  = Info.Attribute;
+
+            do
             {
-                if ((BlkInfo.Attribute  & AttributeMask)  == AttributeExpected &&
-                    (BlkInfo.State      & StateMask)      == StateExpected     &&
-                    (BlkInfo.Permission & PermissionMask) == PermissionExpected)
+                Info = Node.Value.GetInfo();
+
+                //Check if the block state matches what we expect.
+                if ( FirstState                             != Info.State                             ||
+                     FirstPermission                        != Info.Permission                        ||
+                    (Info.Attribute  & AttributeMask)       != AttributeExpected                      ||
+                    (FirstAttribute  | AttributeIgnoreMask) != (Info.Attribute | AttributeIgnoreMask) ||
+                    (FirstState      & StateMask)           != StateExpected                          ||
+                    (FirstPermission & PermissionMask)      != PermissionExpected)
                 {
-                    OutState      = BlkInfo.State;
-                    OutPermission = BlkInfo.Permission;
-                    OutAttribute  = BlkInfo.Attribute & ~AttributeIgnoreMask;
+                    break;
+                }
+
+                //Check if this is the last block on the range, if so return success.
+                if (EndAddr <= Info.Address + Info.Size - 1)
+                {
+                    OutState      = FirstState;
+                    OutPermission = FirstPermission;
+                    OutAttribute  = FirstAttribute & ~AttributeIgnoreMask;
 
                     return true;
                 }
+
+                Node = Node.Next;
             }
+            while (Node != null);
 
             OutState      = MemoryState.Unmapped;
             OutPermission = MemoryPermission.None;
@@ -910,9 +1698,48 @@ namespace Ryujinx.HLE.HOS.Kernel
             return false;
         }
 
+        private bool CheckRange(
+            ulong            Address,
+            ulong            Size,
+            MemoryState      StateMask,
+            MemoryState      StateExpected,
+            MemoryPermission PermissionMask,
+            MemoryPermission PermissionExpected,
+            MemoryAttribute  AttributeMask,
+            MemoryAttribute  AttributeExpected)
+        {
+            ulong EndAddr = Address + Size - 1;
+
+            LinkedListNode<KMemoryBlock> Node = FindBlockNode(Address);
+
+            do
+            {
+                KMemoryInfo Info = Node.Value.GetInfo();
+
+                //Check if the block state matches what we expect.
+                if ((Info.State      & StateMask)      != StateExpected      ||
+                    (Info.Permission & PermissionMask) != PermissionExpected ||
+                    (Info.Attribute  & AttributeMask)  != AttributeExpected)
+                {
+                    break;
+                }
+
+                //Check if this is the last block on the range, if so return success.
+                if (EndAddr <= Info.Address + Info.Size - 1)
+                {
+                    return true;
+                }
+
+                Node = Node.Next;
+            }
+            while (Node != null);
+
+            return false;
+        }
+
         private void InsertBlock(
-            long             BasePosition,
-            long             PagesCount,
+            ulong            BaseAddress,
+            ulong            PagesCount,
             MemoryState      OldState,
             MemoryPermission OldPermission,
             MemoryAttribute  OldAttribute,
@@ -923,10 +1750,11 @@ namespace Ryujinx.HLE.HOS.Kernel
             //Insert new block on the list only on areas where the state
             //of the block matches the state specified on the Old* state
             //arguments, otherwise leave it as is.
+            int OldCount = Blocks.Count;
+
             OldAttribute |= MemoryAttribute.IpcAndDeviceMapped;
 
-            ulong Start = (ulong)BasePosition;
-            ulong End   = (ulong)PagesCount * PageSize + Start;
+            ulong EndAddr = PagesCount * PageSize + BaseAddress;
 
             LinkedListNode<KMemoryBlock> Node = Blocks.First;
 
@@ -937,10 +1765,10 @@ namespace Ryujinx.HLE.HOS.Kernel
 
                 KMemoryBlock CurrBlock = Node.Value;
 
-                ulong CurrStart = (ulong)CurrBlock.BasePosition;
-                ulong CurrEnd   = (ulong)CurrBlock.PagesCount * PageSize + CurrStart;
+                ulong CurrBaseAddr = CurrBlock.BaseAddress;
+                ulong CurrEndAddr  = CurrBlock.PagesCount * PageSize + CurrBaseAddr;
 
-                if (Start < CurrEnd && CurrStart < End)
+                if (BaseAddress < CurrEndAddr && CurrBaseAddr < EndAddr)
                 {
                     MemoryAttribute CurrBlockAttr = CurrBlock.Attribute | MemoryAttribute.IpcAndDeviceMapped;
 
@@ -953,36 +1781,36 @@ namespace Ryujinx.HLE.HOS.Kernel
                         continue;
                     }
 
-                    if (CurrStart >= Start && CurrEnd <= End)
+                    if (CurrBaseAddr >= BaseAddress && CurrEndAddr <= EndAddr)
                     {
                         CurrBlock.State      = NewState;
                         CurrBlock.Permission = NewPermission;
                         CurrBlock.Attribute &= ~MemoryAttribute.IpcAndDeviceMapped;
                         CurrBlock.Attribute |= NewAttribute;
                     }
-                    else if (CurrStart >= Start)
+                    else if (CurrBaseAddr >= BaseAddress)
                     {
-                        CurrBlock.BasePosition = (long)End;
+                        CurrBlock.BaseAddress = EndAddr;
 
-                        CurrBlock.PagesCount = (long)((CurrEnd - End) / PageSize);
+                        CurrBlock.PagesCount = (CurrEndAddr - EndAddr) / PageSize;
 
-                        long NewPagesCount = (long)((End - CurrStart) / PageSize);
+                        ulong NewPagesCount = (EndAddr - CurrBaseAddr) / PageSize;
 
                         NewNode = Blocks.AddBefore(Node, new KMemoryBlock(
-                            (long)CurrStart,
+                            CurrBaseAddr,
                             NewPagesCount,
                             NewState,
                             NewPermission,
                             NewAttribute));
                     }
-                    else if (CurrEnd <= End)
+                    else if (CurrEndAddr <= EndAddr)
                     {
-                        CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize);
+                        CurrBlock.PagesCount = (BaseAddress - CurrBaseAddr) / PageSize;
 
-                        long NewPagesCount = (long)((CurrEnd - Start) / PageSize);
+                        ulong NewPagesCount = (CurrEndAddr - BaseAddress) / PageSize;
 
                         NewNode = Blocks.AddAfter(Node, new KMemoryBlock(
-                            BasePosition,
+                            BaseAddress,
                             NewPagesCount,
                             NewState,
                             NewPermission,
@@ -990,19 +1818,19 @@ namespace Ryujinx.HLE.HOS.Kernel
                     }
                     else
                     {
-                        CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize);
+                        CurrBlock.PagesCount = (BaseAddress - CurrBaseAddr) / PageSize;
 
-                        long NextPagesCount = (long)((CurrEnd - End) / PageSize);
+                        ulong NextPagesCount = (CurrEndAddr - EndAddr) / PageSize;
 
                         NewNode = Blocks.AddAfter(Node, new KMemoryBlock(
-                            BasePosition,
+                            BaseAddress,
                             PagesCount,
                             NewState,
                             NewPermission,
                             NewAttribute));
 
                         Blocks.AddAfter(NewNode, new KMemoryBlock(
-                            (long)End,
+                            EndAddr,
                             NextPagesCount,
                             CurrBlock.State,
                             CurrBlock.Permission,
@@ -1016,21 +1844,24 @@ namespace Ryujinx.HLE.HOS.Kernel
 
                 Node = NextNode;
             }
+
+            BlockAllocator.Count += Blocks.Count - OldCount;
         }
 
         private void InsertBlock(
-            long             BasePosition,
-            long             PagesCount,
+            ulong            BaseAddress,
+            ulong            PagesCount,
             MemoryState      State,
             MemoryPermission Permission = MemoryPermission.None,
             MemoryAttribute  Attribute  = MemoryAttribute.None)
         {
             //Inserts new block at the list, replacing and spliting
             //existing blocks as needed.
-            KMemoryBlock Block = new KMemoryBlock(BasePosition, PagesCount, State, Permission, Attribute);
+            KMemoryBlock Block = new KMemoryBlock(BaseAddress, PagesCount, State, Permission, Attribute);
 
-            ulong Start = (ulong)BasePosition;
-            ulong End   = (ulong)PagesCount * PageSize + Start;
+            int OldCount = Blocks.Count;
+
+            ulong EndAddr = PagesCount * PageSize + BaseAddress;
 
             LinkedListNode<KMemoryBlock> NewNode = null;
 
@@ -1042,26 +1873,26 @@ namespace Ryujinx.HLE.HOS.Kernel
 
                 LinkedListNode<KMemoryBlock> NextNode = Node.Next;
 
-                ulong CurrStart = (ulong)CurrBlock.BasePosition;
-                ulong CurrEnd   = (ulong)CurrBlock.PagesCount * PageSize + CurrStart;
+                ulong CurrBaseAddr = CurrBlock.BaseAddress;
+                ulong CurrEndAddr  = CurrBlock.PagesCount * PageSize + CurrBaseAddr;
 
-                if (Start < CurrEnd && CurrStart < End)
+                if (BaseAddress < CurrEndAddr && CurrBaseAddr < EndAddr)
                 {
-                    if (Start >= CurrStart && End <= CurrEnd)
+                    if (BaseAddress >= CurrBaseAddr && EndAddr <= CurrEndAddr)
                     {
                         Block.Attribute |= CurrBlock.Attribute & MemoryAttribute.IpcAndDeviceMapped;
                     }
 
-                    if (Start > CurrStart && End < CurrEnd)
+                    if (BaseAddress > CurrBaseAddr && EndAddr < CurrEndAddr)
                     {
-                        CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize);
+                        CurrBlock.PagesCount = (BaseAddress - CurrBaseAddr) / PageSize;
 
-                        long NextPagesCount = (long)((CurrEnd - End) / PageSize);
+                        ulong NextPagesCount = (CurrEndAddr - EndAddr) / PageSize;
 
                         NewNode = Blocks.AddAfter(Node, Block);
 
                         Blocks.AddAfter(NewNode, new KMemoryBlock(
-                            (long)End,
+                            EndAddr,
                             NextPagesCount,
                             CurrBlock.State,
                             CurrBlock.Permission,
@@ -1069,20 +1900,20 @@ namespace Ryujinx.HLE.HOS.Kernel
 
                         break;
                     }
-                    else if (Start <= CurrStart && End < CurrEnd)
+                    else if (BaseAddress <= CurrBaseAddr && EndAddr < CurrEndAddr)
                     {
-                        CurrBlock.BasePosition = (long)End;
+                        CurrBlock.BaseAddress = EndAddr;
 
-                        CurrBlock.PagesCount = (long)((CurrEnd - End) / PageSize);
+                        CurrBlock.PagesCount = (CurrEndAddr - EndAddr) / PageSize;
 
                         if (NewNode == null)
                         {
                             NewNode = Blocks.AddBefore(Node, Block);
                         }
                     }
-                    else if (Start > CurrStart && End >= CurrEnd)
+                    else if (BaseAddress > CurrBaseAddr && EndAddr >= CurrEndAddr)
                     {
-                        CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize);
+                        CurrBlock.PagesCount = (BaseAddress - CurrBaseAddr) / PageSize;
 
                         if (NewNode == null)
                         {
@@ -1109,14 +1940,15 @@ namespace Ryujinx.HLE.HOS.Kernel
             }
 
             MergeEqualStateNeighbours(NewNode);
+
+            BlockAllocator.Count += Blocks.Count - OldCount;
         }
 
         private void MergeEqualStateNeighbours(LinkedListNode<KMemoryBlock> Node)
         {
             KMemoryBlock Block = Node.Value;
 
-            ulong Start = (ulong)Block.BasePosition;
-            ulong End   = (ulong)Block.PagesCount * PageSize + Start;
+            ulong EndAddr = Block.PagesCount * PageSize + Block.BaseAddress;
 
             if (Node.Previous != null)
             {
@@ -1126,9 +1958,7 @@ namespace Ryujinx.HLE.HOS.Kernel
                 {
                     Blocks.Remove(Node.Previous);
 
-                    Block.BasePosition = Previous.BasePosition;
-
-                    Start = (ulong)Block.BasePosition;
+                    Block.BaseAddress = Previous.BaseAddress;
                 }
             }
 
@@ -1140,31 +1970,84 @@ namespace Ryujinx.HLE.HOS.Kernel
                 {
                     Blocks.Remove(Node.Next);
 
-                    End = (ulong)(Next.BasePosition + Next.PagesCount * PageSize);
+                    EndAddr = Next.BaseAddress + Next.PagesCount * PageSize;
                 }
             }
 
-            Block.PagesCount = (long)((End - Start) / PageSize);
+            Block.PagesCount = (EndAddr - Block.BaseAddress) / PageSize;
         }
 
-        private static bool BlockStateEquals(KMemoryBlock LHS, KMemoryBlock RHS)
+        private static bool BlockStateEquals(KMemoryBlock Lhs, KMemoryBlock Rhs)
         {
-            return LHS.State          == RHS.State          &&
-                   LHS.Permission     == RHS.Permission     &&
-                   LHS.Attribute      == RHS.Attribute      &&
-                   LHS.DeviceRefCount == RHS.DeviceRefCount &&
-                   LHS.IpcRefCount    == RHS.IpcRefCount;
+            return Lhs.State          == Rhs.State          &&
+                   Lhs.Permission     == Rhs.Permission     &&
+                   Lhs.Attribute      == Rhs.Attribute      &&
+                   Lhs.DeviceRefCount == Rhs.DeviceRefCount &&
+                   Lhs.IpcRefCount    == Rhs.IpcRefCount;
         }
 
-        private KMemoryBlock FindBlock(long Position)
+        private ulong FindFirstFit(
+            ulong RegionStart,
+            ulong RegionPagesCount,
+            ulong NeededPagesCount,
+            int   Alignment,
+            ulong ReservedStart,
+            ulong ReservedPagesCount)
         {
-            return FindBlockNode(Position)?.Value;
+            ulong ReservedSize = ReservedPagesCount * PageSize;
+
+            ulong TotalNeededSize = ReservedSize + NeededPagesCount * PageSize;
+
+            ulong RegionEndAddr = RegionStart + RegionPagesCount * PageSize;
+
+            LinkedListNode<KMemoryBlock> Node = FindBlockNode(RegionStart);
+
+            KMemoryInfo Info = Node.Value.GetInfo();
+
+            while (RegionEndAddr >= Info.Address)
+            {
+                if (Info.State == MemoryState.Unmapped)
+                {
+                    ulong CurrBaseAddr = Info.Address + ReservedSize;
+                    ulong CurrEndAddr  = Info.Address + Info.Size - 1;
+
+                    ulong Address = BitUtils.AlignDown(CurrBaseAddr, Alignment) + ReservedStart;
+
+                    if (CurrBaseAddr > Address)
+                    {
+                        Address += (ulong)Alignment;
+                    }
+
+                    ulong AllocationEndAddr = Address + TotalNeededSize - 1;
+
+                    if (AllocationEndAddr <= RegionEndAddr &&
+                        AllocationEndAddr <= CurrEndAddr   &&
+                        Address           <  AllocationEndAddr)
+                    {
+                        return Address;
+                    }
+                }
+
+                Node = Node.Next;
+
+                if (Node == null)
+                {
+                    break;
+                }
+
+                Info = Node.Value.GetInfo();
+            }
+
+            return 0;
         }
 
-        private LinkedListNode<KMemoryBlock> FindBlockNode(long Position)
+        private KMemoryBlock FindBlock(ulong Address)
         {
-            ulong Addr = (ulong)Position;
+            return FindBlockNode(Address)?.Value;
+        }
 
+        private LinkedListNode<KMemoryBlock> FindBlockNode(ulong Address)
+        {
             lock (Blocks)
             {
                 LinkedListNode<KMemoryBlock> Node = Blocks.First;
@@ -1173,10 +2056,9 @@ namespace Ryujinx.HLE.HOS.Kernel
                 {
                     KMemoryBlock Block = Node.Value;
 
-                    ulong Start = (ulong)Block.BasePosition;
-                    ulong End   = (ulong)Block.PagesCount * PageSize + Start;
+                    ulong CurrEndAddr = Block.PagesCount * PageSize + Block.BaseAddress;
 
-                    if (Start <= Addr && End - 1 >= Addr)
+                    if (Block.BaseAddress <= Address && CurrEndAddr - 1 >= Address)
                     {
                         return Node;
                     }
@@ -1187,5 +2069,390 @@ namespace Ryujinx.HLE.HOS.Kernel
 
             return null;
         }
+
+        private bool ValidateRegionForState(ulong Address, ulong Size, MemoryState State)
+        {
+            ulong EndAddr = Address + Size;
+
+            ulong RegionBaseAddr = GetBaseAddrForState(State);
+
+            ulong RegionEndAddr = RegionBaseAddr + GetSizeForState(State);
+
+            bool InsideRegion()
+            {
+                return RegionBaseAddr <= Address &&
+                       EndAddr        >  Address &&
+                       EndAddr - 1    <= RegionEndAddr - 1;
+            }
+
+            bool OutsideHeapRegion()
+            {
+                return EndAddr <= HeapRegionStart ||
+                       Address >= HeapRegionEnd;
+            }
+
+            bool OutsideMapRegion()
+            {
+                return EndAddr <= AliasRegionStart ||
+                       Address >= AliasRegionEnd;
+            }
+
+            switch (State)
+            {
+                case MemoryState.Io:
+                case MemoryState.Normal:
+                case MemoryState.CodeStatic:
+                case MemoryState.CodeMutable:
+                case MemoryState.SharedMemory:
+                case MemoryState.ModCodeStatic:
+                case MemoryState.ModCodeMutable:
+                case MemoryState.Stack:
+                case MemoryState.ThreadLocal:
+                case MemoryState.TransferMemoryIsolated:
+                case MemoryState.TransferMemory:
+                case MemoryState.ProcessMemory:
+                case MemoryState.CodeReadOnly:
+                case MemoryState.CodeWritable:
+                    return InsideRegion() && OutsideHeapRegion() && OutsideMapRegion();
+
+                case MemoryState.Heap:
+                    return InsideRegion() && OutsideMapRegion();
+
+                case MemoryState.IpcBuffer0:
+                case MemoryState.IpcBuffer1:
+                case MemoryState.IpcBuffer3:
+                    return InsideRegion() && OutsideHeapRegion();
+
+                case MemoryState.KernelStack:
+                    return InsideRegion();
+            }
+
+            throw new ArgumentException($"Invalid state value \"{State}\".");
+        }
+
+        private ulong GetBaseAddrForState(MemoryState State)
+        {
+            switch (State)
+            {
+                case MemoryState.Io:
+                case MemoryState.Normal:
+                case MemoryState.ThreadLocal:
+                    return TlsIoRegionStart;
+
+                case MemoryState.CodeStatic:
+                case MemoryState.CodeMutable:
+                case MemoryState.SharedMemory:
+                case MemoryState.ModCodeStatic:
+                case MemoryState.ModCodeMutable:
+                case MemoryState.TransferMemoryIsolated:
+                case MemoryState.TransferMemory:
+                case MemoryState.ProcessMemory:
+                case MemoryState.CodeReadOnly:
+                case MemoryState.CodeWritable:
+                    return GetAddrSpaceBaseAddr();
+
+                case MemoryState.Heap:
+                    return HeapRegionStart;
+
+                case MemoryState.IpcBuffer0:
+                case MemoryState.IpcBuffer1:
+                case MemoryState.IpcBuffer3:
+                    return AliasRegionStart;
+
+                case MemoryState.Stack:
+                    return StackRegionStart;
+
+                case MemoryState.KernelStack:
+                    return AddrSpaceStart;
+            }
+
+            throw new ArgumentException($"Invalid state value \"{State}\".");
+        }
+
+        private ulong GetSizeForState(MemoryState State)
+        {
+            switch (State)
+            {
+                case MemoryState.Io:
+                case MemoryState.Normal:
+                case MemoryState.ThreadLocal:
+                    return TlsIoRegionEnd - TlsIoRegionStart;
+
+                case MemoryState.CodeStatic:
+                case MemoryState.CodeMutable:
+                case MemoryState.SharedMemory:
+                case MemoryState.ModCodeStatic:
+                case MemoryState.ModCodeMutable:
+                case MemoryState.TransferMemoryIsolated:
+                case MemoryState.TransferMemory:
+                case MemoryState.ProcessMemory:
+                case MemoryState.CodeReadOnly:
+                case MemoryState.CodeWritable:
+                    return GetAddrSpaceSize();
+
+                case MemoryState.Heap:
+                    return HeapRegionEnd - HeapRegionStart;
+
+                case MemoryState.IpcBuffer0:
+                case MemoryState.IpcBuffer1:
+                case MemoryState.IpcBuffer3:
+                    return AliasRegionEnd - AliasRegionStart;
+
+                case MemoryState.Stack:
+                    return StackRegionEnd - StackRegionStart;
+
+                case MemoryState.KernelStack:
+                    return AddrSpaceEnd - AddrSpaceStart;
+            }
+
+            throw new ArgumentException($"Invalid state value \"{State}\".");
+        }
+
+        public ulong GetAddrSpaceBaseAddr()
+        {
+            if (AddrSpaceWidth == 36 || AddrSpaceWidth == 39)
+            {
+                return 0x8000000;
+            }
+            else if (AddrSpaceWidth == 32)
+            {
+                return 0x200000;
+            }
+            else
+            {
+                throw new InvalidOperationException("Invalid address space width!");
+            }
+        }
+
+        public ulong GetAddrSpaceSize()
+        {
+            if (AddrSpaceWidth == 36)
+            {
+                return 0xff8000000;
+            }
+            else if (AddrSpaceWidth == 39)
+            {
+                return 0x7ff8000000;
+            }
+            else if (AddrSpaceWidth == 32)
+            {
+                return 0xffe00000;
+            }
+            else
+            {
+                throw new InvalidOperationException("Invalid address space width!");
+            }
+        }
+
+        private KernelResult MapPages(ulong Address, KPageList PageList, MemoryPermission Permission)
+        {
+            ulong CurrAddr = Address;
+
+            KernelResult Result = KernelResult.Success;
+
+            foreach (KPageNode PageNode in PageList)
+            {
+                Result = DoMmuOperation(
+                    CurrAddr,
+                    PageNode.PagesCount,
+                    PageNode.Address,
+                    true,
+                    Permission,
+                    MemoryOperation.MapPa);
+
+                if (Result != KernelResult.Success)
+                {
+                    KMemoryInfo Info = FindBlock(CurrAddr).GetInfo();
+
+                    ulong PagesCount = (Address - CurrAddr) / PageSize;
+
+                    Result = MmuUnmap(Address, PagesCount);
+
+                    break;
+                }
+
+                CurrAddr += PageNode.PagesCount * PageSize;
+            }
+
+            return Result;
+        }
+
+        private KernelResult MmuUnmap(ulong Address, ulong PagesCount)
+        {
+            return DoMmuOperation(
+                Address,
+                PagesCount,
+                0,
+                false,
+                MemoryPermission.None,
+                MemoryOperation.Unmap);
+        }
+
+        private KernelResult MmuChangePermission(ulong Address, ulong PagesCount, MemoryPermission Permission)
+        {
+            return DoMmuOperation(
+                Address,
+                PagesCount,
+                0,
+                false,
+                Permission,
+                MemoryOperation.ChangePermRw);
+        }
+
+        private KernelResult DoMmuOperation(
+            ulong            DstVa,
+            ulong            PagesCount,
+            ulong            SrcPa,
+            bool             Map,
+            MemoryPermission Permission,
+            MemoryOperation  Operation)
+        {
+            if (Map != (Operation == MemoryOperation.MapPa))
+            {
+                throw new ArgumentException(nameof(Map) + " value is invalid for this operation.");
+            }
+
+            KernelResult Result;
+
+            switch (Operation)
+            {
+                case MemoryOperation.MapPa:
+                {
+                    ulong Size = PagesCount * PageSize;
+
+                    CpuMemory.Map((long)DstVa, (long)(SrcPa - DramMemoryMap.DramBase), (long)Size);
+
+                    Result = KernelResult.Success;
+
+                    break;
+                }
+
+                case MemoryOperation.Allocate:
+                {
+                    KMemoryRegionManager Region = GetMemoryRegionManager();
+
+                    Result = Region.AllocatePages(PagesCount, AslrDisabled, out KPageList PageList);
+
+                    if (Result == KernelResult.Success)
+                    {
+                        Result = MmuMapPages(DstVa, PageList);
+                    }
+
+                    break;
+                }
+
+                case MemoryOperation.Unmap:
+                {
+                    ulong Size = PagesCount * PageSize;
+
+                    CpuMemory.Unmap((long)DstVa, (long)Size);
+
+                    Result = KernelResult.Success;
+
+                    break;
+                }
+
+                case MemoryOperation.ChangePermRw:             Result = KernelResult.Success; break;
+                case MemoryOperation.ChangePermsAndAttributes: Result = KernelResult.Success; break;
+
+                default: throw new ArgumentException($"Invalid operation \"{Operation}\".");
+            }
+
+            return Result;
+        }
+
+        private KernelResult DoMmuOperation(
+            ulong            Address,
+            ulong            PagesCount,
+            KPageList        PageList,
+            MemoryPermission Permission,
+            MemoryOperation  Operation)
+        {
+            if (Operation != MemoryOperation.MapVa)
+            {
+                throw new ArgumentException($"Invalid memory operation \"{Operation}\" specified.");
+            }
+
+            return MmuMapPages(Address, PageList);
+        }
+
+        private KMemoryRegionManager GetMemoryRegionManager()
+        {
+            return System.MemoryRegions[(int)MemRegion];
+        }
+
+        private KernelResult MmuMapPages(ulong Address, KPageList PageList)
+        {
+            foreach (KPageNode PageNode in PageList)
+            {
+                ulong Size = PageNode.PagesCount * PageSize;
+
+                CpuMemory.Map((long)Address, (long)(PageNode.Address - DramMemoryMap.DramBase), (long)Size);
+
+                Address += Size;
+            }
+
+            return KernelResult.Success;
+        }
+
+        public KernelResult ConvertVaToPa(ulong Va, out ulong Pa)
+        {
+            Pa = DramMemoryMap.DramBase + (ulong)CpuMemory.GetPhysicalAddress((long)Va);
+
+            return KernelResult.Success;
+        }
+
+        public long GetMmUsedPages()
+        {
+            lock (Blocks)
+            {
+                return BitUtils.DivRoundUp(GetMmUsedSize(), PageSize);
+            }
+        }
+
+        private long GetMmUsedSize()
+        {
+            return Blocks.Count * KMemoryBlockSize;
+        }
+
+        public bool IsInvalidRegion(ulong Address, ulong Size)
+        {
+            return Address + Size - 1 > GetAddrSpaceBaseAddr() + GetAddrSpaceSize() - 1;
+        }
+
+        public bool InsideAddrSpace(ulong Address, ulong Size)
+        {
+            return AddrSpaceStart <= Address && Address + Size - 1 <= AddrSpaceEnd - 1;
+        }
+
+        public bool InsideAliasRegion(ulong Address, ulong Size)
+        {
+            return Address + Size > AliasRegionStart && AliasRegionEnd > Address;
+        }
+
+        public bool InsideHeapRegion(ulong Address, ulong Size)
+        {
+            return Address + Size > HeapRegionStart && HeapRegionEnd > Address;
+        }
+
+        public bool InsideStackRegion(ulong Address, ulong Size)
+        {
+            return Address + Size > StackRegionStart && StackRegionEnd > Address;
+        }
+
+        public bool OutsideAliasRegion(ulong Address, ulong Size)
+        {
+            return AliasRegionStart > Address || Address + Size - 1 > AliasRegionEnd - 1;
+        }
+
+        public bool OutsideAddrSpace(ulong Address, ulong Size)
+        {
+            return AddrSpaceStart > Address || Address + Size - 1 > AddrSpaceEnd - 1;
+        }
+
+        public bool OutsideStackRegion(ulong Address, ulong Size)
+        {
+            return StackRegionStart > Address || Address + Size - 1 > StackRegionEnd - 1;
+        }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryRegionBlock.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryRegionBlock.cs
new file mode 100644
index 00000000..1f334e65
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KMemoryRegionBlock.cs
@@ -0,0 +1,43 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+    class KMemoryRegionBlock
+    {
+        public long[][] Masks;
+
+        public ulong FreeCount;
+        public int   MaxLevel;
+        public ulong StartAligned;
+        public ulong SizeInBlocksTruncated;
+        public ulong SizeInBlocksRounded;
+        public int   Order;
+        public int   NextOrder;
+
+        public bool TryCoalesce(int Index, int Size)
+        {
+            long Mask = ((1L << Size) - 1) << (Index & 63);
+
+            Index /= 64;
+
+            if ((Mask & ~Masks[MaxLevel - 1][Index]) != 0)
+            {
+                return false;
+            }
+
+            Masks[MaxLevel - 1][Index] &= ~Mask;
+
+            for (int Level = MaxLevel - 2; Level >= 0; Level--, Index /= 64)
+            {
+                Masks[Level][Index / 64] &= ~(1L << (Index & 63));
+
+                if (Masks[Level][Index / 64] != 0)
+                {
+                    break;
+                }
+            }
+
+            FreeCount -= (ulong)Size;
+
+            return true;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryRegionManager.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryRegionManager.cs
new file mode 100644
index 00000000..10db0753
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KMemoryRegionManager.cs
@@ -0,0 +1,428 @@
+using Ryujinx.Common;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+    class KMemoryRegionManager
+    {
+        private static readonly int[] BlockOrders = new int[] { 12, 16, 21, 22, 25, 29, 30 };
+
+        public ulong Address { get; private set; }
+        public ulong EndAddr { get; private set; }
+        public ulong Size    { get; private set; }
+
+        private int BlockOrdersCount;
+
+        private KMemoryRegionBlock[] Blocks;
+
+        public KMemoryRegionManager(ulong Address, ulong Size, ulong EndAddr)
+        {
+            Blocks = new KMemoryRegionBlock[BlockOrders.Length];
+
+            this.Address = Address;
+            this.Size    = Size;
+            this.EndAddr = EndAddr;
+
+            BlockOrdersCount = BlockOrders.Length;
+
+            for (int BlockIndex = 0; BlockIndex < BlockOrdersCount; BlockIndex++)
+            {
+                Blocks[BlockIndex] = new KMemoryRegionBlock();
+
+                Blocks[BlockIndex].Order = BlockOrders[BlockIndex];
+
+                int NextOrder = BlockIndex == BlockOrdersCount - 1 ? 0 : BlockOrders[BlockIndex + 1];
+
+                Blocks[BlockIndex].NextOrder = NextOrder;
+
+                int CurrBlockSize = 1 << BlockOrders[BlockIndex];
+                int NextBlockSize = CurrBlockSize;
+
+                if (NextOrder != 0)
+                {
+                    NextBlockSize = 1 << NextOrder;
+                }
+
+                ulong StartAligned   = BitUtils.AlignDown(Address, NextBlockSize);
+                ulong EndAddrAligned = BitUtils.AlignDown(EndAddr, CurrBlockSize);
+
+                ulong SizeInBlocksTruncated = (EndAddrAligned - StartAligned) >> BlockOrders[BlockIndex];
+
+                ulong EndAddrRounded = BitUtils.AlignUp(Address + Size, NextBlockSize);
+
+                ulong SizeInBlocksRounded = (EndAddrRounded - StartAligned) >> BlockOrders[BlockIndex];
+
+                Blocks[BlockIndex].StartAligned          = StartAligned;
+                Blocks[BlockIndex].SizeInBlocksTruncated = SizeInBlocksTruncated;
+                Blocks[BlockIndex].SizeInBlocksRounded   = SizeInBlocksRounded;
+
+                ulong CurrSizeInBlocks = SizeInBlocksRounded;
+
+                int MaxLevel = 0;
+
+                do
+                {
+                    MaxLevel++;
+                }
+                while ((CurrSizeInBlocks /= 64) != 0);
+
+                Blocks[BlockIndex].MaxLevel = MaxLevel;
+
+                Blocks[BlockIndex].Masks = new long[MaxLevel][];
+
+                CurrSizeInBlocks = SizeInBlocksRounded;
+
+                for (int Level = MaxLevel - 1; Level >= 0; Level--)
+                {
+                    CurrSizeInBlocks = (CurrSizeInBlocks + 63) / 64;
+
+                    Blocks[BlockIndex].Masks[Level] = new long[CurrSizeInBlocks];
+                }
+            }
+
+            if (Size != 0)
+            {
+                FreePages(Address, Size / KMemoryManager.PageSize);
+            }
+        }
+
+        public KernelResult AllocatePages(ulong PagesCount, bool Backwards, out KPageList PageList)
+        {
+            lock (Blocks)
+            {
+                return AllocatePagesImpl(PagesCount, Backwards, out PageList);
+            }
+        }
+
+        private KernelResult AllocatePagesImpl(ulong PagesCount, bool Backwards, out KPageList PageList)
+        {
+            PageList = new KPageList();
+
+            if (BlockOrdersCount > 0)
+            {
+                if (GetFreePagesImpl() < PagesCount)
+                {
+                    return KernelResult.OutOfMemory;
+                }
+            }
+            else if (PagesCount != 0)
+            {
+                return KernelResult.OutOfMemory;
+            }
+
+            for (int BlockIndex = BlockOrdersCount - 1; BlockIndex >= 0; BlockIndex--)
+            {
+                KMemoryRegionBlock Block = Blocks[BlockIndex];
+
+                ulong BestFitBlockSize = 1UL << Block.Order;
+
+                ulong BlockPagesCount = BestFitBlockSize / KMemoryManager.PageSize;
+
+                //Check if this is the best fit for this page size.
+                //If so, try allocating as much requested pages as possible.
+                while (BlockPagesCount <= PagesCount)
+                {
+                    ulong Address = 0;
+
+                    for (int CurrBlockIndex = BlockIndex;
+                             CurrBlockIndex < BlockOrdersCount && Address == 0;
+                             CurrBlockIndex++)
+                    {
+                        Block = Blocks[CurrBlockIndex];
+
+                        int Index = 0;
+
+                        bool ZeroMask = false;
+
+                        for (int Level = 0; Level < Block.MaxLevel; Level++)
+                        {
+                            long Mask = Block.Masks[Level][Index];
+
+                            if (Mask == 0)
+                            {
+                                ZeroMask = true;
+
+                                break;
+                            }
+
+                            if (Backwards)
+                            {
+                                Index = (Index * 64 + 63) - BitUtils.CountLeadingZeros64(Mask);
+                            }
+                            else
+                            {
+                                Index = Index * 64 + BitUtils.CountLeadingZeros64(BitUtils.ReverseBits64(Mask));
+                            }
+                        }
+
+                        if (Block.SizeInBlocksTruncated <= (ulong)Index || ZeroMask)
+                        {
+                            continue;
+                        }
+
+                        Block.FreeCount--;
+
+                        int TempIdx = Index;
+
+                        for (int Level = Block.MaxLevel - 1; Level >= 0; Level--, TempIdx /= 64)
+                        {
+                            Block.Masks[Level][TempIdx / 64] &= ~(1L << (TempIdx & 63));
+
+                            if (Block.Masks[Level][TempIdx / 64] != 0)
+                            {
+                                break;
+                            }
+                        }
+
+                        Address = Block.StartAligned + ((ulong)Index << Block.Order);
+                    }
+
+                    for (int CurrBlockIndex = BlockIndex;
+                             CurrBlockIndex < BlockOrdersCount && Address == 0;
+                             CurrBlockIndex++)
+                    {
+                        Block = Blocks[CurrBlockIndex];
+
+                        int Index = 0;
+
+                        bool ZeroMask = false;
+
+                        for (int Level = 0; Level < Block.MaxLevel; Level++)
+                        {
+                            long Mask = Block.Masks[Level][Index];
+
+                            if (Mask == 0)
+                            {
+                                ZeroMask = true;
+
+                                break;
+                            }
+
+                            if (Backwards)
+                            {
+                                Index = Index * 64 + BitUtils.CountLeadingZeros64(BitUtils.ReverseBits64(Mask));
+                            }
+                            else
+                            {
+                                Index = (Index * 64 + 63) - BitUtils.CountLeadingZeros64(Mask);
+                            }
+                        }
+
+                        if (Block.SizeInBlocksTruncated <= (ulong)Index || ZeroMask)
+                        {
+                            continue;
+                        }
+
+                        Block.FreeCount--;
+
+                        int TempIdx = Index;
+
+                        for (int Level = Block.MaxLevel - 1; Level >= 0; Level--, TempIdx /= 64)
+                        {
+                            Block.Masks[Level][TempIdx / 64] &= ~(1L << (TempIdx & 63));
+
+                            if (Block.Masks[Level][TempIdx / 64] != 0)
+                            {
+                                break;
+                            }
+                        }
+
+                        Address = Block.StartAligned + ((ulong)Index << Block.Order);
+                    }
+
+                    //The address being zero means that no free space was found on that order,
+                    //just give up and try with the next one.
+                    if (Address == 0)
+                    {
+                        break;
+                    }
+
+                    //If we are using a larger order than best fit, then we should
+                    //split it into smaller blocks.
+                    ulong FirstFreeBlockSize = 1UL << Block.Order;
+
+                    if (FirstFreeBlockSize > BestFitBlockSize)
+                    {
+                        FreePages(Address + BestFitBlockSize, (FirstFreeBlockSize - BestFitBlockSize) / KMemoryManager.PageSize);
+                    }
+
+                    //Add new allocated page(s) to the pages list.
+                    //If an error occurs, then free all allocated pages and fail.
+                    KernelResult Result = PageList.AddRange(Address, BlockPagesCount);
+
+                    if (Result != KernelResult.Success)
+                    {
+                        FreePages(Address, BlockPagesCount);
+
+                        foreach (KPageNode PageNode in PageList)
+                        {
+                            FreePages(PageNode.Address, PageNode.PagesCount);
+                        }
+
+                        return Result;
+                    }
+
+                    PagesCount -= BlockPagesCount;
+                }
+            }
+
+            //Success case, all requested pages were allocated successfully.
+            if (PagesCount == 0)
+            {
+                return KernelResult.Success;
+            }
+
+            //Error case, free allocated pages and return out of memory.
+            foreach (KPageNode PageNode in PageList)
+            {
+                FreePages(PageNode.Address, PageNode.PagesCount);
+            }
+
+            PageList = null;
+
+            return KernelResult.OutOfMemory;
+        }
+
+        public void FreePages(KPageList PageList)
+        {
+            lock (Blocks)
+            {
+                foreach (KPageNode PageNode in PageList)
+                {
+                    FreePages(PageNode.Address, PageNode.PagesCount);
+                }
+            }
+        }
+
+        private void FreePages(ulong Address, ulong PagesCount)
+        {
+            ulong EndAddr = Address + PagesCount * KMemoryManager.PageSize;
+
+            int BlockIndex = BlockOrdersCount - 1;
+
+            ulong AddressRounded   = 0;
+            ulong EndAddrTruncated = 0;
+
+            for (; BlockIndex >= 0; BlockIndex--)
+            {
+                KMemoryRegionBlock AllocInfo = Blocks[BlockIndex];
+
+                int BlockSize = 1 << AllocInfo.Order;
+
+                AddressRounded   = BitUtils.AlignUp  (Address, BlockSize);
+                EndAddrTruncated = BitUtils.AlignDown(EndAddr, BlockSize);
+
+                if (AddressRounded < EndAddrTruncated)
+                {
+                    break;
+                }
+            }
+
+            void FreeRegion(ulong CurrAddress)
+            {
+                for (int CurrBlockIndex = BlockIndex;
+                         CurrBlockIndex < BlockOrdersCount && CurrAddress != 0;
+                         CurrBlockIndex++)
+                {
+                    KMemoryRegionBlock Block = Blocks[CurrBlockIndex];
+
+                    Block.FreeCount++;
+
+                    ulong FreedBlocks = (CurrAddress - Block.StartAligned) >> Block.Order;
+
+                    int Index = (int)FreedBlocks;
+
+                    for (int Level = Block.MaxLevel - 1; Level >= 0; Level--, Index /= 64)
+                    {
+                        long Mask = Block.Masks[Level][Index / 64];
+
+                        Block.Masks[Level][Index / 64] = Mask | (1L << (Index & 63));
+
+                        if (Mask != 0)
+                        {
+                            break;
+                        }
+                    }
+
+                    int BlockSizeDelta = 1 << (Block.NextOrder - Block.Order);
+
+                    int FreedBlocksTruncated = BitUtils.AlignDown((int)FreedBlocks, BlockSizeDelta);
+
+                    if (!Block.TryCoalesce(FreedBlocksTruncated, BlockSizeDelta))
+                    {
+                        break;
+                    }
+
+                    CurrAddress = Block.StartAligned + ((ulong)FreedBlocksTruncated << Block.Order);
+                }
+            }
+
+            //Free inside aligned region.
+            ulong BaseAddress = AddressRounded;
+
+            while (BaseAddress < EndAddrTruncated)
+            {
+                ulong BlockSize = 1UL << Blocks[BlockIndex].Order;
+
+                FreeRegion(BaseAddress);
+
+                BaseAddress += BlockSize;
+            }
+
+            int NextBlockIndex = BlockIndex - 1;
+
+            //Free region between Address and aligned region start.
+            BaseAddress = AddressRounded;
+
+            for (BlockIndex = NextBlockIndex; BlockIndex >= 0; BlockIndex--)
+            {
+                ulong BlockSize = 1UL << Blocks[BlockIndex].Order;
+
+                while (BaseAddress - BlockSize >= Address)
+                {
+                    BaseAddress -= BlockSize;
+
+                    FreeRegion(BaseAddress);
+                }
+            }
+
+            //Free region between aligned region end and End Address.
+            BaseAddress = EndAddrTruncated;
+
+            for (BlockIndex = NextBlockIndex; BlockIndex >= 0; BlockIndex--)
+            {
+                ulong BlockSize = 1UL << Blocks[BlockIndex].Order;
+
+                while (BaseAddress + BlockSize <= EndAddr)
+                {
+                    FreeRegion(BaseAddress);
+
+                    BaseAddress += BlockSize;
+                }
+            }
+        }
+
+        public ulong GetFreePages()
+        {
+            lock (Blocks)
+            {
+                return GetFreePagesImpl();
+            }
+        }
+
+        private ulong GetFreePagesImpl()
+        {
+            ulong AvailablePages = 0;
+
+            for (int BlockIndex = 0; BlockIndex < BlockOrdersCount; BlockIndex++)
+            {
+                KMemoryRegionBlock Block = Blocks[BlockIndex];
+
+                ulong BlockPagesCount = (1UL << Block.Order) / KMemoryManager.PageSize;
+
+                AvailablePages += BlockPagesCount * Block.FreeCount;
+            }
+
+            return AvailablePages;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KPageList.cs b/Ryujinx.HLE/HOS/Kernel/KPageList.cs
new file mode 100644
index 00000000..05162323
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KPageList.cs
@@ -0,0 +1,80 @@
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+    class KPageList : IEnumerable<KPageNode>
+    {
+        public LinkedList<KPageNode> Nodes { get; private set; }
+
+        public KPageList()
+        {
+            Nodes = new LinkedList<KPageNode>();
+        }
+
+        public KernelResult AddRange(ulong Address, ulong PagesCount)
+        {
+            if (PagesCount != 0)
+            {
+                if (Nodes.Last != null)
+                {
+                    KPageNode LastNode = Nodes.Last.Value;
+
+                    if (LastNode.Address + LastNode.PagesCount * KMemoryManager.PageSize == Address)
+                    {
+                        Address     = LastNode.Address;
+                        PagesCount += LastNode.PagesCount;
+
+                        Nodes.RemoveLast();
+                    }
+                }
+
+                Nodes.AddLast(new KPageNode(Address, PagesCount));
+            }
+
+            return KernelResult.Success;
+        }
+
+        public ulong GetPagesCount()
+        {
+            ulong Sum = 0;
+
+            foreach (KPageNode Node in Nodes)
+            {
+                Sum += Node.PagesCount;
+            }
+
+            return Sum;
+        }
+
+        public bool IsEqual(KPageList Other)
+        {
+            LinkedListNode<KPageNode> ThisNode  = Nodes.First;
+            LinkedListNode<KPageNode> OtherNode = Other.Nodes.First;
+
+            while (ThisNode != null && OtherNode != null)
+            {
+                if (ThisNode.Value.Address    != OtherNode.Value.Address ||
+                    ThisNode.Value.PagesCount != OtherNode.Value.PagesCount)
+                {
+                    return false;
+                }
+
+                ThisNode  = ThisNode.Next;
+                OtherNode = OtherNode.Next;
+            }
+
+            return ThisNode == null && OtherNode == null;
+        }
+
+        public IEnumerator<KPageNode> GetEnumerator()
+        {
+            return Nodes.GetEnumerator();
+        }
+
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return GetEnumerator();
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KPageNode.cs b/Ryujinx.HLE/HOS/Kernel/KPageNode.cs
new file mode 100644
index 00000000..6cecab2e
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KPageNode.cs
@@ -0,0 +1,14 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+    struct KPageNode
+    {
+        public ulong Address;
+        public ulong PagesCount;
+
+        public KPageNode(ulong Address, ulong PagesCount)
+        {
+            this.Address    = Address;
+            this.PagesCount = PagesCount;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KPort.cs b/Ryujinx.HLE/HOS/Kernel/KPort.cs
new file mode 100644
index 00000000..598f3a32
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KPort.cs
@@ -0,0 +1,26 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+    class KPort : KAutoObject
+    {
+        public KServerPort ServerPort { get; private set; }
+        public KClientPort ClientPort { get; private set; }
+
+        private long NameAddress;
+        private bool IsLight;
+
+        public KPort(Horizon System) : base(System)
+        {
+            ServerPort = new KServerPort(System);
+            ClientPort = new KClientPort(System);
+        }
+
+        public void Initialize(int MaxSessions, bool IsLight, long NameAddress)
+        {
+            ServerPort.Initialize(this);
+            ClientPort.Initialize(this, MaxSessions);
+
+            this.IsLight     = IsLight;
+            this.NameAddress = NameAddress;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KProcess.cs b/Ryujinx.HLE/HOS/Kernel/KProcess.cs
new file mode 100644
index 00000000..094ef222
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KProcess.cs
@@ -0,0 +1,1013 @@
+using ChocolArm64;
+using ChocolArm64.Events;
+using ChocolArm64.Memory;
+using Ryujinx.Common;
+using Ryujinx.Common.Logging;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+    class KProcess : KSynchronizationObject
+    {
+        public const int KernelVersionMajor    = 10;
+        public const int KernelVersionMinor    = 4;
+        public const int KernelVersionRevision = 0;
+
+        public const int KernelVersionPacked =
+            (KernelVersionMajor    << 19) |
+            (KernelVersionMinor    << 15) |
+            (KernelVersionRevision << 0);
+
+        public KMemoryManager MemoryManager { get; private set; }
+
+        private SortedDictionary<ulong, KTlsPageInfo> FullTlsPages;
+        private SortedDictionary<ulong, KTlsPageInfo> FreeTlsPages;
+
+        public int DefaultCpuCore { get; private set; }
+
+        public bool Debug { get; private set; }
+
+        public KResourceLimit ResourceLimit { get; private set; }
+
+        public ulong PersonalMmHeapPagesCount { get; private set; }
+
+        private ProcessState State;
+
+        private object ProcessLock;
+        private object ThreadingLock;
+
+        public KAddressArbiter AddressArbiter { get; private set; }
+
+        public long[] RandomEntropy { get; private set; }
+
+        private bool Signaled;
+        private bool UseSystemMemBlocks;
+
+        public string Name { get; private set; }
+
+        private int ThreadCount;
+
+        public int MmuFlags { get; private set; }
+
+        private MemoryRegion MemRegion;
+
+        public KProcessCapabilities Capabilities { get; private set; }
+
+        public long TitleId { get; private set; }
+        public long Pid     { get; private set; }
+
+        private long  CreationTimestamp;
+        private ulong Entrypoint;
+        private ulong ImageSize;
+        private ulong MainThreadStackSize;
+        private ulong MemoryUsageCapacity;
+        private int   Category;
+
+        public KHandleTable HandleTable { get; private set; }
+
+        public ulong UserExceptionContextAddress { get; private set; }
+
+        private LinkedList<KThread> Threads;
+
+        public bool IsPaused { get; private set; }
+
+        public Translator Translator { get; private set; }
+
+        public MemoryManager CpuMemory { get; private set; }
+
+        private SvcHandler SvcHandler;
+
+        public HleProcessDebugger Debugger { get; private set; }
+
+        public KProcess(Horizon System) : base(System)
+        {
+            ProcessLock   = new object();
+            ThreadingLock = new object();
+
+            CpuMemory = new MemoryManager(System.Device.Memory.RamPointer);
+
+            CpuMemory.InvalidAccess += InvalidAccessHandler;
+
+            AddressArbiter = new KAddressArbiter(System);
+
+            MemoryManager = new KMemoryManager(System, CpuMemory);
+
+            FullTlsPages = new SortedDictionary<ulong, KTlsPageInfo>();
+            FreeTlsPages = new SortedDictionary<ulong, KTlsPageInfo>();
+
+            Capabilities = new KProcessCapabilities();
+
+            RandomEntropy = new long[KScheduler.CpuCoresCount];
+
+            Threads = new LinkedList<KThread>();
+
+            Translator = new Translator();
+
+            Translator.CpuTrace += CpuTraceHandler;
+
+            SvcHandler = new SvcHandler(System.Device, this);
+
+            Debugger = new HleProcessDebugger(this);
+        }
+
+        public KernelResult InitializeKip(
+            ProcessCreationInfo CreationInfo,
+            int[]               Caps,
+            KPageList           PageList,
+            KResourceLimit      ResourceLimit,
+            MemoryRegion        MemRegion)
+        {
+            this.ResourceLimit = ResourceLimit;
+            this.MemRegion     = MemRegion;
+
+            AddressSpaceType AddrSpaceType = (AddressSpaceType)((CreationInfo.MmuFlags >> 1) & 7);
+
+            bool AslrEnabled = ((CreationInfo.MmuFlags >> 5) & 1) != 0;
+
+            ulong CodeAddress = CreationInfo.CodeAddress;
+
+            ulong CodeSize = (ulong)CreationInfo.CodePagesCount * KMemoryManager.PageSize;
+
+            KMemoryBlockAllocator MemoryBlockAllocator = (MmuFlags & 0x40) != 0
+                ? System.LargeMemoryBlockAllocator
+                : System.SmallMemoryBlockAllocator;
+
+            KernelResult Result = MemoryManager.InitializeForProcess(
+                AddrSpaceType,
+                AslrEnabled,
+                !AslrEnabled,
+                MemRegion,
+                CodeAddress,
+                CodeSize,
+                MemoryBlockAllocator);
+
+            if (Result != KernelResult.Success)
+            {
+                return Result;
+            }
+
+            if (!ValidateCodeAddressAndSize(CodeAddress, CodeSize))
+            {
+                return KernelResult.InvalidMemRange;
+            }
+
+            Result = MemoryManager.MapPages(
+                CodeAddress,
+                PageList,
+                MemoryState.CodeStatic,
+                MemoryPermission.None);
+
+            if (Result != KernelResult.Success)
+            {
+                return Result;
+            }
+
+            Result = Capabilities.InitializeForKernel(Caps, MemoryManager);
+
+            if (Result != KernelResult.Success)
+            {
+                return Result;
+            }
+
+            Pid = System.GetKipId();
+
+            if (Pid == 0 || (ulong)Pid >= Horizon.InitialProcessId)
+            {
+                throw new InvalidOperationException($"Invalid KIP Id {Pid}.");
+            }
+
+            Result = ParseProcessInfo(CreationInfo);
+
+            return Result;
+        }
+
+        public KernelResult Initialize(
+            ProcessCreationInfo CreationInfo,
+            int[]               Caps,
+            KResourceLimit      ResourceLimit,
+            MemoryRegion        MemRegion)
+        {
+            this.ResourceLimit = ResourceLimit;
+            this.MemRegion     = MemRegion;
+
+            ulong PersonalMmHeapSize = GetPersonalMmHeapSize((ulong)CreationInfo.PersonalMmHeapPagesCount, MemRegion);
+
+            ulong CodePagesCount = (ulong)CreationInfo.CodePagesCount;
+
+            ulong NeededSizeForProcess = PersonalMmHeapSize + CodePagesCount * KMemoryManager.PageSize;
+
+            if (NeededSizeForProcess != 0 && ResourceLimit != null)
+            {
+                if (!ResourceLimit.Reserve(LimitableResource.Memory, NeededSizeForProcess))
+                {
+                    return KernelResult.ResLimitExceeded;
+                }
+            }
+
+            void CleanUpForError()
+            {
+                if (NeededSizeForProcess != 0 && ResourceLimit != null)
+                {
+                    ResourceLimit.Release(LimitableResource.Memory, NeededSizeForProcess);
+                }
+            }
+
+            PersonalMmHeapPagesCount = (ulong)CreationInfo.PersonalMmHeapPagesCount;
+
+            KMemoryBlockAllocator MemoryBlockAllocator;
+
+            if (PersonalMmHeapPagesCount != 0)
+            {
+                MemoryBlockAllocator = new KMemoryBlockAllocator(PersonalMmHeapPagesCount * KMemoryManager.PageSize);
+            }
+            else
+            {
+                MemoryBlockAllocator = (MmuFlags & 0x40) != 0
+                    ? System.LargeMemoryBlockAllocator
+                    : System.SmallMemoryBlockAllocator;
+            }
+
+            AddressSpaceType AddrSpaceType = (AddressSpaceType)((CreationInfo.MmuFlags >> 1) & 7);
+
+            bool AslrEnabled = ((CreationInfo.MmuFlags >> 5) & 1) != 0;
+
+            ulong CodeAddress = CreationInfo.CodeAddress;
+
+            ulong CodeSize = CodePagesCount * KMemoryManager.PageSize;
+
+            KernelResult Result = MemoryManager.InitializeForProcess(
+                AddrSpaceType,
+                AslrEnabled,
+                !AslrEnabled,
+                MemRegion,
+                CodeAddress,
+                CodeSize,
+                MemoryBlockAllocator);
+
+            if (Result != KernelResult.Success)
+            {
+                CleanUpForError();
+
+                return Result;
+            }
+
+            if (!ValidateCodeAddressAndSize(CodeAddress, CodeSize))
+            {
+                CleanUpForError();
+
+                return KernelResult.InvalidMemRange;
+            }
+
+            Result = MemoryManager.MapNewProcessCode(
+                CodeAddress,
+                CodePagesCount,
+                MemoryState.CodeStatic,
+                MemoryPermission.None);
+
+            if (Result != KernelResult.Success)
+            {
+                CleanUpForError();
+
+                return Result;
+            }
+
+            Result = Capabilities.InitializeForUser(Caps, MemoryManager);
+
+            if (Result != KernelResult.Success)
+            {
+                CleanUpForError();
+
+                return Result;
+            }
+
+            Pid = System.GetProcessId();
+
+            if (Pid == -1 || (ulong)Pid < Horizon.InitialProcessId)
+            {
+                throw new InvalidOperationException($"Invalid Process Id {Pid}.");
+            }
+
+            Result = ParseProcessInfo(CreationInfo);
+
+            if (Result != KernelResult.Success)
+            {
+                CleanUpForError();
+            }
+
+            return Result;
+        }
+
+        private bool ValidateCodeAddressAndSize(ulong Address, ulong Size)
+        {
+            ulong CodeRegionStart;
+            ulong CodeRegionSize;
+
+            switch (MemoryManager.AddrSpaceWidth)
+            {
+                case 32:
+                    CodeRegionStart = 0x200000;
+                    CodeRegionSize  = 0x3fe00000;
+                    break;
+
+                case 36:
+                    CodeRegionStart = 0x8000000;
+                    CodeRegionSize  = 0x78000000;
+                    break;
+
+                case 39:
+                    CodeRegionStart = 0x8000000;
+                    CodeRegionSize  = 0x7ff8000000;
+                    break;
+
+                default: throw new InvalidOperationException("Invalid address space width on memory manager.");
+            }
+
+            ulong EndAddr = Address + Size;
+
+            ulong CodeRegionEnd = CodeRegionStart + CodeRegionSize;
+
+            if (EndAddr     <= Address ||
+                EndAddr - 1 >  CodeRegionEnd - 1)
+            {
+                return false;
+            }
+
+            if (MemoryManager.InsideHeapRegion (Address, Size) ||
+                MemoryManager.InsideAliasRegion(Address, Size))
+            {
+                return false;
+            }
+
+            return true;
+        }
+
+        private KernelResult ParseProcessInfo(ProcessCreationInfo CreationInfo)
+        {
+            //Ensure that the current kernel version is equal or above to the minimum required.
+            uint RequiredKernelVersionMajor =  (uint)Capabilities.KernelReleaseVersion >> 19;
+            uint RequiredKernelVersionMinor = ((uint)Capabilities.KernelReleaseVersion >> 15) & 0xf;
+
+            if (System.EnableVersionChecks)
+            {
+                if (RequiredKernelVersionMajor > KernelVersionMajor)
+                {
+                    return KernelResult.InvalidCombination;
+                }
+
+                if (RequiredKernelVersionMajor != KernelVersionMajor && RequiredKernelVersionMajor < 3)
+                {
+                    return KernelResult.InvalidCombination;
+                }
+
+                if (RequiredKernelVersionMinor > KernelVersionMinor)
+                {
+                    return KernelResult.InvalidCombination;
+                }
+            }
+
+            KernelResult Result = AllocateThreadLocalStorage(out ulong UserExceptionContextAddress);
+
+            if (Result != KernelResult.Success)
+            {
+                return Result;
+            }
+
+            this.UserExceptionContextAddress = UserExceptionContextAddress;
+
+            MemoryHelper.FillWithZeros(CpuMemory, (long)UserExceptionContextAddress, KTlsPageInfo.TlsEntrySize);
+
+            Name = CreationInfo.Name;
+
+            State = ProcessState.Created;
+
+            CreationTimestamp = PerformanceCounter.ElapsedMilliseconds;
+
+            MmuFlags   = CreationInfo.MmuFlags;
+            Category   = CreationInfo.Category;
+            TitleId    = CreationInfo.TitleId;
+            Entrypoint = CreationInfo.CodeAddress;
+            ImageSize  = (ulong)CreationInfo.CodePagesCount * KMemoryManager.PageSize;
+
+            UseSystemMemBlocks = ((MmuFlags >> 6) & 1) != 0;
+
+            switch ((AddressSpaceType)((MmuFlags >> 1) & 7))
+            {
+                case AddressSpaceType.Addr32Bits:
+                case AddressSpaceType.Addr36Bits:
+                case AddressSpaceType.Addr39Bits:
+                    MemoryUsageCapacity = MemoryManager.HeapRegionEnd -
+                                          MemoryManager.HeapRegionStart;
+                    break;
+
+                case AddressSpaceType.Addr32BitsNoMap:
+                    MemoryUsageCapacity = MemoryManager.HeapRegionEnd -
+                                          MemoryManager.HeapRegionStart +
+                                          MemoryManager.AliasRegionEnd -
+                                          MemoryManager.AliasRegionStart;
+                    break;
+
+                default: throw new InvalidOperationException($"Invalid MMU flags value 0x{MmuFlags:x2}.");
+            }
+
+            GenerateRandomEntropy();
+
+            return KernelResult.Success;
+        }
+
+        public KernelResult AllocateThreadLocalStorage(out ulong Address)
+        {
+            System.CriticalSection.Enter();
+
+            KernelResult Result;
+
+            if (FreeTlsPages.Count > 0)
+            {
+                //If we have free TLS pages available, just use the first one.
+                KTlsPageInfo PageInfo = FreeTlsPages.Values.First();
+
+                if (!PageInfo.TryGetFreePage(out Address))
+                {
+                    throw new InvalidOperationException("Unexpected failure getting free TLS page!");
+                }
+
+                if (PageInfo.IsFull())
+                {
+                    FreeTlsPages.Remove(PageInfo.PageAddr);
+
+                    FullTlsPages.Add(PageInfo.PageAddr, PageInfo);
+                }
+
+                Result = KernelResult.Success;
+            }
+            else
+            {
+                //Otherwise, we need to create a new one.
+                Result = AllocateTlsPage(out KTlsPageInfo PageInfo);
+
+                if (Result == KernelResult.Success)
+                {
+                    if (!PageInfo.TryGetFreePage(out Address))
+                    {
+                        throw new InvalidOperationException("Unexpected failure getting free TLS page!");
+                    }
+
+                    FreeTlsPages.Add(PageInfo.PageAddr, PageInfo);
+                }
+                else
+                {
+                    Address = 0;
+                }
+            }
+
+            System.CriticalSection.Leave();
+
+            return Result;
+        }
+
+        private KernelResult AllocateTlsPage(out KTlsPageInfo PageInfo)
+        {
+            PageInfo = default(KTlsPageInfo);
+
+            if (!System.UserSlabHeapPages.TryGetItem(out ulong TlsPagePa))
+            {
+                return KernelResult.OutOfMemory;
+            }
+
+            ulong RegionStart = MemoryManager.TlsIoRegionStart;
+            ulong RegionSize  = MemoryManager.TlsIoRegionEnd - RegionStart;
+
+            ulong RegionPagesCount = RegionSize / KMemoryManager.PageSize;
+
+            KernelResult Result = MemoryManager.AllocateOrMapPa(
+                1,
+                KMemoryManager.PageSize,
+                TlsPagePa,
+                true,
+                RegionStart,
+                RegionPagesCount,
+                MemoryState.ThreadLocal,
+                MemoryPermission.ReadAndWrite,
+                out ulong TlsPageVa);
+
+            if (Result != KernelResult.Success)
+            {
+                System.UserSlabHeapPages.Free(TlsPagePa);
+            }
+            else
+            {
+                PageInfo = new KTlsPageInfo(TlsPageVa);
+
+                MemoryHelper.FillWithZeros(CpuMemory, (long)TlsPageVa, KMemoryManager.PageSize);
+            }
+
+            return Result;
+        }
+
+        public KernelResult FreeThreadLocalStorage(ulong TlsSlotAddr)
+        {
+            ulong TlsPageAddr = BitUtils.AlignDown(TlsSlotAddr, KMemoryManager.PageSize);
+
+            System.CriticalSection.Enter();
+
+            KernelResult Result = KernelResult.Success;
+
+            KTlsPageInfo PageInfo = null;
+
+            if (FullTlsPages.TryGetValue(TlsPageAddr, out PageInfo))
+            {
+                //TLS page was full, free slot and move to free pages tree.
+                FullTlsPages.Remove(TlsPageAddr);
+
+                FreeTlsPages.Add(TlsPageAddr, PageInfo);
+            }
+            else if (!FreeTlsPages.TryGetValue(TlsPageAddr, out PageInfo))
+            {
+                Result = KernelResult.InvalidAddress;
+            }
+
+            if (PageInfo != null)
+            {
+                PageInfo.FreeTlsSlot(TlsSlotAddr);
+
+                if (PageInfo.IsEmpty())
+                {
+                    //TLS page is now empty, we should ensure it is removed
+                    //from all trees, and free the memory it was using.
+                    FreeTlsPages.Remove(TlsPageAddr);
+
+                    System.CriticalSection.Leave();
+
+                    FreeTlsPage(PageInfo);
+
+                    return KernelResult.Success;
+                }
+            }
+
+            System.CriticalSection.Leave();
+
+            return Result;
+        }
+
+        private KernelResult FreeTlsPage(KTlsPageInfo PageInfo)
+        {
+            KernelResult Result = MemoryManager.ConvertVaToPa(PageInfo.PageAddr, out ulong TlsPagePa);
+
+            if (Result != KernelResult.Success)
+            {
+                throw new InvalidOperationException("Unexpected failure translating virtual address to physical.");
+            }
+
+            Result = MemoryManager.UnmapForKernel(PageInfo.PageAddr, 1, MemoryState.ThreadLocal);
+
+            if (Result == KernelResult.Success)
+            {
+                System.UserSlabHeapPages.Free(TlsPagePa);
+            }
+
+            return Result;
+        }
+
+        private void GenerateRandomEntropy()
+        {
+            //TODO.
+        }
+
+        public KernelResult Start(int MainThreadPriority, ulong StackSize)
+        {
+            lock (ProcessLock)
+            {
+                if (State > ProcessState.CreatedAttached)
+                {
+                    return KernelResult.InvalidState;
+                }
+
+                if (ResourceLimit != null && !ResourceLimit.Reserve(LimitableResource.Thread, 1))
+                {
+                    return KernelResult.ResLimitExceeded;
+                }
+
+                KResourceLimit ThreadResourceLimit = ResourceLimit;
+                KResourceLimit MemoryResourceLimit = null;
+
+                if (MainThreadStackSize != 0)
+                {
+                    throw new InvalidOperationException("Trying to start a process with a invalid state!");
+                }
+
+                ulong StackSizeRounded = BitUtils.AlignUp(StackSize, KMemoryManager.PageSize);
+
+                ulong NeededSize = StackSizeRounded + ImageSize;
+
+                //Check if the needed size for the code and the stack will fit on the
+                //memory usage capacity of this Process. Also check for possible overflow
+                //on the above addition.
+                if (NeededSize > MemoryUsageCapacity ||
+                    NeededSize < StackSizeRounded)
+                {
+                    ThreadResourceLimit?.Release(LimitableResource.Thread, 1);
+
+                    return KernelResult.OutOfMemory;
+                }
+
+                if (StackSizeRounded != 0 && ResourceLimit != null)
+                {
+                    MemoryResourceLimit = ResourceLimit;
+
+                    if (!MemoryResourceLimit.Reserve(LimitableResource.Memory, StackSizeRounded))
+                    {
+                        ThreadResourceLimit?.Release(LimitableResource.Thread, 1);
+
+                        return KernelResult.ResLimitExceeded;
+                    }
+                }
+
+                KernelResult Result;
+
+                KThread MainThread = null;
+
+                ulong StackTop = 0;
+
+                void CleanUpForError()
+                {
+                    MainThread?.Terminate();
+                    HandleTable.Destroy();
+
+                    if (MainThreadStackSize != 0)
+                    {
+                        ulong StackBottom = StackTop - MainThreadStackSize;
+
+                        ulong StackPagesCount = MainThreadStackSize / KMemoryManager.PageSize;
+
+                        MemoryManager.UnmapForKernel(StackBottom, StackPagesCount, MemoryState.Stack);
+                    }
+
+                    MemoryResourceLimit?.Release(LimitableResource.Memory, StackSizeRounded);
+                    ThreadResourceLimit?.Release(LimitableResource.Thread, 1);
+                }
+
+                if (StackSizeRounded != 0)
+                {
+                    ulong StackPagesCount = StackSizeRounded / KMemoryManager.PageSize;
+
+                    ulong RegionStart = MemoryManager.StackRegionStart;
+                    ulong RegionSize  = MemoryManager.StackRegionEnd - RegionStart;
+
+                    ulong RegionPagesCount = RegionSize / KMemoryManager.PageSize;
+
+                    Result = MemoryManager.AllocateOrMapPa(
+                        StackPagesCount,
+                        KMemoryManager.PageSize,
+                        0,
+                        false,
+                        RegionStart,
+                        RegionPagesCount,
+                        MemoryState.Stack,
+                        MemoryPermission.ReadAndWrite,
+                        out ulong StackBottom);
+
+                    if (Result != KernelResult.Success)
+                    {
+                        CleanUpForError();
+
+                        return Result;
+                    }
+
+                    MainThreadStackSize += StackSizeRounded;
+
+                    StackTop = StackBottom + StackSizeRounded;
+                }
+
+                ulong HeapCapacity = MemoryUsageCapacity - MainThreadStackSize - ImageSize;
+
+                Result = MemoryManager.SetHeapCapacity(HeapCapacity);
+
+                if (Result != KernelResult.Success)
+                {
+                    CleanUpForError();
+
+                    return Result;
+                }
+
+                HandleTable = new KHandleTable(System);
+
+                Result = HandleTable.Initialize(Capabilities.HandleTableSize);
+
+                if (Result != KernelResult.Success)
+                {
+                    CleanUpForError();
+
+                    return Result;
+                }
+
+                MainThread = new KThread(System);
+
+                Result = MainThread.Initialize(
+                    Entrypoint,
+                    0,
+                    StackTop,
+                    MainThreadPriority,
+                    DefaultCpuCore,
+                    this);
+
+                if (Result != KernelResult.Success)
+                {
+                    CleanUpForError();
+
+                    return Result;
+                }
+
+                Result = HandleTable.GenerateHandle(MainThread, out int MainThreadHandle);
+
+                if (Result != KernelResult.Success)
+                {
+                    CleanUpForError();
+
+                    return Result;
+                }
+
+                MainThread.SetEntryArguments(0, MainThreadHandle);
+
+                ProcessState OldState = State;
+                ProcessState NewState = State != ProcessState.Created
+                    ? ProcessState.Attached
+                    : ProcessState.Started;
+
+                SetState(NewState);
+
+                //TODO: We can't call KThread.Start from a non-guest thread.
+                //We will need to make some changes to allow the creation of
+                //dummy threads that will be used to initialize the current
+                //thread on KCoreContext so that GetCurrentThread doesn't fail.
+                /* Result = MainThread.Start();
+
+                if (Result != KernelResult.Success)
+                {
+                    SetState(OldState);
+
+                    CleanUpForError();
+                } */
+
+                MainThread.Reschedule(ThreadSchedState.Running);
+
+                return Result;
+            }
+        }
+
+        private void SetState(ProcessState NewState)
+        {
+            if (State != NewState)
+            {
+                State    = NewState;
+                Signaled = true;
+
+                Signal();
+            }
+        }
+
+        public KernelResult InitializeThread(
+            KThread Thread,
+            ulong   Entrypoint,
+            ulong   ArgsPtr,
+            ulong   StackTop,
+            int     Priority,
+            int     CpuCore)
+        {
+            lock (ProcessLock)
+            {
+                return Thread.Initialize(Entrypoint, ArgsPtr, StackTop, Priority, CpuCore, this);
+            }
+        }
+
+        public void SubscribeThreadEventHandlers(CpuThread Context)
+        {
+            Context.ThreadState.Interrupt += InterruptHandler;
+            Context.ThreadState.SvcCall   += SvcHandler.SvcCall;
+        }
+
+        private void InterruptHandler(object sender, EventArgs e)
+        {
+            System.Scheduler.ContextSwitch();
+        }
+
+        public void IncrementThreadCount()
+        {
+            Interlocked.Increment(ref ThreadCount);
+
+            System.ThreadCounter.AddCount();
+        }
+
+        public void DecrementThreadCountAndTerminateIfZero()
+        {
+            System.ThreadCounter.Signal();
+
+            if (Interlocked.Decrement(ref ThreadCount) == 0)
+            {
+                Terminate();
+            }
+        }
+
+        public ulong GetMemoryCapacity()
+        {
+            ulong TotalCapacity = (ulong)ResourceLimit.GetRemainingValue(LimitableResource.Memory);
+
+            TotalCapacity += MemoryManager.GetTotalHeapSize();
+
+            TotalCapacity += GetPersonalMmHeapSize();
+
+            TotalCapacity += ImageSize + MainThreadStackSize;
+
+            if (TotalCapacity <= MemoryUsageCapacity)
+            {
+                return TotalCapacity;
+            }
+
+            return MemoryUsageCapacity;
+        }
+
+        public ulong GetMemoryUsage()
+        {
+            return ImageSize + MainThreadStackSize + MemoryManager.GetTotalHeapSize() + GetPersonalMmHeapSize();
+        }
+
+        public ulong GetMemoryCapacityWithoutPersonalMmHeap()
+        {
+            return GetMemoryCapacity() - GetPersonalMmHeapSize();
+        }
+
+        public ulong GetMemoryUsageWithoutPersonalMmHeap()
+        {
+            return GetMemoryUsage() - GetPersonalMmHeapSize();
+        }
+
+        private ulong GetPersonalMmHeapSize()
+        {
+            return GetPersonalMmHeapSize(PersonalMmHeapPagesCount, MemRegion);
+        }
+
+        private static ulong GetPersonalMmHeapSize(ulong PersonalMmHeapPagesCount, MemoryRegion MemRegion)
+        {
+            if (MemRegion == MemoryRegion.Applet)
+            {
+                return 0;
+            }
+
+            return PersonalMmHeapPagesCount * KMemoryManager.PageSize;
+        }
+
+        public void AddThread(KThread Thread)
+        {
+            lock (ThreadingLock)
+            {
+                Thread.ProcessListNode = Threads.AddLast(Thread);
+            }
+        }
+
+        public void RemoveThread(KThread Thread)
+        {
+            lock (ThreadingLock)
+            {
+                Threads.Remove(Thread.ProcessListNode);
+            }
+        }
+
+        public bool IsCpuCoreAllowed(int Core)
+        {
+            return (Capabilities.AllowedCpuCoresMask & (1L << Core)) != 0;
+        }
+
+        public bool IsPriorityAllowed(int Priority)
+        {
+            return (Capabilities.AllowedThreadPriosMask & (1L << Priority)) != 0;
+        }
+
+        public override bool IsSignaled()
+        {
+            return Signaled;
+        }
+
+        public KernelResult Terminate()
+        {
+            KernelResult Result;
+
+            bool ShallTerminate = false;
+
+            System.CriticalSection.Enter();
+
+            lock (ProcessLock)
+            {
+                if (State >= ProcessState.Started)
+                {
+                    if (State == ProcessState.Started  ||
+                        State == ProcessState.Crashed  ||
+                        State == ProcessState.Attached ||
+                        State == ProcessState.DebugSuspended)
+                    {
+                        SetState(ProcessState.Exiting);
+
+                        ShallTerminate = true;
+                    }
+
+                    Result = KernelResult.Success;
+                }
+                else
+                {
+                    Result = KernelResult.InvalidState;
+                }
+            }
+
+            System.CriticalSection.Leave();
+
+            if (ShallTerminate)
+            {
+                //UnpauseAndTerminateAllThreadsExcept(System.Scheduler.GetCurrentThread());
+
+                HandleTable.Destroy();
+
+                SignalExitForDebugEvent();
+                SignalExit();
+            }
+
+            return Result;
+        }
+
+        private void UnpauseAndTerminateAllThreadsExcept(KThread Thread)
+        {
+            //TODO.
+        }
+
+        private void SignalExitForDebugEvent()
+        {
+            //TODO: Debug events.
+        }
+
+        private void SignalExit()
+        {
+            if (ResourceLimit != null)
+            {
+                ResourceLimit.Release(LimitableResource.Memory, GetMemoryUsage());
+            }
+
+            System.CriticalSection.Enter();
+
+            SetState(ProcessState.Exited);
+
+            System.CriticalSection.Leave();
+        }
+
+        public KernelResult ClearIfNotExited()
+        {
+            KernelResult Result;
+
+            System.CriticalSection.Enter();
+
+            lock (ProcessLock)
+            {
+                if (State != ProcessState.Exited && Signaled)
+                {
+                    Signaled = false;
+
+                    Result = KernelResult.Success;
+                }
+                else
+                {
+                    Result = KernelResult.InvalidState;
+                }
+            }
+
+            System.CriticalSection.Leave();
+
+            return Result;
+        }
+
+        public void StopAllThreads()
+        {
+            lock (ThreadingLock)
+            {
+                foreach (KThread Thread in Threads)
+                {
+                    Thread.Context.StopExecution();
+
+                    System.Scheduler.CoreManager.Set(Thread.Context.Work);
+                }
+            }
+        }
+
+        private void InvalidAccessHandler(object sender, InvalidAccessEventArgs e)
+        {
+            PrintCurrentThreadStackTrace();
+        }
+
+        public void PrintCurrentThreadStackTrace()
+        {
+            System.Scheduler.GetCurrentThread().PrintGuestStackTrace();
+        }
+
+        private void CpuTraceHandler(object sender, CpuTraceEventArgs e)
+        {
+            Logger.PrintInfo(LogClass.Cpu, $"Executing at 0x{e.Position:X16}.");
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KProcessCapabilities.cs b/Ryujinx.HLE/HOS/Kernel/KProcessCapabilities.cs
new file mode 100644
index 00000000..dfbe1f36
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KProcessCapabilities.cs
@@ -0,0 +1,311 @@
+using Ryujinx.Common;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+    class KProcessCapabilities
+    {
+        public byte[] SvcAccessMask { get; private set; }
+        public byte[] IrqAccessMask { get; private set; }
+
+        public long AllowedCpuCoresMask    { get; private set; }
+        public long AllowedThreadPriosMask { get; private set; }
+
+        public int DebuggingFlags       { get; private set; }
+        public int HandleTableSize      { get; private set; }
+        public int KernelReleaseVersion { get; private set; }
+        public int ApplicationType      { get; private set; }
+
+        public KProcessCapabilities()
+        {
+            SvcAccessMask = new byte[0x10];
+            IrqAccessMask = new byte[0x80];
+        }
+
+        public KernelResult InitializeForKernel(int[] Caps, KMemoryManager MemoryManager)
+        {
+            AllowedCpuCoresMask    = 0xf;
+            AllowedThreadPriosMask = -1;
+            DebuggingFlags        &= ~3;
+            KernelReleaseVersion   = KProcess.KernelVersionPacked;
+
+            return Parse(Caps, MemoryManager);
+        }
+
+        public KernelResult InitializeForUser(int[] Caps, KMemoryManager MemoryManager)
+        {
+            return Parse(Caps, MemoryManager);
+        }
+
+        private KernelResult Parse(int[] Caps, KMemoryManager MemoryManager)
+        {
+            int Mask0 = 0;
+            int Mask1 = 0;
+
+            for (int Index = 0; Index < Caps.Length; Index++)
+            {
+                int Cap = Caps[Index];
+
+                if (((Cap + 1) & ~Cap) != 0x40)
+                {
+                    KernelResult Result = ParseCapability(Cap, ref Mask0, ref Mask1, MemoryManager);
+
+                    if (Result != KernelResult.Success)
+                    {
+                        return Result;
+                    }
+                }
+                else
+                {
+                    if ((uint)Index + 1 >= Caps.Length)
+                    {
+                        return KernelResult.InvalidCombination;
+                    }
+
+                    int PrevCap = Cap;
+
+                    Cap = Caps[++Index];
+
+                    if (((Cap + 1) & ~Cap) != 0x40)
+                    {
+                        return KernelResult.InvalidCombination;
+                    }
+
+                    if ((Cap & 0x78000000) != 0)
+                    {
+                        return KernelResult.MaximumExceeded;
+                    }
+
+                    if ((Cap & 0x7ffff80) == 0)
+                    {
+                        return KernelResult.InvalidSize;
+                    }
+
+                    long Address = ((long)(uint)PrevCap << 5) & 0xffffff000;
+                    long Size    = ((long)(uint)Cap     << 5) & 0xfffff000;
+
+                    if (((ulong)(Address + Size - 1) >> 36) != 0)
+                    {
+                        return KernelResult.InvalidAddress;
+                    }
+
+                    MemoryPermission Perm = (PrevCap >> 31) != 0
+                        ? MemoryPermission.Read
+                        : MemoryPermission.ReadAndWrite;
+
+                    KernelResult Result;
+
+                    if ((Cap >> 31) != 0)
+                    {
+                        Result = MemoryManager.MapNormalMemory(Address, Size, Perm);
+                    }
+                    else
+                    {
+                        Result = MemoryManager.MapIoMemory(Address, Size, Perm);
+                    }
+
+                    if (Result != KernelResult.Success)
+                    {
+                        return Result;
+                    }
+                }
+            }
+
+            return KernelResult.Success;
+        }
+
+        private KernelResult ParseCapability(int Cap, ref int Mask0, ref int Mask1, KMemoryManager MemoryManager)
+        {
+            int Code = (Cap + 1) & ~Cap;
+
+            if (Code == 1)
+            {
+                return KernelResult.InvalidCapability;
+            }
+            else if (Code == 0)
+            {
+                return KernelResult.Success;
+            }
+
+            int CodeMask = 1 << (32 - BitUtils.CountLeadingZeros32(Code + 1));
+
+            //Check if the property was already set.
+            if (((Mask0 & CodeMask) & 0x1e008) != 0)
+            {
+                return KernelResult.InvalidCombination;
+            }
+
+            Mask0 |= CodeMask;
+
+            switch (Code)
+            {
+                case 8:
+                {
+                    if (AllowedCpuCoresMask != 0 || AllowedThreadPriosMask != 0)
+                    {
+                        return KernelResult.InvalidCapability;
+                    }
+
+                    int LowestCpuCore  = (Cap >> 16) & 0xff;
+                    int HighestCpuCore = (Cap >> 24) & 0xff;
+
+                    if (LowestCpuCore > HighestCpuCore)
+                    {
+                        return KernelResult.InvalidCombination;
+                    }
+
+                    int HighestThreadPrio = (Cap >>  4) & 0x3f;
+                    int LowestThreadPrio  = (Cap >> 10) & 0x3f;
+
+                    if (LowestThreadPrio > HighestThreadPrio)
+                    {
+                        return KernelResult.InvalidCombination;
+                    }
+
+                    if (HighestCpuCore >= KScheduler.CpuCoresCount)
+                    {
+                        return KernelResult.InvalidCpuCore;
+                    }
+
+                    AllowedCpuCoresMask    = GetMaskFromMinMax(LowestCpuCore,    HighestCpuCore);
+                    AllowedThreadPriosMask = GetMaskFromMinMax(LowestThreadPrio, HighestThreadPrio);
+
+                    break;
+                }
+
+                case 0x10:
+                {
+                    int Slot = (Cap >> 29) & 7;
+
+                    int SvcSlotMask = 1 << Slot;
+
+                    if ((Mask1 & SvcSlotMask) != 0)
+                    {
+                        return KernelResult.InvalidCombination;
+                    }
+
+                    Mask1 |= SvcSlotMask;
+
+                    int SvcMask = (Cap >> 5) & 0xffffff;
+
+                    int BaseSvc = Slot * 24;
+
+                    for (int Index = 0; Index < 24; Index++)
+                    {
+                        if (((SvcMask >> Index) & 1) == 0)
+                        {
+                            continue;
+                        }
+
+                        int SvcId = BaseSvc + Index;
+
+                        if (SvcId > 0x7f)
+                        {
+                            return KernelResult.MaximumExceeded;
+                        }
+
+                        SvcAccessMask[SvcId / 8] |= (byte)(1 << (SvcId & 7));
+                    }
+
+                    break;
+                }
+
+                case 0x80:
+                {
+                    long Address = ((long)(uint)Cap << 4) & 0xffffff000;
+
+                    MemoryManager.MapIoMemory(Address, KMemoryManager.PageSize, MemoryPermission.ReadAndWrite);
+
+                    break;
+                }
+
+                case 0x800:
+                {
+                    //TODO: GIC distributor check.
+                    int Irq0 = (Cap >> 12) & 0x3ff;
+                    int Irq1 = (Cap >> 22) & 0x3ff;
+
+                    if (Irq0 != 0x3ff)
+                    {
+                        IrqAccessMask[Irq0 / 8] |= (byte)(1 << (Irq0 & 7));
+                    }
+
+                    if (Irq1 != 0x3ff)
+                    {
+                        IrqAccessMask[Irq1 / 8] |= (byte)(1 << (Irq1 & 7));
+                    }
+
+                    break;
+                }
+
+                case 0x2000:
+                {
+                    int ApplicationType = Cap >> 14;
+
+                    if ((uint)ApplicationType > 7)
+                    {
+                        return KernelResult.ReservedValue;
+                    }
+
+                    this.ApplicationType = ApplicationType;
+
+                    break;
+                }
+
+                case 0x4000:
+                {
+                    //Note: This check is bugged on kernel too, we are just replicating the bug here.
+                    if ((KernelReleaseVersion >> 17) != 0 || Cap < 0x80000)
+                    {
+                        return KernelResult.ReservedValue;
+                    }
+
+                    KernelReleaseVersion = Cap;
+
+                    break;
+                }
+
+                case 0x8000:
+                {
+                    int HandleTableSize = Cap >> 26;
+
+                    if ((uint)HandleTableSize > 0x3ff)
+                    {
+                        return KernelResult.ReservedValue;
+                    }
+
+                    this.HandleTableSize = HandleTableSize;
+
+                    break;
+                }
+
+                case 0x10000:
+                {
+                    int DebuggingFlags = Cap >> 19;
+
+                    if ((uint)DebuggingFlags > 3)
+                    {
+                        return KernelResult.ReservedValue;
+                    }
+
+                    this.DebuggingFlags &= ~3;
+                    this.DebuggingFlags |= DebuggingFlags;
+
+                    break;
+                }
+
+                default: return KernelResult.InvalidCapability;
+            }
+
+            return KernelResult.Success;
+        }
+
+        private static long GetMaskFromMinMax(int Min, int Max)
+        {
+            int Range = Max - Min + 1;
+
+            long Mask = (1L << Range) - 1;
+
+            return Mask << Min;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KReadableEvent.cs b/Ryujinx.HLE/HOS/Kernel/KReadableEvent.cs
index d43fe824..bfb8e7e2 100644
--- a/Ryujinx.HLE/HOS/Kernel/KReadableEvent.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KReadableEvent.cs
@@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Kernel
 
         public override void Signal()
         {
-            System.CriticalSectionLock.Lock();
+            System.CriticalSection.Enter();
 
             if (!Signaled)
             {
@@ -22,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Kernel
                 base.Signal();
             }
 
-            System.CriticalSectionLock.Unlock();
+            System.CriticalSection.Leave();
         }
 
         public KernelResult Clear()
@@ -36,7 +36,7 @@ namespace Ryujinx.HLE.HOS.Kernel
         {
             KernelResult Result;
 
-            System.CriticalSectionLock.Lock();
+            System.CriticalSection.Enter();
 
             if (Signaled)
             {
@@ -49,7 +49,7 @@ namespace Ryujinx.HLE.HOS.Kernel
                 Result = KernelResult.InvalidState;
             }
 
-            System.CriticalSectionLock.Unlock();
+            System.CriticalSection.Leave();
 
             return Result;
         }
diff --git a/Ryujinx.HLE/HOS/Kernel/KResourceLimit.cs b/Ryujinx.HLE/HOS/Kernel/KResourceLimit.cs
new file mode 100644
index 00000000..6fd70d0c
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KResourceLimit.cs
@@ -0,0 +1,146 @@
+using Ryujinx.Common;
+using System.Collections.Generic;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+    class KResourceLimit
+    {
+        private const int Time10SecondsMs = 10000;
+
+        private long[] Current;
+        private long[] Limit;
+        private long[] Available;
+
+        private object LockObj;
+
+        private LinkedList<KThread> WaitingThreads;
+
+        private int WaitingThreadsCount;
+
+        private Horizon System;
+
+        public KResourceLimit(Horizon System)
+        {
+            Current   = new long[(int)LimitableResource.Count];
+            Limit     = new long[(int)LimitableResource.Count];
+            Available = new long[(int)LimitableResource.Count];
+
+            LockObj = new object();
+
+            WaitingThreads = new LinkedList<KThread>();
+
+            this.System = System;
+        }
+
+        public bool Reserve(LimitableResource Resource, ulong Amount)
+        {
+            return Reserve(Resource, (long)Amount);
+        }
+
+        public bool Reserve(LimitableResource Resource, long Amount)
+        {
+            return Reserve(Resource, Amount, KTimeManager.ConvertMillisecondsToNanoseconds(Time10SecondsMs));
+        }
+
+        public bool Reserve(LimitableResource Resource, long Amount, long Timeout)
+        {
+            long EndTimePoint = KTimeManager.ConvertNanosecondsToMilliseconds(Timeout);
+
+            EndTimePoint += PerformanceCounter.ElapsedMilliseconds;
+
+            bool Success = false;
+
+            int Index = GetIndex(Resource);
+
+            lock (LockObj)
+            {
+                long NewCurrent = Current[Index] + Amount;
+
+                while (NewCurrent > Limit[Index] && Available[Index] + Amount <= Limit[Index])
+                {
+                    WaitingThreadsCount++;
+
+                    KConditionVariable.Wait(System, WaitingThreads, LockObj, Timeout);
+
+                    WaitingThreadsCount--;
+
+                    NewCurrent = Current[Index] + Amount;
+
+                    if (Timeout >= 0 && PerformanceCounter.ElapsedMilliseconds > EndTimePoint)
+                    {
+                        break;
+                    }
+                }
+
+                if (NewCurrent <= Limit[Index])
+                {
+                    Current[Index] = NewCurrent;
+
+                    Success = true;
+                }
+            }
+
+            return Success;
+        }
+
+        public void Release(LimitableResource Resource, ulong Amount)
+        {
+            Release(Resource, (long)Amount);
+        }
+
+        public void Release(LimitableResource Resource, long Amount)
+        {
+            Release(Resource, Amount, Amount);
+        }
+
+        private void Release(LimitableResource Resource, long UsedAmount, long AvailableAmount)
+        {
+            int Index = GetIndex(Resource);
+
+            lock (LockObj)
+            {
+                Current  [Index] -= UsedAmount;
+                Available[Index] -= AvailableAmount;
+
+                if (WaitingThreadsCount > 0)
+                {
+                    KConditionVariable.NotifyAll(System, WaitingThreads);
+                }
+            }
+        }
+
+        public long GetRemainingValue(LimitableResource Resource)
+        {
+            int Index = GetIndex(Resource);
+
+            lock (LockObj)
+            {
+                return Limit[Index] - Current[Index];
+            }
+        }
+
+        public KernelResult SetLimitValue(LimitableResource Resource, long Limit)
+        {
+            int Index = GetIndex(Resource);
+
+            lock (LockObj)
+            {
+                if (Current[Index] <= Limit)
+                {
+                    this.Limit[Index] = Limit;
+
+                    return KernelResult.Success;
+                }
+                else
+                {
+                    return KernelResult.InvalidState;
+                }
+            }
+        }
+
+        private static int GetIndex(LimitableResource Resource)
+        {
+            return (int)Resource;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KScheduler.cs b/Ryujinx.HLE/HOS/Kernel/KScheduler.cs
index 3cfda419..3342f4a6 100644
--- a/Ryujinx.HLE/HOS/Kernel/KScheduler.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KScheduler.cs
@@ -38,14 +38,14 @@ namespace Ryujinx.HLE.HOS.Kernel
 
         private void PreemptThreads()
         {
-            System.CriticalSectionLock.Lock();
+            System.CriticalSection.Enter();
 
             PreemptThread(PreemptionPriorityCores012, 0);
             PreemptThread(PreemptionPriorityCores012, 1);
             PreemptThread(PreemptionPriorityCores012, 2);
             PreemptThread(PreemptionPriorityCore3,    3);
 
-            System.CriticalSectionLock.Unlock();
+            System.CriticalSection.Leave();
         }
 
         private void PreemptThread(int Prio, int Core)
@@ -82,7 +82,7 @@ namespace Ryujinx.HLE.HOS.Kernel
                     }
 
                     //If the candidate was scheduled after the current thread, then it's not worth it.
-                    if (SelectedThread == null || SelectedThread.LastScheduledTicks >= Thread.LastScheduledTicks)
+                    if (SelectedThread == null || SelectedThread.LastScheduledTime >= Thread.LastScheduledTime)
                     {
                         yield return Thread;
                     }
@@ -212,6 +212,11 @@ namespace Ryujinx.HLE.HOS.Kernel
             throw new InvalidOperationException("Current thread is not scheduled!");
         }
 
+        public KProcess GetCurrentProcess()
+        {
+            return GetCurrentThread().Owner;
+        }
+
         public void Dispose()
         {
             Dispose(true);
diff --git a/Ryujinx.HLE/HOS/Kernel/KServerPort.cs b/Ryujinx.HLE/HOS/Kernel/KServerPort.cs
new file mode 100644
index 00000000..42135cd8
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KServerPort.cs
@@ -0,0 +1,14 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+    class KServerPort : KSynchronizationObject
+    {
+        private KPort Parent;
+
+        public KServerPort(Horizon System) : base(System) { }
+
+        public void Initialize(KPort Parent)
+        {
+            this.Parent = Parent;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KSharedMemory.cs b/Ryujinx.HLE/HOS/Kernel/KSharedMemory.cs
index cdd31667..a440438b 100644
--- a/Ryujinx.HLE/HOS/Kernel/KSharedMemory.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KSharedMemory.cs
@@ -1,14 +1,68 @@
+using Ryujinx.Common;
+
 namespace Ryujinx.HLE.HOS.Kernel
 {
     class KSharedMemory
     {
-        public long PA   { get; private set; }
-        public long Size { get; private set; }
+        private KPageList PageList;
 
-        public KSharedMemory(long PA, long Size)
+        private long OwnerPid;
+
+        private MemoryPermission OwnerPermission;
+        private MemoryPermission UserPermission;
+
+        public KSharedMemory(
+            KPageList        PageList,
+            long             OwnerPid,
+            MemoryPermission OwnerPermission,
+            MemoryPermission UserPermission)
         {
-            this.PA   = PA;
-            this.Size = Size;
+            this.PageList        = PageList;
+            this.OwnerPid        = OwnerPid;
+            this.OwnerPermission = OwnerPermission;
+            this.UserPermission  = UserPermission;
+        }
+
+        public KernelResult MapIntoProcess(
+            KMemoryManager   MemoryManager,
+            ulong            Address,
+            ulong            Size,
+            KProcess         Process,
+            MemoryPermission Permission)
+        {
+            ulong PagesCountRounded = BitUtils.DivRoundUp(Size, KMemoryManager.PageSize);
+
+            if (PageList.GetPagesCount() != PagesCountRounded)
+            {
+                return KernelResult.InvalidSize;
+            }
+
+            MemoryPermission ExpectedPermission = Process.Pid == OwnerPid
+                ? OwnerPermission
+                : UserPermission;
+
+            if (Permission != ExpectedPermission)
+            {
+                return KernelResult.InvalidPermission;
+            }
+
+            return MemoryManager.MapPages(Address, PageList, MemoryState.SharedMemory, Permission);
+        }
+
+        public KernelResult UnmapFromProcess(
+            KMemoryManager   MemoryManager,
+            ulong            Address,
+            ulong            Size,
+            KProcess         Process)
+        {
+            ulong PagesCountRounded = BitUtils.DivRoundUp(Size, KMemoryManager.PageSize);
+
+            if (PageList.GetPagesCount() != PagesCountRounded)
+            {
+                return KernelResult.InvalidSize;
+            }
+
+            return MemoryManager.UnmapPages(Address, PageList, MemoryState.SharedMemory);
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KSlabHeap.cs b/Ryujinx.HLE/HOS/Kernel/KSlabHeap.cs
new file mode 100644
index 00000000..2d6b3ca0
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KSlabHeap.cs
@@ -0,0 +1,50 @@
+using System.Collections.Generic;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+    class KSlabHeap
+    {
+        private LinkedList<ulong> Items;
+
+        public KSlabHeap(ulong Pa, ulong ItemSize, ulong Size)
+        {
+            Items = new LinkedList<ulong>();
+
+            int ItemsCount = (int)(Size / ItemSize);
+
+            for (int Index = 0; Index < ItemsCount; Index++)
+            {
+                Items.AddLast(Pa);
+
+                Pa += ItemSize;
+            }
+        }
+
+        public bool TryGetItem(out ulong Pa)
+        {
+            lock (Items)
+            {
+                if (Items.First != null)
+                {
+                    Pa = Items.First.Value;
+
+                    Items.RemoveFirst();
+
+                    return true;
+                }
+            }
+
+            Pa = 0;
+
+            return false;
+        }
+
+        public void Free(ulong Pa)
+        {
+            lock (Items)
+            {
+                Items.AddFirst(Pa);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KSynchronization.cs b/Ryujinx.HLE/HOS/Kernel/KSynchronization.cs
index 57a6296c..19e700f4 100644
--- a/Ryujinx.HLE/HOS/Kernel/KSynchronization.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KSynchronization.cs
@@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Kernel
         {
             long Result = MakeError(ErrorModule.Kernel, KernelErr.Timeout);
 
-            System.CriticalSectionLock.Lock();
+            System.CriticalSection.Enter();
 
             //Check if objects are already signaled before waiting.
             for (int Index = 0; Index < SyncObjs.Length; Index++)
@@ -29,14 +29,14 @@ namespace Ryujinx.HLE.HOS.Kernel
 
                 HndIndex = Index;
 
-                System.CriticalSectionLock.Unlock();
+                System.CriticalSection.Leave();
 
                 return 0;
             }
 
             if (Timeout == 0)
             {
-                System.CriticalSectionLock.Unlock();
+                System.CriticalSection.Leave();
 
                 return Result;
             }
@@ -74,7 +74,7 @@ namespace Ryujinx.HLE.HOS.Kernel
                     System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout);
                 }
 
-                System.CriticalSectionLock.Unlock();
+                System.CriticalSection.Leave();
 
                 CurrentThread.WaitingSync = false;
 
@@ -83,7 +83,7 @@ namespace Ryujinx.HLE.HOS.Kernel
                     System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
                 }
 
-                System.CriticalSectionLock.Lock();
+                System.CriticalSection.Enter();
 
                 Result = (uint)CurrentThread.ObjSyncResult;
 
@@ -100,14 +100,14 @@ namespace Ryujinx.HLE.HOS.Kernel
                 }
             }
 
-            System.CriticalSectionLock.Unlock();
+            System.CriticalSection.Leave();
 
             return Result;
         }
 
         public void SignalObject(KSynchronizationObject SyncObj)
         {
-            System.CriticalSectionLock.Lock();
+            System.CriticalSection.Enter();
 
             if (SyncObj.IsSignaled())
             {
@@ -117,7 +117,7 @@ namespace Ryujinx.HLE.HOS.Kernel
                 {
                     KThread Thread = Node.Value;
 
-                    if ((Thread.SchedFlags & ThreadSchedState.LowNibbleMask) == ThreadSchedState.Paused)
+                    if ((Thread.SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused)
                     {
                         Thread.SignaledObj   = SyncObj;
                         Thread.ObjSyncResult = 0;
@@ -129,7 +129,7 @@ namespace Ryujinx.HLE.HOS.Kernel
                 }
             }
 
-            System.CriticalSectionLock.Unlock();
+            System.CriticalSection.Leave();
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KSynchronizationObject.cs b/Ryujinx.HLE/HOS/Kernel/KSynchronizationObject.cs
index 28eac330..5ba7784f 100644
--- a/Ryujinx.HLE/HOS/Kernel/KSynchronizationObject.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KSynchronizationObject.cs
@@ -2,16 +2,12 @@ using System.Collections.Generic;
 
 namespace Ryujinx.HLE.HOS.Kernel
 {
-    class KSynchronizationObject
+    class KSynchronizationObject : KAutoObject
     {
         public LinkedList<KThread> WaitingThreads;
 
-        protected Horizon System;
-
-        public KSynchronizationObject(Horizon System)
+        public KSynchronizationObject(Horizon System) : base(System)
         {
-            this.System = System;
-
             WaitingThreads = new LinkedList<KThread>();
         }
 
diff --git a/Ryujinx.HLE/HOS/Kernel/KThread.cs b/Ryujinx.HLE/HOS/Kernel/KThread.cs
index 73ee2322..88f144c8 100644
--- a/Ryujinx.HLE/HOS/Kernel/KThread.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KThread.cs
@@ -1,4 +1,5 @@
 using ChocolArm64;
+using ChocolArm64.Memory;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -13,20 +14,30 @@ namespace Ryujinx.HLE.HOS.Kernel
 
         public long AffinityMask { get; set; }
 
-        public int ThreadId { get; private set; }
+        public long ThreadUid { get; private set; }
 
-        public KSynchronizationObject SignaledObj;
+        public long TotalTimeRunning { get; set; }
+
+        public KSynchronizationObject SignaledObj { get; set; }
 
         public long CondVarAddress { get; set; }
-        public long MutexAddress   { get; set; }
 
-        public Process Owner { get; private set; }
+        private ulong Entrypoint;
 
-        public long LastScheduledTicks { get; set; }
+        public long MutexAddress { get; set; }
+
+        public KProcess Owner { get; private set; }
+
+        private ulong TlsAddress;
+
+        public long LastScheduledTime { get; set; }
 
         public LinkedListNode<KThread>[] SiblingsPerCore { get; private set; }
 
-        private LinkedListNode<KThread> WithholderNode;
+        public LinkedList<KThread>     Withholder     { get; set; }
+        public LinkedListNode<KThread> WithholderNode { get; set; }
+
+        public LinkedListNode<KThread> ProcessListNode { get; set; }
 
         private LinkedList<KThread>     MutexWaiters;
         private LinkedListNode<KThread> MutexWaiterNode;
@@ -65,38 +76,131 @@ namespace Ryujinx.HLE.HOS.Kernel
 
         public long LastPc { get; set; }
 
-        public KThread(
-            CpuThread Thread,
-            Process   Process,
-            Horizon   System,
-            int       ProcessorId,
-            int       Priority,
-            int       ThreadId) : base(System)
+        public KThread(Horizon System) : base(System)
         {
-            this.ThreadId = ThreadId;
-
-            Context        = Thread;
-            Owner          = Process;
-            PreferredCore  = ProcessorId;
             Scheduler      = System.Scheduler;
             SchedulingData = System.Scheduler.SchedulingData;
 
             SiblingsPerCore = new LinkedListNode<KThread>[KScheduler.CpuCoresCount];
 
             MutexWaiters = new LinkedList<KThread>();
-
-            AffinityMask = 1 << ProcessorId;
-
-            DynamicPriority = BasePriority = Priority;
-
-            CurrentCore = PreferredCore;
         }
 
-        public long Start()
+        public KernelResult Initialize(
+            ulong      Entrypoint,
+            ulong      ArgsPtr,
+            ulong      StackTop,
+            int        Priority,
+            int        DefaultCpuCore,
+            KProcess   Owner,
+            ThreadType Type = ThreadType.User)
         {
-            long Result = MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
+            if ((uint)Type > 3)
+            {
+                throw new ArgumentException($"Invalid thread type \"{Type}\".");
+            }
 
-            System.CriticalSectionLock.Lock();
+            PreferredCore = DefaultCpuCore;
+
+            AffinityMask |= 1L << DefaultCpuCore;
+
+            SchedFlags = Type == ThreadType.Dummy
+                ? ThreadSchedState.Running
+                : ThreadSchedState.None;
+
+            CurrentCore = PreferredCore;
+
+            DynamicPriority = Priority;
+            BasePriority    = Priority;
+
+            ObjSyncResult = 0x7201;
+
+            this.Entrypoint = Entrypoint;
+
+            if (Type == ThreadType.User)
+            {
+                if (Owner.AllocateThreadLocalStorage(out TlsAddress) != KernelResult.Success)
+                {
+                    return KernelResult.OutOfMemory;
+                }
+
+                MemoryHelper.FillWithZeros(Owner.CpuMemory, (long)TlsAddress, KTlsPageInfo.TlsEntrySize);
+            }
+
+            bool Is64Bits;
+
+            if (Owner != null)
+            {
+                this.Owner = Owner;
+
+                Owner.IncrementThreadCount();
+
+                Is64Bits = (Owner.MmuFlags & 1) != 0;
+            }
+            else
+            {
+                Is64Bits = true;
+            }
+
+            Context = new CpuThread(Owner.Translator, Owner.CpuMemory, (long)Entrypoint);
+
+            Context.ThreadState.X0  = ArgsPtr;
+            Context.ThreadState.X31 = StackTop;
+
+            Context.ThreadState.CntfrqEl0 = 19200000;
+            Context.ThreadState.Tpidr     = (long)TlsAddress;
+
+            Owner.SubscribeThreadEventHandlers(Context);
+
+            Context.WorkFinished += ThreadFinishedHandler;
+
+            ThreadUid = System.GetThreadUid();
+
+            if (Owner != null)
+            {
+                Owner.AddThread(this);
+
+                if (Owner.IsPaused)
+                {
+                    System.CriticalSection.Enter();
+
+                    if (ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending)
+                    {
+                        System.CriticalSection.Leave();
+
+                        return KernelResult.Success;
+                    }
+
+                    ForcePauseFlags |= ThreadSchedState.ProcessPauseFlag;
+
+                    CombineForcePauseFlags();
+
+                    System.CriticalSection.Leave();
+                }
+            }
+
+            return KernelResult.Success;
+        }
+
+        public KernelResult Start()
+        {
+            if (!System.KernelInitialized)
+            {
+                System.CriticalSection.Enter();
+
+                if (!ShallBeTerminated && SchedFlags != ThreadSchedState.TerminationPending)
+                {
+                    ForcePauseFlags |= ThreadSchedState.KernelInitPauseFlag;
+
+                    CombineForcePauseFlags();
+                }
+
+                System.CriticalSection.Leave();
+            }
+
+            KernelResult Result = KernelResult.ThreadTerminating;
+
+            System.CriticalSection.Enter();
 
             if (!ShallBeTerminated)
             {
@@ -106,9 +210,9 @@ namespace Ryujinx.HLE.HOS.Kernel
                        CurrentThread.SchedFlags != ThreadSchedState.TerminationPending &&
                        !CurrentThread.ShallBeTerminated)
                 {
-                    if ((SchedFlags & ThreadSchedState.LowNibbleMask) != ThreadSchedState.None)
+                    if ((SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.None)
                     {
-                        Result = MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
+                        Result = KernelResult.InvalidState;
 
                         break;
                     }
@@ -122,7 +226,7 @@ namespace Ryujinx.HLE.HOS.Kernel
 
                         SetNewSchedFlags(ThreadSchedState.Running);
 
-                        Result = 0;
+                        Result = KernelResult.Success;
 
                         break;
                     }
@@ -130,8 +234,8 @@ namespace Ryujinx.HLE.HOS.Kernel
                     {
                         CurrentThread.CombineForcePauseFlags();
 
-                        System.CriticalSectionLock.Unlock();
-                        System.CriticalSectionLock.Lock();
+                        System.CriticalSection.Leave();
+                        System.CriticalSection.Enter();
 
                         if (CurrentThread.ShallBeTerminated)
                         {
@@ -141,25 +245,25 @@ namespace Ryujinx.HLE.HOS.Kernel
                 }
             }
 
-            System.CriticalSectionLock.Unlock();
+            System.CriticalSection.Leave();
 
             return Result;
         }
 
         public void Exit()
         {
-            System.CriticalSectionLock.Lock();
+            System.CriticalSection.Enter();
 
-            ForcePauseFlags &= ~ThreadSchedState.ExceptionalMask;
+            ForcePauseFlags &= ~ThreadSchedState.ForcePauseMask;
 
             ExitImpl();
 
-            System.CriticalSectionLock.Unlock();
+            System.CriticalSection.Leave();
         }
 
         private void ExitImpl()
         {
-            System.CriticalSectionLock.Lock();
+            System.CriticalSection.Enter();
 
             SetNewSchedFlags(ThreadSchedState.TerminationPending);
 
@@ -167,16 +271,16 @@ namespace Ryujinx.HLE.HOS.Kernel
 
             Signal();
 
-            System.CriticalSectionLock.Unlock();
+            System.CriticalSection.Leave();
         }
 
         public long Sleep(long Timeout)
         {
-            System.CriticalSectionLock.Lock();
+            System.CriticalSection.Enter();
 
             if (ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending)
             {
-                System.CriticalSectionLock.Unlock();
+                System.CriticalSection.Leave();
 
                 return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
             }
@@ -188,7 +292,7 @@ namespace Ryujinx.HLE.HOS.Kernel
                 System.TimeManager.ScheduleFutureInvocation(this, Timeout);
             }
 
-            System.CriticalSectionLock.Unlock();
+            System.CriticalSection.Leave();
 
             if (Timeout > 0)
             {
@@ -200,11 +304,11 @@ namespace Ryujinx.HLE.HOS.Kernel
 
         public void Yield()
         {
-            System.CriticalSectionLock.Lock();
+            System.CriticalSection.Enter();
 
             if (SchedFlags != ThreadSchedState.Running)
             {
-                System.CriticalSectionLock.Unlock();
+                System.CriticalSection.Leave();
 
                 System.Scheduler.ContextSwitch();
 
@@ -219,27 +323,27 @@ namespace Ryujinx.HLE.HOS.Kernel
 
             Scheduler.ThreadReselectionRequested = true;
 
-            System.CriticalSectionLock.Unlock();
+            System.CriticalSection.Leave();
 
             System.Scheduler.ContextSwitch();
         }
 
         public void YieldWithLoadBalancing()
         {
-            System.CriticalSectionLock.Lock();
-
-            int Prio = DynamicPriority;
-            int Core = CurrentCore;
+            System.CriticalSection.Enter();
 
             if (SchedFlags != ThreadSchedState.Running)
             {
-                System.CriticalSectionLock.Unlock();
+                System.CriticalSection.Leave();
 
                 System.Scheduler.ContextSwitch();
 
                 return;
             }
 
+            int Prio = DynamicPriority;
+            int Core = CurrentCore;
+
             KThread NextThreadOnCurrentQueue = null;
 
             if (DynamicPriority < KScheduler.PrioritiesCount)
@@ -270,7 +374,7 @@ namespace Ryujinx.HLE.HOS.Kernel
 
                     //If the candidate was scheduled after the current thread, then it's not worth it,
                     //unless the priority is higher than the current one.
-                    if (NextThreadOnCurrentQueue.LastScheduledTicks >= Thread.LastScheduledTicks ||
+                    if (NextThreadOnCurrentQueue.LastScheduledTime >= Thread.LastScheduledTime ||
                         NextThreadOnCurrentQueue.DynamicPriority    <  Thread.DynamicPriority)
                     {
                         yield return Thread;
@@ -292,18 +396,18 @@ namespace Ryujinx.HLE.HOS.Kernel
                 Scheduler.ThreadReselectionRequested = true;
             }
 
-            System.CriticalSectionLock.Unlock();
+            System.CriticalSection.Leave();
 
             System.Scheduler.ContextSwitch();
         }
 
         public void YieldAndWaitForLoadBalancing()
         {
-            System.CriticalSectionLock.Lock();
+            System.CriticalSection.Enter();
 
             if (SchedFlags != ThreadSchedState.Running)
             {
-                System.CriticalSectionLock.Unlock();
+                System.CriticalSection.Leave();
 
                 System.Scheduler.ContextSwitch();
 
@@ -348,47 +452,47 @@ namespace Ryujinx.HLE.HOS.Kernel
                 Scheduler.ThreadReselectionRequested = true;
             }
 
-            System.CriticalSectionLock.Unlock();
+            System.CriticalSection.Leave();
 
             System.Scheduler.ContextSwitch();
         }
 
         public void SetPriority(int Priority)
         {
-            System.CriticalSectionLock.Lock();
+            System.CriticalSection.Enter();
 
             BasePriority = Priority;
 
             UpdatePriorityInheritance();
 
-            System.CriticalSectionLock.Unlock();
+            System.CriticalSection.Leave();
         }
 
         public long SetActivity(bool Pause)
         {
             long Result = 0;
 
-            System.CriticalSectionLock.Lock();
+            System.CriticalSection.Enter();
 
-            ThreadSchedState LowNibble = SchedFlags & ThreadSchedState.LowNibbleMask;
+            ThreadSchedState LowNibble = SchedFlags & ThreadSchedState.LowMask;
 
             if (LowNibble != ThreadSchedState.Paused && LowNibble != ThreadSchedState.Running)
             {
-                System.CriticalSectionLock.Unlock();
+                System.CriticalSection.Leave();
 
                 return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
             }
 
-            System.CriticalSectionLock.Lock();
+            System.CriticalSection.Enter();
 
             if (!ShallBeTerminated && SchedFlags != ThreadSchedState.TerminationPending)
             {
                 if (Pause)
                 {
                     //Pause, the force pause flag should be clear (thread is NOT paused).
-                    if ((ForcePauseFlags & ThreadSchedState.ForcePauseFlag) == 0)
+                    if ((ForcePauseFlags & ThreadSchedState.ThreadPauseFlag) == 0)
                     {
-                        ForcePauseFlags |= ThreadSchedState.ForcePauseFlag;
+                        ForcePauseFlags |= ThreadSchedState.ThreadPauseFlag;
 
                         CombineForcePauseFlags();
                     }
@@ -400,17 +504,17 @@ namespace Ryujinx.HLE.HOS.Kernel
                 else
                 {
                     //Unpause, the force pause flag should be set (thread is paused).
-                    if ((ForcePauseFlags & ThreadSchedState.ForcePauseFlag) != 0)
+                    if ((ForcePauseFlags & ThreadSchedState.ThreadPauseFlag) != 0)
                     {
                         ThreadSchedState OldForcePauseFlags = ForcePauseFlags;
 
-                        ForcePauseFlags &= ~ThreadSchedState.ForcePauseFlag;
+                        ForcePauseFlags &= ~ThreadSchedState.ThreadPauseFlag;
 
-                        if ((OldForcePauseFlags & ~ThreadSchedState.ForcePauseFlag) == ThreadSchedState.None)
+                        if ((OldForcePauseFlags & ~ThreadSchedState.ThreadPauseFlag) == ThreadSchedState.None)
                         {
                             ThreadSchedState OldSchedFlags = SchedFlags;
 
-                            SchedFlags &= ThreadSchedState.LowNibbleMask;
+                            SchedFlags &= ThreadSchedState.LowMask;
 
                             AdjustScheduling(OldSchedFlags);
                         }
@@ -422,27 +526,27 @@ namespace Ryujinx.HLE.HOS.Kernel
                 }
             }
 
-            System.CriticalSectionLock.Unlock();
-            System.CriticalSectionLock.Unlock();
+            System.CriticalSection.Leave();
+            System.CriticalSection.Leave();
 
             return Result;
         }
 
         public void CancelSynchronization()
         {
-            System.CriticalSectionLock.Lock();
+            System.CriticalSection.Enter();
 
-            if ((SchedFlags & ThreadSchedState.LowNibbleMask) != ThreadSchedState.Paused || !WaitingSync)
+            if ((SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.Paused || !WaitingSync)
             {
                 SyncCancelled = true;
             }
-            else if (WithholderNode != null)
+            else if (Withholder != null)
             {
-                System.Withholders.Remove(WithholderNode);
+                Withholder.Remove(WithholderNode);
 
                 SetNewSchedFlags(ThreadSchedState.Running);
 
-                WithholderNode = null;
+                Withholder = null;
 
                 SyncCancelled = true;
             }
@@ -456,12 +560,12 @@ namespace Ryujinx.HLE.HOS.Kernel
                 SyncCancelled = false;
             }
 
-            System.CriticalSectionLock.Unlock();
+            System.CriticalSection.Leave();
         }
 
-        public long SetCoreAndAffinityMask(int NewCore, long NewAffinityMask)
+        public KernelResult SetCoreAndAffinityMask(int NewCore, long NewAffinityMask)
         {
-            System.CriticalSectionLock.Lock();
+            System.CriticalSection.Enter();
 
             bool UseOverride = AffinityOverrideCount != 0;
 
@@ -472,9 +576,9 @@ namespace Ryujinx.HLE.HOS.Kernel
 
                 if ((NewAffinityMask & (1 << NewCore)) == 0)
                 {
-                    System.CriticalSectionLock.Unlock();
+                    System.CriticalSection.Leave();
 
-                    return MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue);
+                    return KernelResult.InvalidCombination;
                 }
             }
 
@@ -510,9 +614,9 @@ namespace Ryujinx.HLE.HOS.Kernel
                 }
             }
 
-            System.CriticalSectionLock.Unlock();
+            System.CriticalSection.Leave();
 
-            return 0;
+            return KernelResult.Success;
         }
 
         private static int HighestSetCore(long Mask)
@@ -531,7 +635,7 @@ namespace Ryujinx.HLE.HOS.Kernel
         private void CombineForcePauseFlags()
         {
             ThreadSchedState OldFlags  = SchedFlags;
-            ThreadSchedState LowNibble = SchedFlags & ThreadSchedState.LowNibbleMask;
+            ThreadSchedState LowNibble = SchedFlags & ThreadSchedState.LowMask;
 
             SchedFlags = LowNibble | ForcePauseFlags;
 
@@ -540,33 +644,33 @@ namespace Ryujinx.HLE.HOS.Kernel
 
         private void SetNewSchedFlags(ThreadSchedState NewFlags)
         {
-            System.CriticalSectionLock.Lock();
+            System.CriticalSection.Enter();
 
             ThreadSchedState OldFlags = SchedFlags;
 
-            SchedFlags = (OldFlags & ThreadSchedState.HighNibbleMask) | NewFlags;
+            SchedFlags = (OldFlags & ThreadSchedState.HighMask) | NewFlags;
 
-            if ((OldFlags & ThreadSchedState.LowNibbleMask) != NewFlags)
+            if ((OldFlags & ThreadSchedState.LowMask) != NewFlags)
             {
                 AdjustScheduling(OldFlags);
             }
 
-            System.CriticalSectionLock.Unlock();
+            System.CriticalSection.Leave();
         }
 
         public void ReleaseAndResume()
         {
-            System.CriticalSectionLock.Lock();
+            System.CriticalSection.Enter();
 
-            if ((SchedFlags & ThreadSchedState.LowNibbleMask) == ThreadSchedState.Paused)
+            if ((SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused)
             {
-                if (WithholderNode != null)
+                if (Withholder != null)
                 {
-                    System.Withholders.Remove(WithholderNode);
+                    Withholder.Remove(WithholderNode);
 
                     SetNewSchedFlags(ThreadSchedState.Running);
 
-                    WithholderNode = null;
+                    Withholder = null;
                 }
                 else
                 {
@@ -574,21 +678,21 @@ namespace Ryujinx.HLE.HOS.Kernel
                 }
             }
 
-            System.CriticalSectionLock.Unlock();
+            System.CriticalSection.Leave();
         }
 
         public void Reschedule(ThreadSchedState NewFlags)
         {
-            System.CriticalSectionLock.Lock();
+            System.CriticalSection.Enter();
 
             ThreadSchedState OldFlags = SchedFlags;
 
-            SchedFlags = (OldFlags & ThreadSchedState.HighNibbleMask) |
-                         (NewFlags & ThreadSchedState.LowNibbleMask);
+            SchedFlags = (OldFlags & ThreadSchedState.HighMask) |
+                         (NewFlags & ThreadSchedState.LowMask);
 
             AdjustScheduling(OldFlags);
 
-            System.CriticalSectionLock.Unlock();
+            System.CriticalSection.Leave();
         }
 
         public void AddMutexWaiter(KThread Requester)
@@ -866,18 +970,61 @@ namespace Ryujinx.HLE.HOS.Kernel
             return HasExited;
         }
 
+        public void SetEntryArguments(long ArgsPtr, int ThreadHandle)
+        {
+            Context.ThreadState.X0 = (ulong)ArgsPtr;
+            Context.ThreadState.X1 = (ulong)ThreadHandle;
+        }
+
         public void ClearExclusive()
         {
-            Owner.Memory.ClearExclusive(CurrentCore);
+            Owner.CpuMemory.ClearExclusive(CurrentCore);
         }
 
         public void TimeUp()
         {
-            System.CriticalSectionLock.Lock();
+            ReleaseAndResume();
+        }
 
-            SetNewSchedFlags(ThreadSchedState.Running);
+        public void PrintGuestStackTrace()
+        {
+            Owner.Debugger.PrintGuestStackTrace(Context.ThreadState);
+        }
 
-            System.CriticalSectionLock.Unlock();
+        private void ThreadFinishedHandler(object sender, EventArgs e)
+        {
+            System.Scheduler.ExitThread(this);
+
+            Terminate();
+
+            System.Scheduler.RemoveThread(this);
+        }
+
+        public void Terminate()
+        {
+            Owner?.RemoveThread(this);
+
+            if (TlsAddress != 0 && Owner.FreeThreadLocalStorage(TlsAddress) != KernelResult.Success)
+            {
+                throw new InvalidOperationException("Unexpected failure freeing thread local storage.");
+            }
+
+            System.CriticalSection.Enter();
+
+            //Wake up all threads that may be waiting for a mutex being held
+            //by this thread.
+            foreach (KThread Thread in MutexWaiters)
+            {
+                Thread.MutexOwner            = null;
+                Thread.PreferredCoreOverride = 0;
+                Thread.ObjSyncResult         = 0xfa01;
+
+                Thread.ReleaseAndResume();
+            }
+
+            System.CriticalSection.Leave();
+
+            Owner?.DecrementThreadCountAndTerminateIfZero();
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KTimeManager.cs b/Ryujinx.HLE/HOS/Kernel/KTimeManager.cs
index 47a3c86c..375789f0 100644
--- a/Ryujinx.HLE/HOS/Kernel/KTimeManager.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KTimeManager.cs
@@ -1,6 +1,6 @@
+using Ryujinx.Common;
 using System;
 using System.Collections.Generic;
-using System.Diagnostics;
 using System.Linq;
 using System.Threading;
 
@@ -25,18 +25,12 @@ namespace Ryujinx.HLE.HOS.Kernel
 
         private AutoResetEvent WaitEvent;
 
-        private Stopwatch Counter;
-
         private bool KeepRunning;
 
         public KTimeManager()
         {
             WaitingObjects = new List<WaitingObject>();
 
-            Counter = new Stopwatch();
-
-            Counter.Start();
-
             KeepRunning = true;
 
             Thread Work = new Thread(WaitAndCheckScheduledObjects);
@@ -46,26 +40,36 @@ namespace Ryujinx.HLE.HOS.Kernel
 
         public void ScheduleFutureInvocation(IKFutureSchedulerObject Object, long Timeout)
         {
+            long TimePoint = PerformanceCounter.ElapsedMilliseconds + ConvertNanosecondsToMilliseconds(Timeout);
+
             lock (WaitingObjects)
             {
-                long TimePoint = Counter.ElapsedMilliseconds + ConvertNanosecondsToMilliseconds(Timeout);
-
                 WaitingObjects.Add(new WaitingObject(Object, TimePoint));
             }
 
             WaitEvent.Set();
         }
 
-        private long ConvertNanosecondsToMilliseconds(long Timeout)
+        public static long ConvertNanosecondsToMilliseconds(long Time)
         {
-            Timeout /= 1000000;
+            Time /= 1000000;
 
-            if ((ulong)Timeout > int.MaxValue)
+            if ((ulong)Time > int.MaxValue)
             {
                 return int.MaxValue;
             }
 
-            return Timeout;
+            return Time;
+        }
+
+        public static long ConvertMillisecondsToNanoseconds(long Time)
+        {
+            return Time * 1000000;
+        }
+
+        public static long ConvertMillisecondsToTicks(long Time)
+        {
+            return Time * 19200;
         }
 
         public void UnscheduleFutureInvocation(IKFutureSchedulerObject Object)
@@ -82,26 +86,31 @@ namespace Ryujinx.HLE.HOS.Kernel
             {
                 while (KeepRunning)
                 {
-                    Monitor.Enter(WaitingObjects);
+                    WaitingObject Next;
 
-                    WaitingObject Next = WaitingObjects.OrderBy(x => x.TimePoint).FirstOrDefault();
-
-                    Monitor.Exit(WaitingObjects);
+                    lock (WaitingObjects)
+                    {
+                        Next = WaitingObjects.OrderBy(x => x.TimePoint).FirstOrDefault();
+                    }
 
                     if (Next != null)
                     {
-                        long TimePoint = Counter.ElapsedMilliseconds;
+                        long TimePoint = PerformanceCounter.ElapsedMilliseconds;
 
                         if (Next.TimePoint > TimePoint)
                         {
                             WaitEvent.WaitOne((int)(Next.TimePoint - TimePoint));
                         }
 
-                        Monitor.Enter(WaitingObjects);
+                        bool TimeUp = PerformanceCounter.ElapsedMilliseconds >= Next.TimePoint;
 
-                        bool TimeUp = Counter.ElapsedMilliseconds >= Next.TimePoint && WaitingObjects.Remove(Next);
-
-                        Monitor.Exit(WaitingObjects);
+                        if (TimeUp)
+                        {
+                            lock (WaitingObjects)
+                            {
+                                TimeUp = WaitingObjects.Remove(Next);
+                            }
+                        }
 
                         if (TimeUp)
                         {
diff --git a/Ryujinx.HLE/HOS/Kernel/KTlsPageInfo.cs b/Ryujinx.HLE/HOS/Kernel/KTlsPageInfo.cs
new file mode 100644
index 00000000..18dc2dec
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KTlsPageInfo.cs
@@ -0,0 +1,73 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+    class KTlsPageInfo
+    {
+        public const int TlsEntrySize = 0x200;
+
+        public ulong PageAddr { get; private set; }
+
+        private bool[] IsSlotFree;
+
+        public KTlsPageInfo(ulong PageAddress)
+        {
+            this.PageAddr = PageAddress;
+
+            IsSlotFree = new bool[KMemoryManager.PageSize / TlsEntrySize];
+
+            for (int Index = 0; Index < IsSlotFree.Length; Index++)
+            {
+                IsSlotFree[Index] = true;
+            }
+        }
+
+        public bool TryGetFreePage(out ulong Address)
+        {
+            Address = PageAddr;
+
+            for (int Index = 0; Index < IsSlotFree.Length; Index++)
+            {
+                if (IsSlotFree[Index])
+                {
+                    IsSlotFree[Index] = false;
+
+                    return true;
+                }
+
+                Address += TlsEntrySize;
+            }
+
+            Address = 0;
+
+            return false;
+        }
+
+        public bool IsFull()
+        {
+            bool HasFree = false;
+
+            for (int Index = 0; Index < IsSlotFree.Length; Index++)
+            {
+                HasFree |= IsSlotFree[Index];
+            }
+
+            return !HasFree;
+        }
+
+        public bool IsEmpty()
+        {
+            bool AllFree = true;
+
+            for (int Index = 0; Index < IsSlotFree.Length; Index++)
+            {
+                AllFree &= IsSlotFree[Index];
+            }
+
+            return AllFree;
+        }
+
+        public void FreeTlsSlot(ulong Address)
+        {
+            IsSlotFree[(Address - PageAddr) / TlsEntrySize] = true;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KTransferMemory.cs b/Ryujinx.HLE/HOS/Kernel/KTransferMemory.cs
index 6ebffa7e..5598f78d 100644
--- a/Ryujinx.HLE/HOS/Kernel/KTransferMemory.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KTransferMemory.cs
@@ -2,13 +2,13 @@ namespace Ryujinx.HLE.HOS.Kernel
 {
     class KTransferMemory
     {
-        public long Position { get; private set; }
-        public long Size     { get; private set; }
+        public ulong Address { get; private set; }
+        public ulong Size     { get; private set; }
 
-        public KTransferMemory(long Position, long Size)
+        public KTransferMemory(ulong Address, ulong Size)
         {
-            this.Position = Position;
-            this.Size     = Size;
+            this.Address = Address;
+            this.Size    = Size;
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KernelInit.cs b/Ryujinx.HLE/HOS/Kernel/KernelInit.cs
new file mode 100644
index 00000000..efb514c1
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KernelInit.cs
@@ -0,0 +1,136 @@
+using System;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+    static class KernelInit
+    {
+        public static void InitializeResourceLimit(KResourceLimit ResourceLimit)
+        {
+            void EnsureSuccess(KernelResult Result)
+            {
+                if (Result != KernelResult.Success)
+                {
+                    throw new InvalidOperationException($"Unexpected result \"{Result}\".");
+                }
+            }
+
+            int KernelMemoryCfg = 0;
+
+            long RamSize = GetRamSize(KernelMemoryCfg);
+
+            EnsureSuccess(ResourceLimit.SetLimitValue(LimitableResource.Memory,         RamSize));
+            EnsureSuccess(ResourceLimit.SetLimitValue(LimitableResource.Thread,         800));
+            EnsureSuccess(ResourceLimit.SetLimitValue(LimitableResource.Event,          700));
+            EnsureSuccess(ResourceLimit.SetLimitValue(LimitableResource.TransferMemory, 200));
+            EnsureSuccess(ResourceLimit.SetLimitValue(LimitableResource.Session,        900));
+
+            if (!ResourceLimit.Reserve(LimitableResource.Memory, 0) ||
+                !ResourceLimit.Reserve(LimitableResource.Memory, 0x60000))
+            {
+                throw new InvalidOperationException("Unexpected failure reserving memory on resource limit.");
+            }
+        }
+
+        public static KMemoryRegionManager[] GetMemoryRegions()
+        {
+            KMemoryArrange Arrange = GetMemoryArrange();
+
+            return new KMemoryRegionManager[]
+            {
+                GetMemoryRegion(Arrange.Application),
+                GetMemoryRegion(Arrange.Applet),
+                GetMemoryRegion(Arrange.Service),
+                GetMemoryRegion(Arrange.NvServices)
+            };
+        }
+
+        private static KMemoryRegionManager GetMemoryRegion(KMemoryArrangeRegion Region)
+        {
+            return new KMemoryRegionManager(Region.Address, Region.Size, Region.EndAddr);
+        }
+
+        private static KMemoryArrange GetMemoryArrange()
+        {
+            int McEmemCfg = 0x1000;
+
+            ulong EmemApertureSize = (ulong)(McEmemCfg & 0x3fff) << 20;
+
+            int KernelMemoryCfg = 0;
+
+            ulong RamSize = (ulong)GetRamSize(KernelMemoryCfg);
+
+            ulong RamPart0;
+            ulong RamPart1;
+
+            if (RamSize * 2 > EmemApertureSize)
+            {
+                RamPart0 = EmemApertureSize / 2;
+                RamPart1 = EmemApertureSize / 2;
+            }
+            else
+            {
+                RamPart0 = EmemApertureSize;
+                RamPart1 = 0;
+            }
+
+            int MemoryArrange = 1;
+
+            ulong ApplicationRgSize;
+
+            switch (MemoryArrange)
+            {
+                case 2:    ApplicationRgSize = 0x80000000;  break;
+                case 0x11:
+                case 0x21: ApplicationRgSize = 0x133400000; break;
+                default:   ApplicationRgSize = 0xcd500000;  break;
+            }
+
+            ulong AppletRgSize;
+
+            switch (MemoryArrange)
+            {
+                case 2:    AppletRgSize = 0x61200000; break;
+                case 3:    AppletRgSize = 0x1c000000; break;
+                case 0x11: AppletRgSize = 0x23200000; break;
+                case 0x12:
+                case 0x21: AppletRgSize = 0x89100000; break;
+                default:   AppletRgSize = 0x1fb00000; break;
+            }
+
+            KMemoryArrangeRegion ServiceRg;
+            KMemoryArrangeRegion NvServicesRg;
+            KMemoryArrangeRegion AppletRg;
+            KMemoryArrangeRegion ApplicationRg;
+
+            const ulong NvServicesRgSize = 0x29ba000;
+
+            ulong ApplicationRgEnd = DramMemoryMap.DramEnd; //- RamPart0;
+
+            ApplicationRg = new KMemoryArrangeRegion(ApplicationRgEnd - ApplicationRgSize, ApplicationRgSize);
+
+            ulong NvServicesRgEnd = ApplicationRg.Address - AppletRgSize;
+
+            NvServicesRg = new KMemoryArrangeRegion(NvServicesRgEnd - NvServicesRgSize, NvServicesRgSize);
+            AppletRg     = new KMemoryArrangeRegion(NvServicesRgEnd, AppletRgSize);
+
+            //Note: There is an extra region used by the kernel, however
+            //since we are doing HLE we are not going to use that memory, so give all
+            //the remaining memory space to services.
+            ulong ServiceRgSize = NvServicesRg.Address - DramMemoryMap.SlabHeapEnd;
+
+            ServiceRg = new KMemoryArrangeRegion(DramMemoryMap.SlabHeapEnd, ServiceRgSize);
+
+            return new KMemoryArrange(ServiceRg, NvServicesRg, AppletRg, ApplicationRg);
+        }
+
+        private static long GetRamSize(int KernelMemoryCfg)
+        {
+            switch ((KernelMemoryCfg >> 16) & 3)
+            {
+                case 1:  return 0x180000000;
+                case 2:  return 0x200000000;
+                default: return 0x100000000;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KernelResult.cs b/Ryujinx.HLE/HOS/Kernel/KernelResult.cs
index d9cbfc67..9870d175 100644
--- a/Ryujinx.HLE/HOS/Kernel/KernelResult.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KernelResult.cs
@@ -2,9 +2,30 @@ namespace Ryujinx.HLE.HOS.Kernel
 {
     enum KernelResult
     {
-        Success         = 0,
-        HandleTableFull = 0xd201,
-        InvalidHandle   = 0xe401,
-        InvalidState    = 0xfa01
+        Success            = 0,
+        InvalidCapability  = 0x1c01,
+        ThreadTerminating  = 0x7601,
+        InvalidSize        = 0xca01,
+        InvalidAddress     = 0xcc01,
+        OutOfResource      = 0xce01,
+        OutOfMemory        = 0xd001,
+        HandleTableFull    = 0xd201,
+        InvalidMemState    = 0xd401,
+        InvalidPermission  = 0xd801,
+        InvalidMemRange    = 0xdc01,
+        InvalidPriority    = 0xe001,
+        InvalidCpuCore     = 0xe201,
+        InvalidHandle      = 0xe401,
+        UserCopyFailed     = 0xe601,
+        InvalidCombination = 0xe801,
+        TimedOut           = 0xea01,
+        Cancelled          = 0xec01,
+        MaximumExceeded    = 0xee01,
+        InvalidEnumValue   = 0xf001,
+        NotFound           = 0xf201,
+        InvalidThread      = 0xf401,
+        InvalidState       = 0xfa01,
+        ReservedValue      = 0xfc01,
+        ResLimitExceeded   = 0x10801
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KernelTransfer.cs b/Ryujinx.HLE/HOS/Kernel/KernelTransfer.cs
new file mode 100644
index 00000000..a3fabeae
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KernelTransfer.cs
@@ -0,0 +1,71 @@
+using ChocolArm64.Memory;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+    static class KernelTransfer
+    {
+        public static bool UserToKernelInt32(Horizon System, long Address, out int Value)
+        {
+            KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+            if (CurrentProcess.CpuMemory.IsMapped(Address) &&
+                CurrentProcess.CpuMemory.IsMapped(Address + 3))
+            {
+                Value = CurrentProcess.CpuMemory.ReadInt32(Address);
+
+                return true;
+            }
+
+            Value = 0;
+
+            return false;
+        }
+
+        public static bool UserToKernelString(Horizon System, long Address, int Size, out string Value)
+        {
+            KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+            if (CurrentProcess.CpuMemory.IsMapped(Address) &&
+                CurrentProcess.CpuMemory.IsMapped(Address + Size - 1))
+            {
+                Value = MemoryHelper.ReadAsciiString(CurrentProcess.CpuMemory, Address, Size);
+
+                return true;
+            }
+
+            Value = null;
+
+            return false;
+        }
+
+        public static bool KernelToUserInt32(Horizon System, long Address, int Value)
+        {
+            KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+            if (CurrentProcess.CpuMemory.IsMapped(Address) &&
+                CurrentProcess.CpuMemory.IsMapped(Address + 3))
+            {
+                CurrentProcess.CpuMemory.WriteInt32ToSharedAddr(Address, Value);
+
+                return true;
+            }
+
+            return false;
+        }
+
+        public static bool KernelToUserInt64(Horizon System, long Address, long Value)
+        {
+            KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+            if (CurrentProcess.CpuMemory.IsMapped(Address) &&
+                CurrentProcess.CpuMemory.IsMapped(Address + 7))
+            {
+                CurrentProcess.CpuMemory.WriteInt64(Address, Value);
+
+                return true;
+            }
+
+            return false;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/LimitableResource.cs b/Ryujinx.HLE/HOS/Kernel/LimitableResource.cs
new file mode 100644
index 00000000..baab4222
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/LimitableResource.cs
@@ -0,0 +1,13 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+    enum LimitableResource : byte
+    {
+        Memory         = 0,
+        Thread         = 1,
+        Event          = 2,
+        TransferMemory = 3,
+        Session        = 4,
+
+        Count = 5
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/MemoryOperation.cs b/Ryujinx.HLE/HOS/Kernel/MemoryOperation.cs
new file mode 100644
index 00000000..b9350121
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/MemoryOperation.cs
@@ -0,0 +1,12 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+    enum MemoryOperation
+    {
+        MapPa,
+        MapVa,
+        Allocate,
+        Unmap,
+        ChangePermRw,
+        ChangePermsAndAttributes
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/MemoryRegion.cs b/Ryujinx.HLE/HOS/Kernel/MemoryRegion.cs
new file mode 100644
index 00000000..ea4f33c9
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/MemoryRegion.cs
@@ -0,0 +1,10 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+    enum MemoryRegion
+    {
+        Application = 0,
+        Applet      = 1,
+        Service     = 2,
+        NvServices  = 3
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/MemoryState.cs b/Ryujinx.HLE/HOS/Kernel/MemoryState.cs
index 2c37723c..e2ce27ef 100644
--- a/Ryujinx.HLE/HOS/Kernel/MemoryState.cs
+++ b/Ryujinx.HLE/HOS/Kernel/MemoryState.cs
@@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Kernel
         ModCodeStatic          = 0x00DD7E08,
         ModCodeMutable         = 0x03FFBD09,
         IpcBuffer0             = 0x005C3C0A,
-        MappedMemory           = 0x005C3C0B,
+        Stack                  = 0x005C3C0B,
         ThreadLocal            = 0x0040200C,
         TransferMemoryIsolated = 0x015C3C0D,
         TransferMemory         = 0x005C380E,
diff --git a/Ryujinx.HLE/HOS/Kernel/MersenneTwister.cs b/Ryujinx.HLE/HOS/Kernel/MersenneTwister.cs
new file mode 100644
index 00000000..b90d54d2
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/MersenneTwister.cs
@@ -0,0 +1,128 @@
+using Ryujinx.Common;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+    class MersenneTwister
+    {
+        private int Index;
+        private uint[] Mt;
+
+        public MersenneTwister(uint Seed)
+        {
+            Mt = new uint[624];
+
+            Mt[0] = Seed;
+
+            for (int MtIdx = 1; MtIdx < Mt.Length; MtIdx++)
+            {
+                uint Prev = Mt[MtIdx - 1];
+
+                Mt[MtIdx] = (uint)(0x6c078965 * (Prev ^ (Prev >> 30)) + MtIdx);
+            }
+
+            Index = Mt.Length;
+        }
+
+        public long GenRandomNumber(long Min, long Max)
+        {
+            long Range = Max - Min;
+
+            if (Min == Max)
+            {
+                return Min;
+            }
+
+            if (Range == -1)
+            {
+                //Increment would cause a overflow, special case.
+                return GenRandomNumber(2, 2, 32, 0xffffffffu, 0xffffffffu);
+            }
+
+            Range++;
+
+            //This is log2(Range) plus one.
+            int NextRangeLog2 = 64 - BitUtils.CountLeadingZeros64(Range);
+
+            //If Range is already power of 2, subtract one to use log2(Range) directly.
+            int RangeLog2 = NextRangeLog2 - (BitUtils.IsPowerOfTwo64(Range) ? 1 : 0);
+
+            int Parts       = RangeLog2 > 32 ? 2 : 1;
+            int BitsPerPart = RangeLog2 / Parts;
+
+            int FullParts = Parts - (RangeLog2 - Parts * BitsPerPart);
+
+            uint Mask      = 0xffffffffu >> (32 - BitsPerPart);
+            uint MaskPlus1 = 0xffffffffu >> (31 - BitsPerPart);
+
+            long RandomNumber;
+
+            do
+            {
+                RandomNumber = GenRandomNumber(Parts, FullParts, BitsPerPart, Mask, MaskPlus1);
+            }
+            while ((ulong)RandomNumber >= (ulong)Range);
+
+            return Min + RandomNumber;
+        }
+
+        private long GenRandomNumber(
+            int  Parts,
+            int  FullParts,
+            int  BitsPerPart,
+            uint Mask,
+            uint MaskPlus1)
+        {
+            long RandomNumber = 0;
+
+            int Part = 0;
+
+            for (; Part < FullParts; Part++)
+            {
+                RandomNumber <<= BitsPerPart;
+                RandomNumber  |= GenRandomNumber() & Mask;
+            }
+
+            for (; Part < Parts; Part++)
+            {
+                RandomNumber <<= BitsPerPart + 1;
+                RandomNumber  |= GenRandomNumber() & MaskPlus1;
+            }
+
+            return RandomNumber;
+        }
+
+        private uint GenRandomNumber()
+        {
+            if (Index >= Mt.Length)
+            {
+                Twist();
+            }
+
+            uint Value = Mt[Index++];
+
+            Value ^= Value >> 11;
+            Value ^= (Value << 7) & 0x9d2c5680;
+            Value ^= (Value << 15) & 0xefc60000;
+            Value ^= Value >> 18;
+
+            return Value;
+        }
+
+        private void Twist()
+        {
+            for (int MtIdx = 0; MtIdx < Mt.Length; MtIdx++)
+            {
+                uint Value = (Mt[MtIdx] & 0x80000000) + (Mt[(MtIdx + 1) % Mt.Length] & 0x7fffffff);
+
+                Mt[MtIdx] = Mt[(MtIdx + 397) % Mt.Length] ^ (Value >> 1);
+
+                if ((Value & 1) != 0)
+                {
+                    Mt[MtIdx] ^= 0x9908b0df;
+                }
+            }
+
+            Index = 0;
+        }
+    }
+}
diff --git a/Ryujinx.HLE/HOS/Kernel/ProcessCreationInfo.cs b/Ryujinx.HLE/HOS/Kernel/ProcessCreationInfo.cs
new file mode 100644
index 00000000..dae1345a
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/ProcessCreationInfo.cs
@@ -0,0 +1,37 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+    struct ProcessCreationInfo
+    {
+        public string Name { get; private set; }
+
+        public int  Category { get; private set; }
+        public long TitleId  { get; private set; }
+
+        public ulong CodeAddress    { get; private set; }
+        public int   CodePagesCount { get; private set; }
+
+        public int MmuFlags                 { get; private set; }
+        public int ResourceLimitHandle      { get; private set; }
+        public int PersonalMmHeapPagesCount { get; private set; }
+
+        public ProcessCreationInfo(
+            string Name,
+            int    Category,
+            long   TitleId,
+            ulong  CodeAddress,
+            int    CodePagesCount,
+            int    MmuFlags,
+            int    ResourceLimitHandle,
+            int    PersonalMmHeapPagesCount)
+        {
+            this.Name                     = Name;
+            this.Category                 = Category;
+            this.TitleId                  = TitleId;
+            this.CodeAddress              = CodeAddress;
+            this.CodePagesCount           = CodePagesCount;
+            this.MmuFlags                 = MmuFlags;
+            this.ResourceLimitHandle      = ResourceLimitHandle;
+            this.PersonalMmHeapPagesCount = PersonalMmHeapPagesCount;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/ProcessState.cs b/Ryujinx.HLE/HOS/Kernel/ProcessState.cs
new file mode 100644
index 00000000..98ff4207
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/ProcessState.cs
@@ -0,0 +1,14 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+    enum ProcessState : byte
+    {
+        Created         = 0,
+        CreatedAttached = 1,
+        Started         = 2,
+        Crashed         = 3,
+        Attached        = 4,
+        Exiting         = 5,
+        Exited          = 6,
+        DebugSuspended  = 7
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs b/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs
index 9b475d4e..cbc5e31c 100644
--- a/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs
@@ -1,8 +1,8 @@
 using ChocolArm64.Events;
 using ChocolArm64.Memory;
 using ChocolArm64.State;
-using Ryujinx.Common.Logging;
 using Ryujinx.HLE.HOS.Ipc;
+using Ryujinx.Common.Logging;
 using System;
 using System.Collections.Generic;
 
@@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Kernel
         private Dictionary<int, SvcFunc> SvcFuncs;
 
         private Switch        Device;
-        private Process       Process;
+        private KProcess      Process;
         private Horizon       System;
         private MemoryManager Memory;
 
@@ -39,9 +39,7 @@ namespace Ryujinx.HLE.HOS.Kernel
             }
         }
 
-        private static Random Rng;
-
-        public SvcHandler(Switch Device, Process Process)
+        public SvcHandler(Switch Device, KProcess Process)
         {
             SvcFuncs = new Dictionary<int, SvcFunc>()
             {
@@ -51,14 +49,14 @@ namespace Ryujinx.HLE.HOS.Kernel
                 { 0x05, SvcUnmapMemory                   },
                 { 0x06, SvcQueryMemory                   },
                 { 0x07, SvcExitProcess                   },
-                { 0x08, SvcCreateThread                  },
+                { 0x08, CreateThread64                   },
                 { 0x09, SvcStartThread                   },
                 { 0x0a, SvcExitThread                    },
                 { 0x0b, SvcSleepThread                   },
                 { 0x0c, SvcGetThreadPriority             },
                 { 0x0d, SvcSetThreadPriority             },
                 { 0x0e, SvcGetThreadCoreMask             },
-                { 0x0f, SvcSetThreadCoreMask             },
+                { 0x0f, SetThreadCoreMask64              },
                 { 0x10, SvcGetCurrentProcessorNumber     },
                 { 0x11, SignalEvent64                    },
                 { 0x12, ClearEvent64                     },
@@ -77,36 +75,34 @@ namespace Ryujinx.HLE.HOS.Kernel
                 { 0x1f, SvcConnectToNamedPort            },
                 { 0x21, SvcSendSyncRequest               },
                 { 0x22, SvcSendSyncRequestWithUserBuffer },
+                { 0x24, GetProcessId64                   },
                 { 0x25, SvcGetThreadId                   },
                 { 0x26, SvcBreak                         },
                 { 0x27, SvcOutputDebugString             },
-                { 0x29, SvcGetInfo                       },
+                { 0x29, GetInfo64                        },
                 { 0x2c, SvcMapPhysicalMemory             },
                 { 0x2d, SvcUnmapPhysicalMemory           },
                 { 0x32, SvcSetThreadActivity             },
                 { 0x33, SvcGetThreadContext3             },
                 { 0x34, SvcWaitForAddress                },
                 { 0x35, SvcSignalToAddress               },
-                { 0x45, CreateEvent64                    }
+                { 0x45, CreateEvent64                    },
+                { 0x65, GetProcessList64                 },
+                { 0x6f, GetSystemInfo64                  },
+                { 0x70, CreatePort64                     },
+                { 0x71, ManageNamedPort64                }
             };
 
             this.Device  = Device;
             this.Process = Process;
-            this.System  = Process.Device.System;
-            this.Memory  = Process.Memory;
-        }
-
-        static SvcHandler()
-        {
-            Rng = new Random();
+            this.System  = Device.System;
+            this.Memory  = Process.CpuMemory;
         }
 
         public void SvcCall(object sender, InstExceptionEventArgs e)
         {
             CpuThreadState ThreadState = (CpuThreadState)sender;
 
-            Process.GetThread(ThreadState.Tpidr).LastPc = e.Position;
-
             if (SvcFuncs.TryGetValue(e.Id, out SvcFunc Func))
             {
                 Logger.PrintDebug(LogClass.KernelSvc, $"{Func.Method.Name} called.");
@@ -117,7 +113,7 @@ namespace Ryujinx.HLE.HOS.Kernel
             }
             else
             {
-                Process.PrintStackTrace(ThreadState);
+                //Process.PrintStackTrace(ThreadState);
 
                 throw new NotImplementedException($"0x{e.Id:x4}");
             }
diff --git a/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs b/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs
index 560ad4b3..b5845f0b 100644
--- a/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs
@@ -11,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Kernel
         {
             ulong Size = ThreadState.X1;
 
-            if ((Size & 0xFFFFFFFE001FFFFF) != 0)
+            if ((Size & 0xfffffffe001fffff) != 0)
             {
                 Logger.PrintWarning(LogClass.KernelSvc, $"Heap size 0x{Size:x16} is not aligned!");
 
@@ -20,24 +20,24 @@ namespace Ryujinx.HLE.HOS.Kernel
                 return;
             }
 
-            long Result = Process.MemoryManager.TrySetHeapSize((long)Size, out long Position);
+            KernelResult Result = Process.MemoryManager.SetHeapSize(Size, out ulong Position);
 
             ThreadState.X0 = (ulong)Result;
 
-            if (Result == 0)
+            if (Result == KernelResult.Success)
             {
-                ThreadState.X1 = (ulong)Position;
+                ThreadState.X1 = Position;
             }
             else
             {
-                Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
+                Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{Result}\".");
             }
         }
 
         private void SvcSetMemoryAttribute(CpuThreadState ThreadState)
         {
-            long Position = (long)ThreadState.X0;
-            long Size     = (long)ThreadState.X1;
+            ulong Position = ThreadState.X0;
+            ulong Size     = ThreadState.X1;
 
             if (!PageAligned(Position))
             {
@@ -72,19 +72,19 @@ namespace Ryujinx.HLE.HOS.Kernel
                 return;
             }
 
-            long Result = Process.MemoryManager.SetMemoryAttribute(
+            KernelResult Result = Process.MemoryManager.SetMemoryAttribute(
                 Position,
                 Size,
                 AttributeMask,
                 AttributeValue);
 
-            if (Result != 0)
+            if (Result != KernelResult.Success)
             {
-                Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
+                Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{Result}\".");
             }
             else
             {
-                Memory.StopObservingRegion(Position, Size);
+                Memory.StopObservingRegion((long)Position, (long)Size);
             }
 
             ThreadState.X0 = (ulong)Result;
@@ -92,9 +92,9 @@ namespace Ryujinx.HLE.HOS.Kernel
 
         private void SvcMapMemory(CpuThreadState ThreadState)
         {
-            long Dst  = (long)ThreadState.X0;
-            long Src  = (long)ThreadState.X1;
-            long Size = (long)ThreadState.X2;
+            ulong Dst  = ThreadState.X0;
+            ulong Src  = ThreadState.X1;
+            ulong Size = ThreadState.X2;
 
             if (!PageAligned(Src | Dst))
             {
@@ -114,7 +114,7 @@ namespace Ryujinx.HLE.HOS.Kernel
                 return;
             }
 
-            if ((ulong)(Src + Size) <= (ulong)Src || (ulong)(Dst + Size) <= (ulong)Dst)
+            if (Src + Size <= Src || Dst + Size <= Dst)
             {
                 Logger.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!");
 
@@ -123,7 +123,9 @@ namespace Ryujinx.HLE.HOS.Kernel
                 return;
             }
 
-            if (!InsideAddrSpace(Src, Size))
+            KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+            if (!CurrentProcess.MemoryManager.InsideAddrSpace(Src, Size))
             {
                 Logger.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!");
 
@@ -132,7 +134,9 @@ namespace Ryujinx.HLE.HOS.Kernel
                 return;
             }
 
-            if (!InsideNewMapRegion(Dst, Size))
+            if (CurrentProcess.MemoryManager.OutsideStackRegion(Dst, Size) ||
+                CurrentProcess.MemoryManager.InsideHeapRegion  (Dst, Size) ||
+                CurrentProcess.MemoryManager.InsideAliasRegion (Dst, Size))
             {
                 Logger.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!");
 
@@ -141,9 +145,9 @@ namespace Ryujinx.HLE.HOS.Kernel
                 return;
             }
 
-            long Result = Process.MemoryManager.Map(Src, Dst, Size);
+            KernelResult Result = Process.MemoryManager.Map(Dst, Src, Size);
 
-            if (Result != 0)
+            if (Result != KernelResult.Success)
             {
                 Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
             }
@@ -153,9 +157,9 @@ namespace Ryujinx.HLE.HOS.Kernel
 
         private void SvcUnmapMemory(CpuThreadState ThreadState)
         {
-            long Dst  = (long)ThreadState.X0;
-            long Src  = (long)ThreadState.X1;
-            long Size = (long)ThreadState.X2;
+            ulong Dst  = ThreadState.X0;
+            ulong Src  = ThreadState.X1;
+            ulong Size = ThreadState.X2;
 
             if (!PageAligned(Src | Dst))
             {
@@ -175,7 +179,7 @@ namespace Ryujinx.HLE.HOS.Kernel
                 return;
             }
 
-            if ((ulong)(Src + Size) <= (ulong)Src || (ulong)(Dst + Size) <= (ulong)Dst)
+            if (Src + Size <= Src || Dst + Size <= Dst)
             {
                 Logger.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!");
 
@@ -184,7 +188,9 @@ namespace Ryujinx.HLE.HOS.Kernel
                 return;
             }
 
-            if (!InsideAddrSpace(Src, Size))
+            KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+            if (!CurrentProcess.MemoryManager.InsideAddrSpace(Src, Size))
             {
                 Logger.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!");
 
@@ -193,7 +199,9 @@ namespace Ryujinx.HLE.HOS.Kernel
                 return;
             }
 
-            if (!InsideNewMapRegion(Dst, Size))
+            if (CurrentProcess.MemoryManager.OutsideStackRegion(Dst, Size) ||
+                CurrentProcess.MemoryManager.InsideHeapRegion  (Dst, Size) ||
+                CurrentProcess.MemoryManager.InsideAliasRegion (Dst, Size))
             {
                 Logger.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!");
 
@@ -202,9 +210,9 @@ namespace Ryujinx.HLE.HOS.Kernel
                 return;
             }
 
-            long Result = Process.MemoryManager.Unmap(Src, Dst, Size);
+            KernelResult Result = Process.MemoryManager.Unmap(Dst, Src, Size);
 
-            if (Result != 0)
+            if (Result != KernelResult.Success)
             {
                 Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
             }
@@ -214,19 +222,19 @@ namespace Ryujinx.HLE.HOS.Kernel
 
         private void SvcQueryMemory(CpuThreadState ThreadState)
         {
-            long InfoPtr  = (long)ThreadState.X0;
-            long Position = (long)ThreadState.X2;
+            long  InfoPtr  = (long)ThreadState.X0;
+            ulong Position =       ThreadState.X2;
 
             KMemoryInfo BlkInfo = Process.MemoryManager.QueryMemory(Position);
 
-            Memory.WriteInt64(InfoPtr + 0x00, BlkInfo.Position);
-            Memory.WriteInt64(InfoPtr + 0x08, BlkInfo.Size);
-            Memory.WriteInt32(InfoPtr + 0x10, (int)BlkInfo.State & 0xff);
-            Memory.WriteInt32(InfoPtr + 0x14, (int)BlkInfo.Attribute);
-            Memory.WriteInt32(InfoPtr + 0x18, (int)BlkInfo.Permission);
-            Memory.WriteInt32(InfoPtr + 0x1c, BlkInfo.IpcRefCount);
-            Memory.WriteInt32(InfoPtr + 0x20, BlkInfo.DeviceRefCount);
-            Memory.WriteInt32(InfoPtr + 0x24, 0);
+            Memory.WriteUInt64(InfoPtr + 0x00, BlkInfo.Address);
+            Memory.WriteUInt64(InfoPtr + 0x08, BlkInfo.Size);
+            Memory.WriteInt32 (InfoPtr + 0x10, (int)BlkInfo.State & 0xff);
+            Memory.WriteInt32 (InfoPtr + 0x14, (int)BlkInfo.Attribute);
+            Memory.WriteInt32 (InfoPtr + 0x18, (int)BlkInfo.Permission);
+            Memory.WriteInt32 (InfoPtr + 0x1c, BlkInfo.IpcRefCount);
+            Memory.WriteInt32 (InfoPtr + 0x20, BlkInfo.DeviceRefCount);
+            Memory.WriteInt32 (InfoPtr + 0x24, 0);
 
             ThreadState.X0 = 0;
             ThreadState.X1 = 0;
@@ -234,13 +242,13 @@ namespace Ryujinx.HLE.HOS.Kernel
 
         private void SvcMapSharedMemory(CpuThreadState ThreadState)
         {
-            int  Handle   =  (int)ThreadState.X0;
-            long Position = (long)ThreadState.X1;
-            long Size     = (long)ThreadState.X2;
+            int   Handle  =  (int)ThreadState.X0;
+            ulong Address =       ThreadState.X1;
+            ulong Size    =       ThreadState.X2;
 
-            if (!PageAligned(Position))
+            if (!PageAligned(Address))
             {
-                Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
+                Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Address:x16} is not page aligned!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
 
@@ -256,9 +264,9 @@ namespace Ryujinx.HLE.HOS.Kernel
                 return;
             }
 
-            if ((ulong)(Position + Size) <= (ulong)Position)
+            if (Address + Size <= Address)
             {
-                Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
+                Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Address:x16} / size 0x{Size:x16}!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
 
@@ -276,7 +284,9 @@ namespace Ryujinx.HLE.HOS.Kernel
                 return;
             }
 
-            KSharedMemory SharedMemory = Process.HandleTable.GetObject<KSharedMemory>(Handle);
+            KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+            KSharedMemory SharedMemory = CurrentProcess.HandleTable.GetObject<KSharedMemory>(Handle);
 
             if (SharedMemory == null)
             {
@@ -287,29 +297,27 @@ namespace Ryujinx.HLE.HOS.Kernel
                 return;
             }
 
-            if (!InsideAddrSpace(Position, Size) || InsideMapRegion(Position, Size) || InsideHeapRegion(Position, Size))
+            if (CurrentProcess.MemoryManager.IsInvalidRegion  (Address, Size) ||
+                CurrentProcess.MemoryManager.InsideHeapRegion (Address, Size) ||
+                CurrentProcess.MemoryManager.InsideAliasRegion(Address, Size))
             {
-                Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} out of range!");
+                Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Address:x16} out of range!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
 
                 return;
             }
 
-            if (SharedMemory.Size != Size)
+            KernelResult Result = SharedMemory.MapIntoProcess(
+                CurrentProcess.MemoryManager,
+                Address,
+                Size,
+                CurrentProcess,
+                Permission);
+
+            if (Result != KernelResult.Success)
             {
-                Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} does not match shared memory size 0x{SharedMemory.Size:16}!");
-
-                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
-
-                return;
-            }
-
-            long Result = Process.MemoryManager.MapSharedMemory(SharedMemory, Permission, Position);
-
-            if (Result != 0)
-            {
-                Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
+                Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{Result}\".");
             }
 
             ThreadState.X0 = (ulong)Result;
@@ -317,13 +325,13 @@ namespace Ryujinx.HLE.HOS.Kernel
 
         private void SvcUnmapSharedMemory(CpuThreadState ThreadState)
         {
-            int  Handle   =  (int)ThreadState.X0;
-            long Position = (long)ThreadState.X1;
-            long Size     = (long)ThreadState.X2;
+            int   Handle  =  (int)ThreadState.X0;
+            ulong Address =       ThreadState.X1;
+            ulong Size    =       ThreadState.X2;
 
-            if (!PageAligned(Position))
+            if (!PageAligned(Address))
             {
-                Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
+                Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Address:x16} is not page aligned!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
 
@@ -339,16 +347,18 @@ namespace Ryujinx.HLE.HOS.Kernel
                 return;
             }
 
-            if ((ulong)(Position + Size) <= (ulong)Position)
+            if (Address + Size <= Address)
             {
-                Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
+                Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Address:x16} / size 0x{Size:x16}!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
 
                 return;
             }
 
-            KSharedMemory SharedMemory = Process.HandleTable.GetObject<KSharedMemory>(Handle);
+            KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+            KSharedMemory SharedMemory = CurrentProcess.HandleTable.GetObject<KSharedMemory>(Handle);
 
             if (SharedMemory == null)
             {
@@ -359,20 +369,26 @@ namespace Ryujinx.HLE.HOS.Kernel
                 return;
             }
 
-            if (!InsideAddrSpace(Position, Size) || InsideMapRegion(Position, Size) || InsideHeapRegion(Position, Size))
+            if (CurrentProcess.MemoryManager.IsInvalidRegion  (Address, Size) ||
+                CurrentProcess.MemoryManager.InsideHeapRegion (Address, Size) ||
+                CurrentProcess.MemoryManager.InsideAliasRegion(Address, Size))
             {
-                Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} out of range!");
+                Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Address:x16} out of range!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
 
                 return;
             }
 
-            long Result = Process.MemoryManager.UnmapSharedMemory(Position, Size);
+            KernelResult Result = SharedMemory.UnmapFromProcess(
+                CurrentProcess.MemoryManager,
+                Address,
+                Size,
+                CurrentProcess);
 
-            if (Result != 0)
+            if (Result != KernelResult.Success)
             {
-                Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
+                Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{Result}\".");
             }
 
             ThreadState.X0 = (ulong)Result;
@@ -380,12 +396,12 @@ namespace Ryujinx.HLE.HOS.Kernel
 
         private void SvcCreateTransferMemory(CpuThreadState ThreadState)
         {
-            long Position = (long)ThreadState.X1;
-            long Size     = (long)ThreadState.X2;
+            ulong Address = ThreadState.X1;
+            ulong Size    = ThreadState.X2;
 
-            if (!PageAligned(Position))
+            if (!PageAligned(Address))
             {
-                Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
+                Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Address:x16} is not page aligned!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
 
@@ -401,9 +417,9 @@ namespace Ryujinx.HLE.HOS.Kernel
                 return;
             }
 
-            if ((ulong)(Position + Size) <= (ulong)Position)
+            if (Address + Size <= Address)
             {
-                Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
+                Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Address:x16} / size 0x{Size:x16}!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
 
@@ -421,9 +437,9 @@ namespace Ryujinx.HLE.HOS.Kernel
                 return;
             }
 
-            Process.MemoryManager.ReserveTransferMemory(Position, Size, Permission);
+            Process.MemoryManager.ReserveTransferMemory(Address, Size, Permission);
 
-            KTransferMemory TransferMemory = new KTransferMemory(Position, Size);
+            KTransferMemory TransferMemory = new KTransferMemory(Address, Size);
 
             KernelResult Result = Process.HandleTable.GenerateHandle(TransferMemory, out int Handle);
 
@@ -433,12 +449,12 @@ namespace Ryujinx.HLE.HOS.Kernel
 
         private void SvcMapPhysicalMemory(CpuThreadState ThreadState)
         {
-            long Position = (long)ThreadState.X0;
-            long Size     = (long)ThreadState.X1;
+            ulong Address = ThreadState.X0;
+            ulong Size    = ThreadState.X1;
 
-            if (!PageAligned(Position))
+            if (!PageAligned(Address))
             {
-                Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
+                Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Address:x16} is not page aligned!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
 
@@ -454,27 +470,39 @@ namespace Ryujinx.HLE.HOS.Kernel
                 return;
             }
 
-            if ((ulong)(Position + Size) <= (ulong)Position)
+            if (Address + Size <= Address)
             {
-                Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
+                Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Address:x16} / size 0x{Size:x16}!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
 
                 return;
             }
 
-            if (!InsideAddrSpace(Position, Size))
+            KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+            if ((CurrentProcess.PersonalMmHeapPagesCount & 0xfffffffffffff) == 0)
             {
-                Logger.PrintWarning(LogClass.KernelSvc, $"Invalid address {Position:x16}!");
+                Logger.PrintWarning(LogClass.KernelSvc, $"System resource size is zero.");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
+
+                return;
+            }
+
+            if (!CurrentProcess.MemoryManager.InsideAddrSpace   (Address, Size) ||
+                 CurrentProcess.MemoryManager.OutsideAliasRegion(Address, Size))
+            {
+                Logger.PrintWarning(LogClass.KernelSvc, $"Invalid address {Address:x16}.");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
 
                 return;
             }
 
-            long Result = Process.MemoryManager.MapPhysicalMemory(Position, Size);
+            KernelResult Result = Process.MemoryManager.MapPhysicalMemory(Address, Size);
 
-            if (Result != 0)
+            if (Result != KernelResult.Success)
             {
                 Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
             }
@@ -484,12 +512,12 @@ namespace Ryujinx.HLE.HOS.Kernel
 
         private void SvcUnmapPhysicalMemory(CpuThreadState ThreadState)
         {
-            long Position = (long)ThreadState.X0;
-            long Size     = (long)ThreadState.X1;
+            ulong Address = ThreadState.X0;
+            ulong Size    = ThreadState.X1;
 
-            if (!PageAligned(Position))
+            if (!PageAligned(Address))
             {
-                Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
+                Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Address:x16} is not page aligned!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
 
@@ -505,27 +533,39 @@ namespace Ryujinx.HLE.HOS.Kernel
                 return;
             }
 
-            if ((ulong)(Position + Size) <= (ulong)Position)
+            if (Address + Size <= Address)
             {
-                Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
+                Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Address:x16} / size 0x{Size:x16}!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
 
                 return;
             }
 
-            if (!InsideAddrSpace(Position, Size))
+            KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+            if ((CurrentProcess.PersonalMmHeapPagesCount & 0xfffffffffffff) == 0)
             {
-                Logger.PrintWarning(LogClass.KernelSvc, $"Invalid address {Position:x16}!");
+                Logger.PrintWarning(LogClass.KernelSvc, $"System resource size is zero.");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
+
+                return;
+            }
+
+            if (!CurrentProcess.MemoryManager.InsideAddrSpace   (Address, Size) ||
+                 CurrentProcess.MemoryManager.OutsideAliasRegion(Address, Size))
+            {
+                Logger.PrintWarning(LogClass.KernelSvc, $"Invalid address {Address:x16}.");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
 
                 return;
             }
 
-            long Result = Process.MemoryManager.UnmapPhysicalMemory(Position, Size);
+            KernelResult Result = Process.MemoryManager.UnmapPhysicalMemory(Address, Size);
 
-            if (Result != 0)
+            if (Result != KernelResult.Success)
             {
                 Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
             }
@@ -533,45 +573,9 @@ namespace Ryujinx.HLE.HOS.Kernel
             ThreadState.X0 = (ulong)Result;
         }
 
-        private static bool PageAligned(long Position)
+        private static bool PageAligned(ulong Position)
         {
             return (Position & (KMemoryManager.PageSize - 1)) == 0;
         }
-
-        private bool InsideAddrSpace(long Position, long Size)
-        {
-            ulong Start = (ulong)Position;
-            ulong End   = (ulong)Size + Start;
-
-            return Start >= (ulong)Process.MemoryManager.AddrSpaceStart &&
-                   End   <  (ulong)Process.MemoryManager.AddrSpaceEnd;
-        }
-
-        private bool InsideMapRegion(long Position, long Size)
-        {
-            ulong Start = (ulong)Position;
-            ulong End   = (ulong)Size + Start;
-
-            return Start >= (ulong)Process.MemoryManager.MapRegionStart &&
-                   End   <  (ulong)Process.MemoryManager.MapRegionEnd;
-        }
-
-        private bool InsideHeapRegion(long Position, long Size)
-        {
-            ulong Start = (ulong)Position;
-            ulong End   = (ulong)Size + Start;
-
-            return Start >= (ulong)Process.MemoryManager.HeapRegionStart &&
-                   End   <  (ulong)Process.MemoryManager.HeapRegionEnd;
-        }
-
-        private bool InsideNewMapRegion(long Position, long Size)
-        {
-            ulong Start = (ulong)Position;
-            ulong End   = (ulong)Size + Start;
-
-            return Start >= (ulong)Process.MemoryManager.NewMapRegionStart &&
-                   End   <  (ulong)Process.MemoryManager.NewMapRegionEnd;
-        }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs b/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs
index 54aef5d7..1c1d76f1 100644
--- a/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs
@@ -1,5 +1,6 @@
 using ChocolArm64.Memory;
 using ChocolArm64.State;
+using Ryujinx.Common;
 using Ryujinx.Common.Logging;
 using Ryujinx.HLE.Exceptions;
 using Ryujinx.HLE.HOS.Ipc;
@@ -13,13 +14,9 @@ namespace Ryujinx.HLE.HOS.Kernel
 {
     partial class SvcHandler
     {
-        private const int AllowedCpuIdBitmask = 0b1111;
-
-        private const bool EnableProcessDebugging = false;
-
         private void SvcExitProcess(CpuThreadState ThreadState)
         {
-            Device.System.ExitProcess(Process.ProcessId);
+            System.Scheduler.GetCurrentProcess().Terminate();
         }
 
         private void SignalEvent64(CpuThreadState ThreadState)
@@ -106,7 +103,7 @@ namespace Ryujinx.HLE.HOS.Kernel
             else if (Obj is KTransferMemory TransferMemory)
             {
                 Process.MemoryManager.ResetTransferMemory(
-                    TransferMemory.Position,
+                    TransferMemory.Address,
                     TransferMemory.Size);
             }
 
@@ -120,18 +117,28 @@ namespace Ryujinx.HLE.HOS.Kernel
 
         private KernelResult ResetSignal(int Handle)
         {
-            KReadableEvent ReadableEvent = Process.HandleTable.GetObject<KReadableEvent>(Handle);
+            KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+            KReadableEvent ReadableEvent = CurrentProcess.HandleTable.GetObject<KReadableEvent>(Handle);
 
             KernelResult Result;
 
-            //TODO: KProcess support.
             if (ReadableEvent != null)
             {
                 Result = ReadableEvent.ClearIfSignaled();
             }
             else
             {
-                Result = KernelResult.InvalidHandle;
+                KProcess Process = CurrentProcess.HandleTable.GetKProcess(Handle);
+
+                if (Process != null)
+                {
+                    Result = Process.ClearIfNotExited();
+                }
+                else
+                {
+                    Result = KernelResult.InvalidHandle;
+                }
             }
 
             if (Result == KernelResult.InvalidState)
@@ -187,17 +194,13 @@ namespace Ryujinx.HLE.HOS.Kernel
 
         private void SendSyncRequest(CpuThreadState ThreadState, long MessagePtr, long Size, int Handle)
         {
-            KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
-
             byte[] MessageData = Memory.ReadBytes(MessagePtr, Size);
 
             KSession Session = Process.HandleTable.GetObject<KSession>(Handle);
 
             if (Session != null)
             {
-                //Process.Scheduler.Suspend(CurrThread);
-
-                System.CriticalSectionLock.Lock();
+                System.CriticalSection.Enter();
 
                 KThread CurrentThread = System.Scheduler.GetCurrentThread();
 
@@ -214,7 +217,9 @@ namespace Ryujinx.HLE.HOS.Kernel
                     Message,
                     MessagePtr));
 
-                System.CriticalSectionLock.Unlock();
+                System.ThreadCounter.AddCount();
+
+                System.CriticalSection.Leave();
 
                 ThreadState.X0 = (ulong)CurrentThread.ObjSyncResult;
             }
@@ -238,25 +243,65 @@ namespace Ryujinx.HLE.HOS.Kernel
                 IpcMessage.Message,
                 IpcMessage.MessagePtr);
 
+            System.ThreadCounter.Signal();
+
             IpcMessage.Thread.Reschedule(ThreadSchedState.Running);
         }
 
+        private void GetProcessId64(CpuThreadState ThreadState)
+        {
+            int Handle = (int)ThreadState.X1;
+
+            KernelResult Result = GetProcessId(Handle, out long Pid);
+
+            ThreadState.X0 = (ulong)Result;
+            ThreadState.X1 = (ulong)Pid;
+        }
+
+        private KernelResult GetProcessId(int Handle, out long Pid)
+        {
+            KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+            KProcess Process = CurrentProcess.HandleTable.GetKProcess(Handle);
+
+            if (Process == null)
+            {
+                KThread Thread = CurrentProcess.HandleTable.GetKThread(Handle);
+
+                if (Thread != null)
+                {
+                    Process = Thread.Owner;
+                }
+
+                //TODO: KDebugEvent.
+            }
+
+            Pid = Process?.Pid ?? 0;
+
+            return Process != null
+                ? KernelResult.Success
+                : KernelResult.InvalidHandle;
+        }
+
         private void SvcBreak(CpuThreadState ThreadState)
         {
             long Reason  = (long)ThreadState.X0;
             long Unknown = (long)ThreadState.X1;
             long Info    = (long)ThreadState.X2;
 
+            KThread CurrentThread = System.Scheduler.GetCurrentThread();
+
             if ((Reason & (1 << 31)) == 0)
             {
-                Process.PrintStackTrace(ThreadState);
+                CurrentThread.PrintGuestStackTrace();
 
                 throw new GuestBrokeExecutionException();
             }
             else
             {
-                Logger.PrintInfo(LogClass.KernelSvc, "Debugger triggered");
-                Process.PrintStackTrace(ThreadState);
+                Logger.PrintInfo(LogClass.KernelSvc, "Debugger triggered.");
+
+                CurrentThread.PrintGuestStackTrace();
             }
         }
 
@@ -272,98 +317,243 @@ namespace Ryujinx.HLE.HOS.Kernel
             ThreadState.X0 = 0;
         }
 
-        private void SvcGetInfo(CpuThreadState ThreadState)
+        private void GetInfo64(CpuThreadState ThreadState)
         {
             long StackPtr = (long)ThreadState.X0;
-            int  InfoType =  (int)ThreadState.X1;
-            long Handle   = (long)ThreadState.X2;
-            int  InfoId   =  (int)ThreadState.X3;
+            uint Id       = (uint)ThreadState.X1;
+            int  Handle   =  (int)ThreadState.X2;
+            long SubId    = (long)ThreadState.X3;
 
-            //Fail for info not available on older Kernel versions.
-            if (InfoType == 18 ||
-                InfoType == 19 ||
-                InfoType == 20 ||
-                InfoType == 21 ||
-                InfoType == 22)
-            {
-                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidEnumValue);
+            KernelResult Result = GetInfo(Id, Handle, SubId, out long Value);
 
-                return;
-            }
+            ThreadState.X0 = (ulong)Result;
+            ThreadState.X1 = (ulong)Value;
+        }
 
-            switch (InfoType)
+        private KernelResult GetInfo(uint Id, int Handle, long SubId, out long Value)
+        {
+            Value = 0;
+
+            switch (Id)
             {
                 case 0:
-                    ThreadState.X1 = AllowedCpuIdBitmask;
-                    break;
-
+                case 1:
                 case 2:
-                    ThreadState.X1 = (ulong)Process.MemoryManager.MapRegionStart;
-                    break;
-
                 case 3:
-                    ThreadState.X1 = (ulong)Process.MemoryManager.MapRegionEnd -
-                                     (ulong)Process.MemoryManager.MapRegionStart;
-                    break;
-
                 case 4:
-                    ThreadState.X1 = (ulong)Process.MemoryManager.HeapRegionStart;
-                    break;
-
                 case 5:
-                    ThreadState.X1 = (ulong)Process.MemoryManager.HeapRegionEnd -
-                                     (ulong)Process.MemoryManager.HeapRegionStart;
-                    break;
-
                 case 6:
-                    ThreadState.X1 = (ulong)Process.Device.Memory.Allocator.TotalAvailableSize;
-                    break;
-
                 case 7:
-                    ThreadState.X1 = (ulong)Process.Device.Memory.Allocator.TotalUsedSize;
+                case 12:
+                case 13:
+                case 14:
+                case 15:
+                case 16:
+                case 17:
+                case 18:
+                case 20:
+                case 21:
+                case 22:
+                {
+                    if (SubId != 0)
+                    {
+                        return KernelResult.InvalidCombination;
+                    }
+
+                    KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+                    KProcess Process = CurrentProcess.HandleTable.GetKProcess(Handle);
+
+                    if (Process == null)
+                    {
+                        return KernelResult.InvalidHandle;
+                    }
+
+                    switch (Id)
+                    {
+                        case 0: Value = Process.Capabilities.AllowedCpuCoresMask;    break;
+                        case 1: Value = Process.Capabilities.AllowedThreadPriosMask; break;
+
+                        case 2: Value = (long)Process.MemoryManager.AliasRegionStart; break;
+                        case 3: Value = (long)(Process.MemoryManager.AliasRegionEnd -
+                                               Process.MemoryManager.AliasRegionStart); break;
+
+                        case 4: Value = (long)Process.MemoryManager.HeapRegionStart; break;
+                        case 5: Value = (long)(Process.MemoryManager.HeapRegionEnd -
+                                               Process.MemoryManager.HeapRegionStart); break;
+
+                        case 6: Value = (long)Process.GetMemoryCapacity(); break;
+
+                        case 7: Value = (long)Process.GetMemoryUsage(); break;
+
+                        case 12: Value = (long)Process.MemoryManager.GetAddrSpaceBaseAddr(); break;
+
+                        case 13: Value = (long)Process.MemoryManager.GetAddrSpaceSize(); break;
+
+                        case 14: Value = (long)Process.MemoryManager.StackRegionStart; break;
+                        case 15: Value = (long)(Process.MemoryManager.StackRegionEnd -
+                                                Process.MemoryManager.StackRegionStart); break;
+
+                        case 16: Value = (long)Process.PersonalMmHeapPagesCount * KMemoryManager.PageSize; break;
+
+                        case 17:
+                            if (Process.PersonalMmHeapPagesCount != 0)
+                            {
+                                Value = Process.MemoryManager.GetMmUsedPages() * KMemoryManager.PageSize;
+                            }
+
+                            break;
+
+                        case 18: Value = Process.TitleId; break;
+
+                        case 20: Value = (long)Process.UserExceptionContextAddress; break;
+
+                        case 21: Value = (long)Process.GetMemoryCapacityWithoutPersonalMmHeap(); break;
+
+                        case 22: Value = (long)Process.GetMemoryUsageWithoutPersonalMmHeap(); break;
+                    }
+
                     break;
+                }
 
                 case 8:
-                    ThreadState.X1 = EnableProcessDebugging ? 1 : 0;
+                {
+                    if (Handle != 0)
+                    {
+                        return KernelResult.InvalidHandle;
+                    }
+
+                    if (SubId != 0)
+                    {
+                        return KernelResult.InvalidCombination;
+                    }
+
+                    Value = System.Scheduler.GetCurrentProcess().Debug ? 1 : 0;
+
                     break;
+                }
+
+                case 9:
+                {
+                    if (Handle != 0)
+                    {
+                        return KernelResult.InvalidHandle;
+                    }
+
+                    if (SubId != 0)
+                    {
+                        return KernelResult.InvalidCombination;
+                    }
+
+                    KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+                    if (CurrentProcess.ResourceLimit != null)
+                    {
+                        KHandleTable   HandleTable   = CurrentProcess.HandleTable;
+                        KResourceLimit ResourceLimit = CurrentProcess.ResourceLimit;
+
+                        KernelResult Result = HandleTable.GenerateHandle(ResourceLimit, out int ResLimHandle);
+
+                        if (Result != KernelResult.Success)
+                        {
+                            return Result;
+                        }
+
+                        Value = (uint)ResLimHandle;
+                    }
+
+                    break;
+                }
+
+                case 10:
+                {
+                    if (Handle != 0)
+                    {
+                        return KernelResult.InvalidHandle;
+                    }
+
+                    int CurrentCore = System.Scheduler.GetCurrentThread().CurrentCore;
+
+                    if (SubId != -1 && SubId != CurrentCore)
+                    {
+                        return KernelResult.InvalidCombination;
+                    }
+
+                    Value = System.Scheduler.CoreContexts[CurrentCore].TotalIdleTimeTicks;
+
+                    break;
+                }
 
                 case 11:
-                    ThreadState.X1 = (ulong)Rng.Next() + ((ulong)Rng.Next() << 32);
+                {
+                    if (Handle != 0)
+                    {
+                        return KernelResult.InvalidHandle;
+                    }
+
+                    if ((ulong)SubId > 3)
+                    {
+                        return KernelResult.InvalidCombination;
+                    }
+
+                    KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+
+                    Value = CurrentProcess.RandomEntropy[SubId];
+
                     break;
+                }
+
+                case 0xf0000002u:
+                {
+                    if (SubId < -1 || SubId > 3)
+                    {
+                        return KernelResult.InvalidCombination;
+                    }
+
+                    KThread Thread = System.Scheduler.GetCurrentProcess().HandleTable.GetKThread(Handle);
+
+                    if (Thread == null)
+                    {
+                        return KernelResult.InvalidHandle;
+                    }
+
+                    KThread CurrentThread = System.Scheduler.GetCurrentThread();
+
+                    int CurrentCore = CurrentThread.CurrentCore;
+
+                    if (SubId != -1 && SubId != CurrentCore)
+                    {
+                        return KernelResult.Success;
+                    }
+
+                    KCoreContext CoreContext = System.Scheduler.CoreContexts[CurrentCore];
+
+                    long TimeDelta = PerformanceCounter.ElapsedMilliseconds - CoreContext.LastContextSwitchTime;
+
+                    if (SubId != -1)
+                    {
+                        Value = KTimeManager.ConvertMillisecondsToTicks(TimeDelta);
+                    }
+                    else
+                    {
+                        long TotalTimeRunning = Thread.TotalTimeRunning;
+
+                        if (Thread == CurrentThread)
+                        {
+                            TotalTimeRunning += TimeDelta;
+                        }
+
+                        Value = KTimeManager.ConvertMillisecondsToTicks(TotalTimeRunning);
+                    }
 
-                case 12:
-                    ThreadState.X1 = (ulong)Process.MemoryManager.AddrSpaceStart;
                     break;
+                }
 
-                case 13:
-                    ThreadState.X1 = (ulong)Process.MemoryManager.AddrSpaceEnd -
-                                     (ulong)Process.MemoryManager.AddrSpaceStart;
-                    break;
-
-                case 14:
-                    ThreadState.X1 = (ulong)Process.MemoryManager.NewMapRegionStart;
-                    break;
-
-                case 15:
-                    ThreadState.X1 = (ulong)Process.MemoryManager.NewMapRegionEnd -
-                                     (ulong)Process.MemoryManager.NewMapRegionStart;
-                    break;
-
-                case 16:
-                    ThreadState.X1 = (ulong)(Process.MetaData?.SystemResourceSize ?? 0);
-                    break;
-
-                case 17:
-                    ThreadState.X1 = (ulong)Process.MemoryManager.PersonalMmHeapUsage;
-                    break;
-
-                default:
-                    Process.PrintStackTrace(ThreadState);
-
-                    throw new NotImplementedException($"SvcGetInfo: {InfoType} 0x{Handle:x8} {InfoId}");
+                default: return KernelResult.InvalidEnumValue;
             }
 
-            ThreadState.X0 = 0;
+            return KernelResult.Success;
         }
 
         private void CreateEvent64(CpuThreadState State)
@@ -397,5 +587,241 @@ namespace Ryujinx.HLE.HOS.Kernel
 
             return Result;
         }
+
+        private void GetProcessList64(CpuThreadState State)
+        {
+            ulong Address =      State.X1;
+            int   MaxOut  = (int)State.X2;
+
+            KernelResult Result = GetProcessList(Address, MaxOut, out int Count);
+
+            State.X0 = (ulong)Result;
+            State.X1 = (ulong)Count;
+        }
+
+        private KernelResult GetProcessList(ulong Address, int MaxCount, out int Count)
+        {
+            Count = 0;
+
+            if ((MaxCount >> 28) != 0)
+            {
+                return KernelResult.MaximumExceeded;
+            }
+
+            if (MaxCount != 0)
+            {
+                KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+                ulong CopySize = (ulong)MaxCount * 8;
+
+                if (Address + CopySize <= Address)
+                {
+                    return KernelResult.InvalidMemState;
+                }
+
+                if (CurrentProcess.MemoryManager.OutsideAddrSpace(Address, CopySize))
+                {
+                    return KernelResult.InvalidMemState;
+                }
+            }
+
+            int CopyCount = 0;
+
+            lock (System.Processes)
+            {
+                foreach (KProcess Process in System.Processes.Values)
+                {
+                    if (CopyCount < MaxCount)
+                    {
+                        if (!KernelTransfer.KernelToUserInt64(System, (long)Address + CopyCount * 8, Process.Pid))
+                        {
+                            return KernelResult.UserCopyFailed;
+                        }
+                    }
+
+                    CopyCount++;
+                }
+            }
+
+            Count = CopyCount;
+
+            return KernelResult.Success;
+        }
+
+        private void GetSystemInfo64(CpuThreadState State)
+        {
+            uint Id     = (uint)State.X1;
+            int  Handle =  (int)State.X2;
+            long SubId  = (long)State.X3;
+
+            KernelResult Result = GetSystemInfo(Id, Handle, SubId, out long Value);
+
+            State.X0 = (ulong)Result;
+            State.X1 = (ulong)Value;
+        }
+
+        private KernelResult GetSystemInfo(uint Id, int Handle, long SubId, out long Value)
+        {
+            Value = 0;
+
+            if (Id > 2)
+            {
+                return KernelResult.InvalidEnumValue;
+            }
+
+            if (Handle != 0)
+            {
+                return KernelResult.InvalidHandle;
+            }
+
+            if (Id < 2)
+            {
+                if ((ulong)SubId > 3)
+                {
+                    return KernelResult.InvalidCombination;
+                }
+
+                KMemoryRegionManager Region = System.MemoryRegions[SubId];
+
+                switch (Id)
+                {
+                    //Memory region capacity.
+                    case 0: Value = (long)Region.Size; break;
+
+                    //Memory region free space.
+                    case 1:
+                    {
+                        ulong FreePagesCount = Region.GetFreePages();
+
+                        Value = (long)(FreePagesCount * KMemoryManager.PageSize);
+
+                        break;
+                    }
+                }
+            }
+            else /* if (Id == 2) */
+            {
+                if ((ulong)SubId > 1)
+                {
+                    return KernelResult.InvalidCombination;
+                }
+
+                switch (SubId)
+                {
+                    case 0: Value = System.PrivilegedProcessLowestId;  break;
+                    case 1: Value = System.PrivilegedProcessHighestId; break;
+                }
+            }
+
+            return KernelResult.Success;
+        }
+
+        private void CreatePort64(CpuThreadState State)
+        {
+            int  MaxSessions =  (int)State.X2;
+            bool IsLight     =      (State.X3 & 1) != 0;
+            long NameAddress = (long)State.X4;
+
+            KernelResult Result = CreatePort(
+                MaxSessions,
+                IsLight,
+                NameAddress,
+                out int ServerPortHandle,
+                out int ClientPortHandle);
+
+            State.X0 = (ulong)Result;
+            State.X1 = (ulong)ServerPortHandle;
+            State.X2 = (ulong)ClientPortHandle;
+        }
+
+        private KernelResult CreatePort(
+            int     MaxSessions,
+            bool    IsLight,
+            long    NameAddress,
+            out int ServerPortHandle,
+            out int ClientPortHandle)
+        {
+            ServerPortHandle = ClientPortHandle = 0;
+
+            if (MaxSessions < 1)
+            {
+                return KernelResult.MaximumExceeded;
+            }
+
+            KPort Port = new KPort(System);
+
+            Port.Initialize(MaxSessions, IsLight, NameAddress);
+
+            KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+            KernelResult Result = CurrentProcess.HandleTable.GenerateHandle(Port.ClientPort, out ClientPortHandle);
+
+            if (Result != KernelResult.Success)
+            {
+                return Result;
+            }
+
+            Result = CurrentProcess.HandleTable.GenerateHandle(Port.ServerPort, out ServerPortHandle);
+
+            if (Result != KernelResult.Success)
+            {
+                CurrentProcess.HandleTable.CloseHandle(ClientPortHandle);
+            }
+
+            return Result;
+        }
+
+        private void ManageNamedPort64(CpuThreadState State)
+        {
+            long NameAddress = (long)State.X1;
+            int  MaxSessions =  (int)State.X2;
+
+            KernelResult Result = ManageNamedPort(NameAddress, MaxSessions, out int Handle);
+
+            State.X0 = (ulong)Result;
+            State.X1 = (ulong)Handle;
+        }
+
+        private KernelResult ManageNamedPort(long NameAddress, int MaxSessions, out int Handle)
+        {
+            Handle = 0;
+
+            if (!KernelTransfer.UserToKernelString(System, NameAddress, 12, out string Name))
+            {
+                return KernelResult.UserCopyFailed;
+            }
+
+            if (MaxSessions < 0 || Name.Length > 11)
+            {
+                return KernelResult.MaximumExceeded;
+            }
+
+            if (MaxSessions == 0)
+            {
+                return KClientPort.RemoveName(System, Name);
+            }
+
+            KPort Port = new KPort(System);
+
+            KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+            KernelResult Result = CurrentProcess.HandleTable.GenerateHandle(Port.ServerPort, out Handle);
+
+            if (Result != KernelResult.Success)
+            {
+                return Result;
+            }
+
+            Port.Initialize(MaxSessions, false, 0);
+
+            Result = Port.SetName(Name);
+
+            if (Result != KernelResult.Success)
+            {
+                CurrentProcess.HandleTable.CloseHandle(Handle);
+            }
+
+            return Result;
+        }
     }
 }
diff --git a/Ryujinx.HLE/HOS/Kernel/SvcThread.cs b/Ryujinx.HLE/HOS/Kernel/SvcThread.cs
index 53a557de..ded8f8dc 100644
--- a/Ryujinx.HLE/HOS/Kernel/SvcThread.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SvcThread.cs
@@ -7,48 +7,84 @@ namespace Ryujinx.HLE.HOS.Kernel
 {
     partial class SvcHandler
     {
-        private void SvcCreateThread(CpuThreadState ThreadState)
+        private void CreateThread64(CpuThreadState ThreadState)
         {
-            long EntryPoint  = (long)ThreadState.X1;
-            long ArgsPtr     = (long)ThreadState.X2;
-            long StackTop    = (long)ThreadState.X3;
-            int  Priority    =  (int)ThreadState.X4;
-            int  ProcessorId =  (int)ThreadState.X5;
+            ulong Entrypoint =      ThreadState.X1;
+            ulong ArgsPtr    =      ThreadState.X2;
+            ulong StackTop   =      ThreadState.X3;
+            int   Priority   = (int)ThreadState.X4;
+            int   CpuCore    = (int)ThreadState.X5;
 
-            if ((uint)Priority > 0x3f)
-            {
-                Logger.PrintWarning(LogClass.KernelSvc, $"Invalid priority 0x{Priority:x8}!");
+            KernelResult Result = CreateThread(Entrypoint, ArgsPtr, StackTop, Priority, CpuCore, out int Handle);
 
-                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidPriority);
-
-                return;
-            }
-
-            if (ProcessorId == -2)
-            {
-                //TODO: Get this value from the NPDM file.
-                ProcessorId = 0;
-            }
-            else if ((uint)ProcessorId > 3)
-            {
-                Logger.PrintWarning(LogClass.KernelSvc, $"Invalid core id 0x{ProcessorId:x8}!");
-
-                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreId);
-
-                return;
-            }
-
-            int Handle = Process.MakeThread(
-                EntryPoint,
-                StackTop,
-                ArgsPtr,
-                Priority,
-                ProcessorId);
-
-            ThreadState.X0 = 0;
+            ThreadState.X0 = (ulong)Result;
             ThreadState.X1 = (ulong)Handle;
         }
 
+        private KernelResult CreateThread(
+            ulong   Entrypoint,
+            ulong   ArgsPtr,
+            ulong   StackTop,
+            int     Priority,
+            int     CpuCore,
+            out int Handle)
+        {
+            Handle = 0;
+
+            KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+            if (CpuCore == -2)
+            {
+                CpuCore = CurrentProcess.DefaultCpuCore;
+            }
+
+            if ((uint)CpuCore >= KScheduler.CpuCoresCount || !CurrentProcess.IsCpuCoreAllowed(CpuCore))
+            {
+                return KernelResult.InvalidCpuCore;
+            }
+
+            if ((uint)Priority >= KScheduler.PrioritiesCount || !CurrentProcess.IsPriorityAllowed(Priority))
+            {
+                return KernelResult.InvalidPriority;
+            }
+
+            long Timeout = KTimeManager.ConvertMillisecondsToNanoseconds(100);
+
+            if (CurrentProcess.ResourceLimit != null &&
+               !CurrentProcess.ResourceLimit.Reserve(LimitableResource.Thread, 1, Timeout))
+            {
+                return KernelResult.ResLimitExceeded;
+            }
+
+            KThread Thread = new KThread(System);
+
+            KernelResult Result = CurrentProcess.InitializeThread(
+                Thread,
+                Entrypoint,
+                ArgsPtr,
+                StackTop,
+                Priority,
+                CpuCore);
+
+            if (Result != KernelResult.Success)
+            {
+                CurrentProcess.ResourceLimit?.Release(LimitableResource.Thread, 1);
+
+                return Result;
+            }
+
+            Result = Process.HandleTable.GenerateHandle(Thread, out Handle);
+
+            if (Result != KernelResult.Success)
+            {
+                Thread.Terminate();
+
+                CurrentProcess.ResourceLimit?.Release(LimitableResource.Thread, 1);
+            }
+
+            return Result;
+        }
+
         private void SvcStartThread(CpuThreadState ThreadState)
         {
             int Handle = (int)ThreadState.X0;
@@ -57,11 +93,11 @@ namespace Ryujinx.HLE.HOS.Kernel
 
             if (Thread != null)
             {
-                long Result = Thread.Start();
+                KernelResult Result = Thread.Start();
 
-                if (Result != 0)
+                if (Result != KernelResult.Success)
                 {
-                    Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
+                    Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{Result}\".");
                 }
 
                 ThreadState.X0 = (ulong)Result;
@@ -78,9 +114,9 @@ namespace Ryujinx.HLE.HOS.Kernel
         {
             KThread CurrentThread = System.Scheduler.GetCurrentThread();
 
-            CurrentThread.Exit();
+            System.Scheduler.ExitThread(CurrentThread);
 
-            System.Scheduler.StopThread(CurrentThread);
+            CurrentThread.Exit();
         }
 
         private void SvcSleepThread(CpuThreadState ThreadState)
@@ -176,46 +212,60 @@ namespace Ryujinx.HLE.HOS.Kernel
             }
         }
 
-        private void SvcSetThreadCoreMask(CpuThreadState ThreadState)
+        private void SetThreadCoreMask64(CpuThreadState ThreadState)
         {
             int  Handle        =  (int)ThreadState.X0;
-            int  PrefferedCore =  (int)ThreadState.X1;
+            int  PreferredCore =  (int)ThreadState.X1;
             long AffinityMask  = (long)ThreadState.X2;
 
             Logger.PrintDebug(LogClass.KernelSvc,
                 "Handle = 0x"        + Handle       .ToString("x8") + ", " +
-                "PrefferedCore = 0x" + PrefferedCore.ToString("x8") + ", " +
+                "PreferredCore = 0x" + PreferredCore.ToString("x8") + ", " +
                 "AffinityMask = 0x"  + AffinityMask .ToString("x16"));
 
-            if (PrefferedCore == -2)
-            {
-                //TODO: Get this value from the NPDM file.
-                PrefferedCore = 0;
+            KernelResult Result = SetThreadCoreMask(Handle, PreferredCore, AffinityMask);
 
-                AffinityMask = 1 << PrefferedCore;
+            if (Result != KernelResult.Success)
+            {
+                Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{Result}\".");
+            }
+
+            ThreadState.X0 = (ulong)Result;
+        }
+
+        private KernelResult SetThreadCoreMask(int Handle, int PreferredCore, long AffinityMask)
+        {
+            KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+            if (PreferredCore == -2)
+            {
+                PreferredCore = CurrentProcess.DefaultCpuCore;
+
+                AffinityMask = 1 << PreferredCore;
             }
             else
             {
-                //TODO: Check allowed cores from NPDM file.
-
-                if ((uint)PrefferedCore > 3)
+                if ((CurrentProcess.Capabilities.AllowedCpuCoresMask | AffinityMask) !=
+                     CurrentProcess.Capabilities.AllowedCpuCoresMask)
                 {
-                    if ((PrefferedCore | 2) != -1)
+                    return KernelResult.InvalidCpuCore;
+                }
+
+                if (AffinityMask == 0)
+                {
+                    return KernelResult.InvalidCombination;
+                }
+
+                if ((uint)PreferredCore > 3)
+                {
+                    if ((PreferredCore | 2) != -1)
                     {
-                        Logger.PrintWarning(LogClass.KernelSvc, $"Invalid core id 0x{PrefferedCore:x8}!");
-
-                        ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreId);
-
-                        return;
+                        return KernelResult.InvalidCpuCore;
                     }
                 }
-                else if ((AffinityMask & (1 << PrefferedCore)) == 0)
+                else if ((AffinityMask & (1 << PreferredCore)) == 0)
                 {
-                    Logger.PrintWarning(LogClass.KernelSvc, $"Invalid core mask 0x{AffinityMask:x8}!");
-
-                    ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue);
-
-                    return;
+                    return KernelResult.InvalidCombination;
                 }
             }
 
@@ -223,26 +273,15 @@ namespace Ryujinx.HLE.HOS.Kernel
 
             if (Thread == null)
             {
-                Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
-
-                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
-
-                return;
+                return KernelResult.InvalidHandle;
             }
 
-            long Result = Thread.SetCoreAndAffinityMask(PrefferedCore, AffinityMask);
-
-            if (Result != 0)
-            {
-                Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
-            }
-
-            ThreadState.X0 = (ulong)Result;
+            return Thread.SetCoreAndAffinityMask(PreferredCore, AffinityMask);
         }
 
         private void SvcGetCurrentProcessorNumber(CpuThreadState ThreadState)
         {
-            ThreadState.X0 = (ulong)Process.GetThread(ThreadState.Tpidr).CurrentCore;
+            ThreadState.X0 = (ulong)System.Scheduler.GetCurrentThread().CurrentCore;
         }
 
         private void SvcGetThreadId(CpuThreadState ThreadState)
@@ -254,7 +293,7 @@ namespace Ryujinx.HLE.HOS.Kernel
             if (Thread != null)
             {
                 ThreadState.X0 = 0;
-                ThreadState.X1 = (ulong)Thread.ThreadId;
+                ThreadState.X1 = (ulong)Thread.ThreadUid;
             }
             else
             {
@@ -280,15 +319,24 @@ namespace Ryujinx.HLE.HOS.Kernel
                 return;
             }
 
-            if (Thread.Owner != Process)
+            if (Thread.Owner != System.Scheduler.GetCurrentProcess())
             {
-                Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread owner process!");
+                Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread, it belongs to another process.");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
 
                 return;
             }
 
+            if (Thread == System.Scheduler.GetCurrentThread())
+            {
+                Logger.PrintWarning(LogClass.KernelSvc, "Invalid thread, current thread is not accepted.");
+
+                ThreadState.X0 = (ulong)KernelResult.InvalidThread;
+
+                return;
+            }
+
             long Result = Thread.SetActivity(Pause);
 
             if (Result != 0)
@@ -304,6 +352,9 @@ namespace Ryujinx.HLE.HOS.Kernel
             long Position = (long)ThreadState.X0;
             int  Handle   =  (int)ThreadState.X1;
 
+            KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+            KThread  CurrentThread  = System.Scheduler.GetCurrentThread();
+
             KThread Thread = Process.HandleTable.GetObject<KThread>(Handle);
 
             if (Thread == null)
@@ -315,9 +366,18 @@ namespace Ryujinx.HLE.HOS.Kernel
                 return;
             }
 
-            if (Process.GetThread(ThreadState.Tpidr) == Thread)
+            if (Thread.Owner != CurrentProcess)
             {
-                Logger.PrintWarning(LogClass.KernelSvc, $"Thread handle 0x{Handle:x8} is current thread!");
+                Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread, it belongs to another process.");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
+
+                return;
+            }
+
+            if (CurrentThread == Thread)
+            {
+                Logger.PrintWarning(LogClass.KernelSvc, "Invalid thread, current thread is not accepted.");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidThread);
 
diff --git a/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs b/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs
index 318bd290..3935df5d 100644
--- a/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs
@@ -32,6 +32,8 @@ namespace Ryujinx.HLE.HOS.Kernel
             {
                 int Handle = Memory.ReadInt32(HandlesPtr + Index * 4);
 
+                Logger.PrintDebug(LogClass.KernelSvc, $"Sync handle 0x{Handle:x8}");
+
                 KSynchronizationObject SyncObj = Process.HandleTable.GetObject<KSynchronizationObject>(Handle);
 
                 if (SyncObj == null)
@@ -116,12 +118,9 @@ namespace Ryujinx.HLE.HOS.Kernel
                 return;
             }
 
-            long Result = System.AddressArbiter.ArbitrateLock(
-                Process,
-                Memory,
-                OwnerHandle,
-                MutexAddress,
-                RequesterHandle);
+            KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+            long Result = CurrentProcess.AddressArbiter.ArbitrateLock(OwnerHandle, MutexAddress, RequesterHandle);
 
             if (Result != 0)
             {
@@ -155,7 +154,9 @@ namespace Ryujinx.HLE.HOS.Kernel
                 return;
             }
 
-            long Result = System.AddressArbiter.ArbitrateUnlock(Memory, MutexAddress);
+            KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+            long Result = CurrentProcess.AddressArbiter.ArbitrateUnlock(MutexAddress);
 
             if (Result != 0)
             {
@@ -196,8 +197,9 @@ namespace Ryujinx.HLE.HOS.Kernel
                 return;
             }
 
-            long Result = System.AddressArbiter.WaitProcessWideKeyAtomic(
-                Memory,
+            KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+            long Result = CurrentProcess.AddressArbiter.WaitProcessWideKeyAtomic(
                 MutexAddress,
                 CondVarAddress,
                 ThreadHandle,
@@ -227,7 +229,9 @@ namespace Ryujinx.HLE.HOS.Kernel
                 "Address = 0x" + Address.ToString("x16") + ", " +
                 "Count = 0x"   + Count  .ToString("x8"));
 
-            System.AddressArbiter.SignalProcessWideKey(Process, Memory, Address, Count);
+            KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+            CurrentProcess.AddressArbiter.SignalProcessWideKey(Address, Count);
 
             ThreadState.X0 = 0;
         }
@@ -263,20 +267,22 @@ namespace Ryujinx.HLE.HOS.Kernel
                 return;
             }
 
+            KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
             long Result;
 
             switch (Type)
             {
                 case ArbitrationType.WaitIfLessThan:
-                    Result = System.AddressArbiter.WaitForAddressIfLessThan(Memory, Address, Value, false, Timeout);
+                    Result = CurrentProcess.AddressArbiter.WaitForAddressIfLessThan(Address, Value, false, Timeout);
                     break;
 
                 case ArbitrationType.DecrementAndWaitIfLessThan:
-                    Result = System.AddressArbiter.WaitForAddressIfLessThan(Memory, Address, Value, true, Timeout);
+                    Result = CurrentProcess.AddressArbiter.WaitForAddressIfLessThan(Address, Value, true, Timeout);
                     break;
 
                 case ArbitrationType.WaitIfEqual:
-                    Result = System.AddressArbiter.WaitForAddressIfEqual(Memory, Address, Value, Timeout);
+                    Result = CurrentProcess.AddressArbiter.WaitForAddressIfEqual(Address, Value, Timeout);
                     break;
 
                 default:
@@ -323,20 +329,22 @@ namespace Ryujinx.HLE.HOS.Kernel
                 return;
             }
 
+            KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
             long Result;
 
             switch (Type)
             {
                 case SignalType.Signal:
-                    Result = System.AddressArbiter.Signal(Address, Count);
+                    Result = CurrentProcess.AddressArbiter.Signal(Address, Count);
                     break;
 
                 case SignalType.SignalAndIncrementIfEqual:
-                    Result = System.AddressArbiter.SignalAndIncrementIfEqual(Memory, Address, Value, Count);
+                    Result = CurrentProcess.AddressArbiter.SignalAndIncrementIfEqual(Address, Value, Count);
                     break;
 
                 case SignalType.SignalAndModifyIfEqual:
-                    Result = System.AddressArbiter.SignalAndModifyIfEqual(Memory, Address, Value, Count);
+                    Result = CurrentProcess.AddressArbiter.SignalAndModifyIfEqual(Address, Value, Count);
                     break;
 
                 default:
diff --git a/Ryujinx.HLE/HOS/Kernel/ThreadSchedState.cs b/Ryujinx.HLE/HOS/Kernel/ThreadSchedState.cs
index 603446f3..37e5908a 100644
--- a/Ryujinx.HLE/HOS/Kernel/ThreadSchedState.cs
+++ b/Ryujinx.HLE/HOS/Kernel/ThreadSchedState.cs
@@ -1,11 +1,15 @@
 namespace Ryujinx.HLE.HOS.Kernel
 {
-    enum ThreadSchedState : byte
+    enum ThreadSchedState : ushort
     {
-        LowNibbleMask   = 0xf,
-        HighNibbleMask  = 0xf0,
-        ExceptionalMask = 0x70,
-        ForcePauseFlag  = 0x20,
+        LowMask        = 0xf,
+        HighMask       = 0xfff0,
+        ForcePauseMask = 0x70,
+
+        ProcessPauseFlag      = 1 << 4,
+        ThreadPauseFlag       = 1 << 5,
+        ProcessDebugPauseFlag = 1 << 6,
+        KernelInitPauseFlag   = 1 << 8,
 
         None               = 0,
         Paused             = 1,
diff --git a/Ryujinx.HLE/HOS/Kernel/ThreadType.cs b/Ryujinx.HLE/HOS/Kernel/ThreadType.cs
new file mode 100644
index 00000000..0fe83423
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/ThreadType.cs
@@ -0,0 +1,10 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+    enum ThreadType
+    {
+        Dummy,
+        Kernel,
+        Kernel2,
+        User
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Process.cs b/Ryujinx.HLE/HOS/Process.cs
deleted file mode 100644
index 93b2d68d..00000000
--- a/Ryujinx.HLE/HOS/Process.cs
+++ /dev/null
@@ -1,528 +0,0 @@
-using ChocolArm64;
-using ChocolArm64.Events;
-using ChocolArm64.Memory;
-using ChocolArm64.State;
-using LibHac;
-using Ryujinx.Common.Logging;
-using Ryujinx.HLE.Exceptions;
-using Ryujinx.HLE.HOS.Diagnostics.Demangler;
-using Ryujinx.HLE.HOS.Kernel;
-using Ryujinx.HLE.HOS.Services.Nv;
-using Ryujinx.HLE.HOS.SystemState;
-using Ryujinx.HLE.Loaders;
-using Ryujinx.HLE.Loaders.Executables;
-using Ryujinx.HLE.Loaders.Npdm;
-using Ryujinx.HLE.Utilities;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.IO;
-using System.Text;
-
-namespace Ryujinx.HLE.HOS
-{
-    class Process : IDisposable
-    {
-        private const int TickFreq = 19_200_000;
-
-        public Switch Device { get; private set; }
-
-        public bool NeedsHbAbi { get; private set; }
-
-        public long HbAbiDataPosition { get; private set; }
-
-        public int ProcessId { get; private set; }
-
-        private Translator Translator;
-
-        public MemoryManager Memory { get; private set; }
-
-        public KMemoryManager MemoryManager { get; private set; }
-
-        private List<KTlsPageManager> TlsPages;
-
-        public Npdm MetaData { get; private set; }
-
-        public Nacp ControlData { get; set; }
-
-        public KProcessHandleTable HandleTable { get; private set; }
-
-        public AppletStateMgr AppletState { get; private set; }
-
-        private SvcHandler SvcHandler;
-
-        private ConcurrentDictionary<long, KThread> Threads;
-
-        private List<Executable> Executables;
-
-        private long ImageBase;
-
-        private bool Disposed;
-
-        public Process(Switch Device, int ProcessId, Npdm MetaData)
-        {
-            this.Device    = Device;
-            this.MetaData  = MetaData;
-            this.ProcessId = ProcessId;
-
-            Memory = new MemoryManager(Device.Memory.RamPointer);
-
-            Memory.InvalidAccess += CpuInvalidAccessHandler;
-
-            MemoryManager = new KMemoryManager(this);
-
-            TlsPages = new List<KTlsPageManager>();
-
-            int HandleTableSize = 1024;
-
-            if (MetaData != null)
-            {
-                foreach (KernelAccessControlItem Item in MetaData.ACI0.KernelAccessControl.Items)
-                {
-                    if (Item.HasHandleTableSize)
-                    {
-                        HandleTableSize = Item.HandleTableSize;
-
-                        break;
-                    }
-                }
-            }
-
-            HandleTable = new KProcessHandleTable(Device.System, HandleTableSize);
-
-            AppletState = new AppletStateMgr(Device.System);
-
-            SvcHandler = new SvcHandler(Device, this);
-
-            Threads = new ConcurrentDictionary<long, KThread>();
-
-            Executables = new List<Executable>();
-
-            ImageBase = MemoryManager.CodeRegionStart;
-        }
-
-        public void LoadProgram(IExecutable Program)
-        {
-            if (Disposed)
-            {
-                throw new ObjectDisposedException(nameof(Process));
-            }
-
-            long ImageEnd = LoadProgram(Program, ImageBase);
-
-            ImageBase = IntUtils.AlignUp(ImageEnd, KMemoryManager.PageSize);
-        }
-
-        public long LoadProgram(IExecutable Program, long ExecutableBase)
-        {
-            if (Disposed)
-            {
-                throw new ObjectDisposedException(nameof(Process));
-            }
-
-            Logger.PrintInfo(LogClass.Loader, $"Image base at 0x{ExecutableBase:x16}.");
-
-            Executable Executable = new Executable(Program, MemoryManager, Memory, ExecutableBase);
-
-            Executables.Add(Executable);
-
-            return Executable.ImageEnd;
-        }
-
-        public void RemoveProgram(long ExecutableBase)
-        {
-            foreach (Executable Executable in Executables)
-            {
-                if (Executable.ImageBase == ExecutableBase)
-                {
-                    Executables.Remove(Executable);
-                    break;
-                }
-            }
-        }
-
-        public void SetEmptyArgs()
-        {
-            //TODO: This should be part of Run.
-            ImageBase += KMemoryManager.PageSize;
-        }
-
-        public bool Run(bool NeedsHbAbi = false)
-        {
-            if (Disposed)
-            {
-                throw new ObjectDisposedException(nameof(Process));
-            }
-
-            this.NeedsHbAbi = NeedsHbAbi;
-
-            if (Executables.Count == 0)
-            {
-                return false;
-            }
-
-            long MainStackTop = MemoryManager.CodeRegionEnd - KMemoryManager.PageSize;
-
-            long MainStackSize = 1 * 1024 * 1024;
-
-            long MainStackBottom = MainStackTop - MainStackSize;
-
-            MemoryManager.HleMapCustom(
-                MainStackBottom,
-                MainStackSize,
-                MemoryState.MappedMemory,
-                MemoryPermission.ReadAndWrite);
-
-            int Handle = MakeThread(Executables[0].ImageBase, MainStackTop, 0, 44, 0);
-
-            if (Handle == -1)
-            {
-                return false;
-            }
-
-            KThread MainThread = HandleTable.GetKThread(Handle);
-
-            if (NeedsHbAbi)
-            {
-                HbAbiDataPosition = IntUtils.AlignUp(Executables[0].ImageEnd, KMemoryManager.PageSize);
-
-                const long HbAbiDataSize = KMemoryManager.PageSize;
-
-                MemoryManager.HleMapCustom(
-                    HbAbiDataPosition,
-                    HbAbiDataSize,
-                    MemoryState.MappedMemory,
-                    MemoryPermission.ReadAndWrite);
-
-                string SwitchPath = Device.FileSystem.SystemPathToSwitchPath(Executables[0].FilePath);
-
-                Homebrew.WriteHbAbiData(Memory, HbAbiDataPosition, Handle, SwitchPath);
-
-                MainThread.Context.ThreadState.X0 = (ulong)HbAbiDataPosition;
-                MainThread.Context.ThreadState.X1 = ulong.MaxValue;
-            }
-
-            MainThread.TimeUp();
-
-            return true;
-        }
-
-        private int ThreadIdCtr = 1;
-
-        public int MakeThread(
-            long EntryPoint,
-            long StackTop,
-            long ArgsPtr,
-            int  Priority,
-            int  ProcessorId)
-        {
-            if (Disposed)
-            {
-                throw new ObjectDisposedException(nameof(Process));
-            }
-
-            CpuThread CpuThread = new CpuThread(GetTranslator(), Memory, EntryPoint);
-
-            long Tpidr = GetFreeTls();
-
-            int ThreadId = ThreadIdCtr++; //(int)((Tpidr - MemoryManager.TlsIoRegionStart) / 0x200) + 1;
-
-            KThread Thread = new KThread(CpuThread, this, Device.System, ProcessorId, Priority, ThreadId);
-
-            Thread.LastPc = EntryPoint;
-
-            HandleTable.GenerateHandle(Thread, out int Handle);
-
-            CpuThread.ThreadState.CntfrqEl0 = TickFreq;
-            CpuThread.ThreadState.Tpidr     = Tpidr;
-
-            CpuThread.ThreadState.X0  = (ulong)ArgsPtr;
-            CpuThread.ThreadState.X1  = (ulong)Handle;
-            CpuThread.ThreadState.X31 = (ulong)StackTop;
-
-            CpuThread.ThreadState.Interrupt += InterruptHandler;
-            CpuThread.ThreadState.Break     += BreakHandler;
-            CpuThread.ThreadState.SvcCall   += SvcHandler.SvcCall;
-            CpuThread.ThreadState.Undefined += UndefinedHandler;
-
-            CpuThread.WorkFinished += ThreadFinished;
-
-            Threads.TryAdd(CpuThread.ThreadState.Tpidr, Thread);
-
-            return Handle;
-        }
-
-        private long GetFreeTls()
-        {
-            long Position;
-
-            lock (TlsPages)
-            {
-                for (int Index = 0; Index < TlsPages.Count; Index++)
-                {
-                    if (TlsPages[Index].TryGetFreeTlsAddr(out Position))
-                    {
-                        return Position;
-                    }
-                }
-
-                long PagePosition = MemoryManager.HleMapTlsPage();
-
-                KTlsPageManager TlsPage = new KTlsPageManager(PagePosition);
-
-                TlsPages.Add(TlsPage);
-
-                TlsPage.TryGetFreeTlsAddr(out Position);
-            }
-
-            return Position;
-        }
-
-        private void InterruptHandler(object sender, EventArgs e)
-        {
-            Device.System.Scheduler.ContextSwitch();
-        }
-
-        private void BreakHandler(object sender, InstExceptionEventArgs e)
-        {
-            PrintStackTraceForCurrentThread();
-
-            throw new GuestBrokeExecutionException();
-        }
-
-        private void UndefinedHandler(object sender, InstUndefinedEventArgs e)
-        {
-            PrintStackTraceForCurrentThread();
-
-            throw new UndefinedInstructionException(e.Position, e.RawOpCode);
-        }
-
-        public void EnableCpuTracing()
-        {
-            Translator.EnableCpuTrace = true;
-        }
-
-        public void DisableCpuTracing()
-        {
-            Translator.EnableCpuTrace = false;
-        }
-
-        private void CpuTraceHandler(object sender, CpuTraceEventArgs e)
-        {
-            Executable Exe = GetExecutable(e.Position);
-
-            if (Exe == null)
-            {
-                return;
-            }
-
-            if (!TryGetSubName(Exe, e.Position, out string SubName))
-            {
-                SubName = string.Empty;
-            }
-
-            long Offset = e.Position - Exe.ImageBase;
-
-            string ExeNameWithAddr = $"{Exe.Name}:0x{Offset:x8}";
-
-            Logger.PrintDebug(LogClass.Cpu, ExeNameWithAddr + " " + SubName);
-        }
-
-        private Translator GetTranslator()
-        {
-            if (Translator == null)
-            {
-                Translator = new Translator();
-
-                Translator.CpuTrace += CpuTraceHandler;
-            }
-
-            return Translator;
-        }
-
-        private void CpuInvalidAccessHandler(object sender, InvalidAccessEventArgs e)
-        {
-            PrintStackTraceForCurrentThread();
-        }
-
-        private void PrintStackTraceForCurrentThread()
-        {
-            foreach (KThread Thread in Threads.Values)
-            {
-                if (Thread.Context.IsCurrentThread())
-                {
-                    PrintStackTrace(Thread.Context.ThreadState);
-
-                    break;
-                }
-            }
-        }
-
-        public void PrintStackTrace(CpuThreadState ThreadState)
-        {
-            StringBuilder Trace = new StringBuilder();
-
-            Trace.AppendLine("Guest stack trace:");
-
-            void AppendTrace(long Position)
-            {
-                Executable Exe = GetExecutable(Position);
-
-                if (Exe == null)
-                {
-                    return;
-                }
-
-                if (!TryGetSubName(Exe, Position, out string SubName))
-                {
-                    SubName = $"Sub{Position:x16}";
-                }
-                else if (SubName.StartsWith("_Z"))
-                {
-                    SubName = Demangler.Parse(SubName);
-                }
-
-                long Offset = Position - Exe.ImageBase;
-
-                string ExeNameWithAddr = $"{Exe.Name}:0x{Offset:x8}";
-
-                Trace.AppendLine(" " + ExeNameWithAddr + " " + SubName);
-            }
-
-            long FramePointer = (long)ThreadState.X29;
-
-            while (FramePointer != 0)
-            {
-                AppendTrace(Memory.ReadInt64(FramePointer + 8));
-
-                FramePointer = Memory.ReadInt64(FramePointer);
-            }
-
-            Logger.PrintInfo(LogClass.Cpu, Trace.ToString());
-        }
-
-        private bool TryGetSubName(Executable Exe, long Position, out string Name)
-        {
-            Position -= Exe.ImageBase;
-
-            int Left  = 0;
-            int Right = Exe.SymbolTable.Count - 1;
-
-            while (Left <= Right)
-            {
-                int Size = Right - Left;
-
-                int Middle = Left + (Size >> 1);
-
-                ElfSym Symbol = Exe.SymbolTable[Middle];
-
-                long EndPosition = Symbol.Value + Symbol.Size;
-
-                if ((ulong)Position >= (ulong)Symbol.Value && (ulong)Position < (ulong)EndPosition)
-                {
-                    Name = Symbol.Name;
-
-                    return true;
-                }
-
-                if ((ulong)Position < (ulong)Symbol.Value)
-                {
-                    Right = Middle - 1;
-                }
-                else
-                {
-                    Left = Middle + 1;
-                }
-            }
-
-            Name = null;
-
-            return false;
-        }
-
-        private Executable GetExecutable(long Position)
-        {
-            string Name = string.Empty;
-
-            for (int Index = Executables.Count - 1; Index >= 0; Index--)
-            {
-                if ((ulong)Position >= (ulong)Executables[Index].ImageBase)
-                {
-                    return Executables[Index];
-                }
-            }
-
-            return null;
-        }
-
-        private void ThreadFinished(object sender, EventArgs e)
-        {
-            if (sender is CpuThread Thread)
-            {
-                if (Threads.TryRemove(Thread.ThreadState.Tpidr, out KThread KernelThread))
-                {
-                    Device.System.Scheduler.RemoveThread(KernelThread);
-                }
-            }
-
-            if (Threads.Count == 0)
-            {
-                Device.System.ExitProcess(ProcessId);
-            }
-        }
-
-        public KThread GetThread(long Tpidr)
-        {
-            if (!Threads.TryGetValue(Tpidr, out KThread Thread))
-            {
-                throw new InvalidOperationException();
-            }
-
-            return Thread;
-        }
-
-        private void Unload()
-        {
-            if (Disposed || Threads.Count > 0)
-            {
-                return;
-            }
-
-            Disposed = true;
-
-            HandleTable.Destroy();
-
-            INvDrvServices.UnloadProcess(this);
-
-            if (NeedsHbAbi && Executables.Count > 0 && Executables[0].FilePath.EndsWith(Homebrew.TemporaryNroSuffix))
-            {
-                File.Delete(Executables[0].FilePath);
-            }
-
-            Logger.PrintInfo(LogClass.Loader, $"Process {ProcessId} exiting...");
-        }
-
-        public void Dispose()
-        {
-            Dispose(true);
-        }
-
-        protected virtual void Dispose(bool Disposing)
-        {
-            if (Disposing)
-            {
-                if (Threads.Count > 0)
-                {
-                    foreach (KThread Thread in Threads.Values)
-                    {
-                        Device.System.Scheduler.StopThread(Thread);
-                    }
-                }
-                else
-                {
-                    Unload();
-                }
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/ProgramLoader.cs b/Ryujinx.HLE/HOS/ProgramLoader.cs
new file mode 100644
index 00000000..ddacd3fd
--- /dev/null
+++ b/Ryujinx.HLE/HOS/ProgramLoader.cs
@@ -0,0 +1,292 @@
+using ChocolArm64.Memory;
+using Ryujinx.Common;
+using Ryujinx.Common.Logging;
+using Ryujinx.HLE.HOS.Kernel;
+using Ryujinx.HLE.Loaders.Executables;
+using Ryujinx.HLE.Loaders.Npdm;
+
+namespace Ryujinx.HLE.HOS
+{
+    class ProgramLoader
+    {
+        private const bool AslrEnabled = true;
+
+        private const int ArgsHeaderSize = 8;
+        private const int ArgsDataSize   = 0x9000;
+        private const int ArgsTotalSize  = ArgsHeaderSize + ArgsDataSize;
+
+        public static bool LoadKernelInitalProcess(Horizon System, KernelInitialProcess Kip)
+        {
+            int EndOffset = Kip.DataOffset + Kip.Data.Length;
+
+            if (Kip.BssSize != 0)
+            {
+                EndOffset = Kip.BssOffset + Kip.BssSize;
+            }
+
+            int CodeSize = BitUtils.AlignUp(Kip.TextOffset + EndOffset, KMemoryManager.PageSize);
+
+            int CodePagesCount = CodeSize / KMemoryManager.PageSize;
+
+            ulong CodeBaseAddress = Kip.Addr39Bits ? 0x8000000UL : 0x200000UL;
+
+            ulong CodeAddress = CodeBaseAddress + (ulong)Kip.TextOffset;
+
+            int MmuFlags = 0;
+
+            if (AslrEnabled)
+            {
+                //TODO: Randomization.
+
+                MmuFlags |= 0x20;
+            }
+
+            if (Kip.Addr39Bits)
+            {
+                MmuFlags |= (int)AddressSpaceType.Addr39Bits << 1;
+            }
+
+            if (Kip.Is64Bits)
+            {
+                MmuFlags |= 1;
+            }
+
+            ProcessCreationInfo CreationInfo = new ProcessCreationInfo(
+                Kip.Name,
+                Kip.ProcessCategory,
+                Kip.TitleId,
+                CodeAddress,
+                CodePagesCount,
+                MmuFlags,
+                0,
+                0);
+
+            MemoryRegion MemRegion = Kip.IsService
+                ? MemoryRegion.Service
+                : MemoryRegion.Application;
+
+            KMemoryRegionManager Region = System.MemoryRegions[(int)MemRegion];
+
+            KernelResult Result = Region.AllocatePages((ulong)CodePagesCount, false, out KPageList PageList);
+
+            if (Result != KernelResult.Success)
+            {
+                Logger.PrintError(LogClass.Loader, $"Process initialization returned error \"{Result}\".");
+
+                return false;
+            }
+
+            KProcess Process = new KProcess(System);
+
+            Result = Process.InitializeKip(
+                CreationInfo,
+                Kip.Capabilities,
+                PageList,
+                System.ResourceLimit,
+                MemRegion);
+
+            if (Result != KernelResult.Success)
+            {
+                Logger.PrintError(LogClass.Loader, $"Process initialization returned error \"{Result}\".");
+
+                return false;
+            }
+
+            Result = LoadIntoMemory(Process, Kip, CodeBaseAddress);
+
+            if (Result != KernelResult.Success)
+            {
+                Logger.PrintError(LogClass.Loader, $"Process initialization returned error \"{Result}\".");
+
+                return false;
+            }
+
+            Result = Process.Start(Kip.MainThreadPriority, (ulong)Kip.MainThreadStackSize);
+
+            if (Result != KernelResult.Success)
+            {
+                Logger.PrintError(LogClass.Loader, $"Process start returned error \"{Result}\".");
+
+                return false;
+            }
+
+            System.Processes.Add(Process.Pid, Process);
+
+            return true;
+        }
+
+        public static bool LoadStaticObjects(
+            Horizon       System,
+            Npdm          MetaData,
+            IExecutable[] StaticObjects,
+            byte[]        Arguments = null)
+        {
+            ulong ArgsStart = 0;
+            int   ArgsSize  = 0;
+            ulong CodeStart = 0x8000000;
+            int   CodeSize  = 0;
+
+            ulong[] NsoBase = new ulong[StaticObjects.Length];
+
+            for (int Index = 0; Index < StaticObjects.Length; Index++)
+            {
+                IExecutable StaticObject = StaticObjects[Index];
+
+                int TextEnd = StaticObject.TextOffset + StaticObject.Text.Length;
+                int ROEnd   = StaticObject.ROOffset   + StaticObject.RO.Length;
+                int DataEnd = StaticObject.DataOffset + StaticObject.Data.Length + StaticObject.BssSize;
+
+                int NsoSize = TextEnd;
+
+                if ((uint)NsoSize < (uint)ROEnd)
+                {
+                    NsoSize = ROEnd;
+                }
+
+                if ((uint)NsoSize < (uint)DataEnd)
+                {
+                    NsoSize = DataEnd;
+                }
+
+                NsoSize = BitUtils.AlignUp(NsoSize, KMemoryManager.PageSize);
+
+                NsoBase[Index] = CodeStart + (ulong)CodeSize;
+
+                CodeSize += NsoSize;
+
+                if (Arguments != null && ArgsSize == 0)
+                {
+                    ArgsStart = (ulong)CodeSize;
+
+                    ArgsSize = BitUtils.AlignDown(Arguments.Length * 2 + ArgsTotalSize - 1, KMemoryManager.PageSize);
+
+                    CodeSize += ArgsSize;
+                }
+            }
+
+            int CodePagesCount = CodeSize / KMemoryManager.PageSize;
+
+            int PersonalMmHeapPagesCount = MetaData.PersonalMmHeapSize / KMemoryManager.PageSize;
+
+            ProcessCreationInfo CreationInfo = new ProcessCreationInfo(
+                MetaData.TitleName,
+                MetaData.ProcessCategory,
+                MetaData.ACI0.TitleId,
+                CodeStart,
+                CodePagesCount,
+                MetaData.MmuFlags,
+                0,
+                PersonalMmHeapPagesCount);
+
+            KernelResult Result;
+
+            KResourceLimit ResourceLimit = new KResourceLimit(System);
+
+            long ApplicationRgSize = (long)System.MemoryRegions[(int)MemoryRegion.Application].Size;
+
+            Result  = ResourceLimit.SetLimitValue(LimitableResource.Memory,         ApplicationRgSize);
+            Result |= ResourceLimit.SetLimitValue(LimitableResource.Thread,         608);
+            Result |= ResourceLimit.SetLimitValue(LimitableResource.Event,          700);
+            Result |= ResourceLimit.SetLimitValue(LimitableResource.TransferMemory, 128);
+            Result |= ResourceLimit.SetLimitValue(LimitableResource.Session,        894);
+
+            if (Result != KernelResult.Success)
+            {
+                Logger.PrintError(LogClass.Loader, $"Process initialization failed setting resource limit values.");
+
+                return false;
+            }
+
+            KProcess Process = new KProcess(System);
+
+            Result = Process.Initialize(
+                CreationInfo,
+                MetaData.ACI0.KernelAccessControl.Capabilities,
+                ResourceLimit,
+                MemoryRegion.Application);
+
+            if (Result != KernelResult.Success)
+            {
+                Logger.PrintError(LogClass.Loader, $"Process initialization returned error \"{Result}\".");
+
+                return false;
+            }
+
+            for (int Index = 0; Index < StaticObjects.Length; Index++)
+            {
+                Logger.PrintInfo(LogClass.Loader, $"Loading image {Index} at 0x{NsoBase[Index]:x16}...");
+
+                Result = LoadIntoMemory(Process, StaticObjects[Index], NsoBase[Index]);
+
+                if (Result != KernelResult.Success)
+                {
+                    Logger.PrintError(LogClass.Loader, $"Process initialization returned error \"{Result}\".");
+
+                    return false;
+                }
+            }
+
+            Result = Process.Start(MetaData.MainThreadPriority, (ulong)MetaData.MainThreadStackSize);
+
+            if (Result != KernelResult.Success)
+            {
+                Logger.PrintError(LogClass.Loader, $"Process start returned error \"{Result}\".");
+
+                return false;
+            }
+
+            System.Processes.Add(Process.Pid, Process);
+
+            return true;
+        }
+
+        private static KernelResult LoadIntoMemory(KProcess Process, IExecutable Image, ulong BaseAddress)
+        {
+            ulong TextStart = BaseAddress + (ulong)Image.TextOffset;
+            ulong ROStart   = BaseAddress + (ulong)Image.ROOffset;
+            ulong DataStart = BaseAddress + (ulong)Image.DataOffset;
+            ulong BssStart  = BaseAddress + (ulong)Image.BssOffset;
+
+            ulong End = DataStart + (ulong)Image.Data.Length;
+
+            if (Image.BssSize != 0)
+            {
+                End = BssStart + (ulong)Image.BssSize;
+            }
+
+            Process.CpuMemory.WriteBytes((long)TextStart, Image.Text);
+            Process.CpuMemory.WriteBytes((long)ROStart,   Image.RO);
+            Process.CpuMemory.WriteBytes((long)DataStart, Image.Data);
+
+            MemoryHelper.FillWithZeros(Process.CpuMemory, (long)BssStart, Image.BssSize);
+
+            KernelResult SetProcessMemoryPermission(ulong Address, ulong Size, MemoryPermission Permission)
+            {
+                if (Size == 0)
+                {
+                    return KernelResult.Success;
+                }
+
+                Size = BitUtils.AlignUp(Size, KMemoryManager.PageSize);
+
+                return Process.MemoryManager.SetProcessMemoryPermission(Address, Size, Permission);
+            }
+
+            KernelResult Result = SetProcessMemoryPermission(TextStart, (ulong)Image.Text.Length, MemoryPermission.ReadAndExecute);
+
+            if (Result != KernelResult.Success)
+            {
+                return Result;
+            }
+
+            Result = SetProcessMemoryPermission(ROStart, (ulong)Image.RO.Length, MemoryPermission.Read);
+
+            if (Result != KernelResult.Success)
+            {
+                return Result;
+            }
+
+            return SetProcessMemoryPermission(DataStart, End - DataStart, MemoryPermission.ReadAndWrite);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/ServiceCtx.cs b/Ryujinx.HLE/HOS/ServiceCtx.cs
index a591673e..76c426bc 100644
--- a/Ryujinx.HLE/HOS/ServiceCtx.cs
+++ b/Ryujinx.HLE/HOS/ServiceCtx.cs
@@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS
     class ServiceCtx
     {
         public Switch        Device       { get; private set; }
-        public Process       Process      { get; private set; }
+        public KProcess      Process      { get; private set; }
         public MemoryManager Memory       { get; private set; }
         public KSession      Session      { get; private set; }
         public IpcMessage    Request      { get; private set; }
@@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS
 
         public ServiceCtx(
             Switch        Device,
-            Process       Process,
+            KProcess      Process,
             MemoryManager Memory,
             KSession      Session,
             IpcMessage    Request,
diff --git a/Ryujinx.HLE/HOS/Services/Am/ICommonStateGetter.cs b/Ryujinx.HLE/HOS/Services/Am/ICommonStateGetter.cs
index 6b012689..2feaf8fc 100644
--- a/Ryujinx.HLE/HOS/Services/Am/ICommonStateGetter.cs
+++ b/Ryujinx.HLE/HOS/Services/Am/ICommonStateGetter.cs
@@ -35,7 +35,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
 
         public long GetEventHandle(ServiceCtx Context)
         {
-            KEvent Event = Context.Process.AppletState.MessageEvent;
+            KEvent Event = Context.Device.System.AppletState.MessageEvent;
 
             if (Context.Process.HandleTable.GenerateHandle(Event.ReadableEvent, out int Handle) != KernelResult.Success)
             {
@@ -49,7 +49,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
 
         public long ReceiveMessage(ServiceCtx Context)
         {
-            if (!Context.Process.AppletState.TryDequeueMessage(out MessageInfo Message))
+            if (!Context.Device.System.AppletState.TryDequeueMessage(out MessageInfo Message))
             {
                 return MakeError(ErrorModule.Am, AmErr.NoMessages);
             }
@@ -92,7 +92,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
 
         public long GetCurrentFocusState(ServiceCtx Context)
         {
-            Context.ResponseData.Write((byte)Context.Process.AppletState.FocusState);
+            Context.ResponseData.Write((byte)Context.Device.System.AppletState.FocusState);
 
             return 0;
         }
diff --git a/Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs b/Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs
index 4d595fde..f0899bd4 100644
--- a/Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs
+++ b/Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs
@@ -1,4 +1,7 @@
-using Ryujinx.HLE.HOS.Ipc;
+using ChocolArm64.Memory;
+using Ryujinx.Common;
+using Ryujinx.HLE.HOS.Ipc;
+using Ryujinx.HLE.HOS.Kernel;
 using Ryujinx.HLE.Loaders.Executables;
 using Ryujinx.HLE.Utilities;
 using System;
@@ -62,17 +65,32 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
 
     class NroInfo
     {
-        public Nro    Executable       { get; private set; }
-        public byte[] Hash             { get; private set; }
-        public long   NroAddress       { get; private set; }
-        public long   TotalSize        { get; private set; }
-        public long   NroMappedAddress { get; set; }
+        public NxRelocatableObject Executable { get; private set; }
 
-        public NroInfo(Nro Executable, byte[] Hash, long TotalSize)
+        public byte[] Hash             { get; private set; }
+        public ulong  NroAddress       { get; private set; }
+        public ulong  NroSize          { get; private set; }
+        public ulong  BssAddress       { get; private set; }
+        public ulong  BssSize          { get; private set; }
+        public ulong  TotalSize        { get; private set; }
+        public ulong  NroMappedAddress { get; set; }
+
+        public NroInfo(
+            NxRelocatableObject Executable,
+            byte[]              Hash,
+            ulong               NroAddress,
+            ulong               NroSize,
+            ulong               BssAddress,
+            ulong               BssSize,
+            ulong               TotalSize)
         {
             this.Executable = Executable;
             this.Hash       = Hash;
-            this.TotalSize = TotalSize;
+            this.NroAddress = NroAddress;
+            this.NroSize    = NroSize;
+            this.BssAddress = BssAddress;
+            this.BssSize    = BssSize;
+            this.TotalSize  = TotalSize;
         }
     }
 
@@ -174,7 +192,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
             return false;
         }
 
-        public long ParseNro(out NroInfo Res, ServiceCtx Context, long NroHeapAddress, long NroSize, long BssHeapAddress, long BssSize)
+        public long ParseNro(out NroInfo Res, ServiceCtx Context, ulong NroAddress, ulong NroSize, ulong BssAddress, ulong BssSize)
         {
             Res = null;
 
@@ -182,28 +200,28 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
             {
                 return MakeError(ErrorModule.Loader, LoaderErr.MaxNro);
             }
-            else if (NroSize == 0 || NroHeapAddress + NroSize <= NroHeapAddress || (NroSize & 0xFFF) != 0)
+            else if (NroSize == 0 || NroAddress + NroSize <= NroAddress || (NroSize & 0xFFF) != 0)
             {
                 return MakeError(ErrorModule.Loader, LoaderErr.BadSize);
             }
-            else if (BssSize != 0 && (BssHeapAddress + BssSize) <= BssHeapAddress)
+            else if (BssSize != 0 && BssAddress + BssSize <= BssAddress)
             {
                 return MakeError(ErrorModule.Loader, LoaderErr.BadSize);
             }
-            else if ((NroHeapAddress & 0xFFF) != 0)
+            else if ((NroAddress & 0xFFF) != 0)
             {
                 return MakeError(ErrorModule.Loader, LoaderErr.UnalignedAddress);
             }
 
-            uint Magic       = Context.Memory.ReadUInt32(NroHeapAddress + 0x10);
-            uint NroFileSize = Context.Memory.ReadUInt32(NroHeapAddress + 0x18);
+            uint Magic       = Context.Memory.ReadUInt32((long)NroAddress + 0x10);
+            uint NroFileSize = Context.Memory.ReadUInt32((long)NroAddress + 0x18);
 
             if (Magic != NroMagic || NroSize != NroFileSize)
             {
                 return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro);
             }
 
-            byte[] NroData = Context.Memory.ReadBytes(NroHeapAddress, NroSize);
+            byte[] NroData = Context.Memory.ReadBytes((long)NroAddress, (long)NroSize);
             byte[] NroHash = null;
 
             MemoryStream Stream = new MemoryStream(NroData);
@@ -225,67 +243,106 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
 
             Stream.Position = 0;
 
-            Nro Executable = new Nro(Stream, "memory", NroHeapAddress, BssHeapAddress);
+            NxRelocatableObject Executable = new NxRelocatableObject(Stream, NroAddress, BssAddress);
 
             // check if everything is page align.
-            if ((Executable.Text.Length & 0xFFF) != 0 || (Executable.RO.Length & 0xFFF) != 0
-                || (Executable.Data.Length & 0xFFF) != 0 || (Executable.BssSize & 0xFFF) !=  0)
+            if ((Executable.Text.Length & 0xFFF) != 0 || (Executable.RO.Length & 0xFFF) != 0 ||
+                (Executable.Data.Length & 0xFFF) != 0 || (Executable.BssSize & 0xFFF)   != 0)
             {
                 return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro);
             }
 
             // check if everything is contiguous.
-            if (Executable.ROOffset != Executable.TextOffset + Executable.Text.Length
-                || Executable.DataOffset != Executable.ROOffset + Executable.RO.Length
-                || NroFileSize != Executable.DataOffset + Executable.Data.Length)
+            if (Executable.ROOffset   != Executable.TextOffset + Executable.Text.Length ||
+                Executable.DataOffset != Executable.ROOffset   + Executable.RO.Length   ||
+                NroFileSize           != Executable.DataOffset + Executable.Data.Length)
             {
                 return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro);
             }
 
             // finally check the bss size match.
-            if (Executable.BssSize != BssSize)
+            if ((ulong)Executable.BssSize != BssSize)
             {
                 return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro);
             }
 
-            Res = new NroInfo(Executable, NroHash, Executable.Text.Length + Executable.RO.Length + Executable.Data.Length + Executable.BssSize);
+            int TotalSize = Executable.Text.Length + Executable.RO.Length + Executable.Data.Length + Executable.BssSize;
+
+            Res = new NroInfo(
+                Executable,
+                NroHash,
+                NroAddress,
+                NroSize,
+                BssAddress,
+                BssSize,
+                (ulong)TotalSize);
 
             return 0;
         }
 
-        private long MapNro(ServiceCtx Context, NroInfo Info, out long NroMappedAddress)
+        private long MapNro(ServiceCtx Context, NroInfo Info, out ulong NroMappedAddress)
         {
             NroMappedAddress = 0;
-            long TargetAddress = Context.Process.MemoryManager.AddrSpaceStart;
 
-            long HeapRegionStart = Context.Process.MemoryManager.HeapRegionStart;
-            long HeapRegionEnd   = Context.Process.MemoryManager.HeapRegionEnd;
+            KMemoryManager MemMgr = Context.Process.MemoryManager;
 
-            long MapRegionStart = Context.Process.MemoryManager.MapRegionStart;
-            long MapRegionEnd   = Context.Process.MemoryManager.MapRegionEnd;
+            ulong TargetAddress = MemMgr.GetAddrSpaceBaseAddr();
 
             while (true)
             {
-                if (TargetAddress + Info.TotalSize >= Context.Process.MemoryManager.AddrSpaceEnd)
+                if (TargetAddress + Info.TotalSize >= MemMgr.AddrSpaceEnd)
                 {
                     return MakeError(ErrorModule.Loader, LoaderErr.InvalidMemoryState);
                 }
 
-                bool IsValidAddress = !(HeapRegionStart > 0 && HeapRegionStart <= TargetAddress + Info.TotalSize - 1
-                    && TargetAddress <= HeapRegionEnd - 1)
-                    && !(MapRegionStart > 0
-                    && MapRegionStart <= TargetAddress + Info.TotalSize - 1
-                    && TargetAddress <= MapRegionEnd - 1);
+                KMemoryInfo MemInfo = MemMgr.QueryMemory(TargetAddress);
 
-                if (IsValidAddress && Context.Process.MemoryManager.HleIsUnmapped(TargetAddress, Info.TotalSize))
+                if (MemInfo.State == MemoryState.Unmapped && MemInfo.Size >= Info.TotalSize)
                 {
-                    break;
+                    if (!MemMgr.InsideHeapRegion (TargetAddress, Info.TotalSize) &&
+                        !MemMgr.InsideAliasRegion(TargetAddress, Info.TotalSize))
+                    {
+                        break;
+                    }
                 }
 
-                TargetAddress += 0x1000;
+                TargetAddress += MemInfo.Size;
             }
 
-            Context.Process.LoadProgram(Info.Executable, TargetAddress);
+            KernelResult Result = MemMgr.MapProcessCodeMemory(TargetAddress, Info.NroAddress, Info.NroSize);
+
+            if (Result != KernelResult.Success)
+            {
+                return MakeError(ErrorModule.Loader, LoaderErr.InvalidMemoryState);
+            }
+
+            ulong BssTargetAddress = TargetAddress + Info.NroSize;
+
+            if (Info.BssSize != 0)
+            {
+                Result = MemMgr.MapProcessCodeMemory(BssTargetAddress, Info.BssAddress, Info.BssSize);
+
+                if (Result != KernelResult.Success)
+                {
+                    MemMgr.UnmapProcessCodeMemory(TargetAddress, Info.NroAddress, Info.NroSize);
+
+                    return MakeError(ErrorModule.Loader, LoaderErr.InvalidMemoryState);
+                }
+            }
+
+            Result = LoadNroIntoMemory(Context.Process, Info.Executable, TargetAddress);
+
+            if (Result != KernelResult.Success)
+            {
+                MemMgr.UnmapProcessCodeMemory(TargetAddress, Info.NroAddress, Info.NroSize);
+
+                if (Info.BssSize != 0)
+                {
+                    MemMgr.UnmapProcessCodeMemory(BssTargetAddress, Info.BssAddress, Info.BssSize);
+                }
+
+                return 0;
+            }
 
             Info.NroMappedAddress = TargetAddress;
             NroMappedAddress      = TargetAddress;
@@ -293,6 +350,41 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
             return 0;
         }
 
+        private KernelResult LoadNroIntoMemory(KProcess Process, IExecutable RelocatableObject, ulong BaseAddress)
+        {
+            ulong TextStart = BaseAddress + (ulong)RelocatableObject.TextOffset;
+            ulong ROStart   = BaseAddress + (ulong)RelocatableObject.ROOffset;
+            ulong DataStart = BaseAddress + (ulong)RelocatableObject.DataOffset;
+
+            ulong BssStart = DataStart + (ulong)RelocatableObject.Data.Length;
+
+            ulong BssEnd = BitUtils.AlignUp(BssStart + (ulong)RelocatableObject.BssSize, KMemoryManager.PageSize);
+
+            Process.CpuMemory.WriteBytes((long)TextStart, RelocatableObject.Text);
+            Process.CpuMemory.WriteBytes((long)ROStart,   RelocatableObject.RO);
+            Process.CpuMemory.WriteBytes((long)DataStart, RelocatableObject.Data);
+
+            MemoryHelper.FillWithZeros(Process.CpuMemory, (long)BssStart, (int)(BssEnd - BssStart));
+
+            KernelResult Result;
+
+            Result = Process.MemoryManager.SetProcessMemoryPermission(TextStart, ROStart - TextStart, MemoryPermission.ReadAndExecute);
+
+            if (Result != KernelResult.Success)
+            {
+                return Result;
+            }
+
+            Result = Process.MemoryManager.SetProcessMemoryPermission(ROStart, DataStart - ROStart, MemoryPermission.Read);
+
+            if (Result != KernelResult.Success)
+            {
+                return Result;
+            }
+
+            return Process.MemoryManager.SetProcessMemoryPermission(DataStart, BssEnd - DataStart, MemoryPermission.ReadAndWrite);
+        }
+
         private long RemoveNrrInfo(long NrrAddress)
         {
             foreach (NrrInfo Info in NrrInfos)
@@ -308,24 +400,46 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
             return MakeError(ErrorModule.Loader, LoaderErr.BadNrrAddress);
         }
 
-        private long RemoveNroInfo(ServiceCtx Context, long NroMappedAddress, long NroHeapAddress)
+        private long RemoveNroInfo(ServiceCtx Context, ulong NroMappedAddress)
         {
             foreach (NroInfo Info in NroInfos)
             {
-                if (Info.NroMappedAddress == NroMappedAddress && Info.Executable.SourceAddress == NroHeapAddress)
+                if (Info.NroMappedAddress == NroMappedAddress)
                 {
                     NroInfos.Remove(Info);
 
-                    Context.Process.RemoveProgram(Info.NroMappedAddress);
+                    ulong TextSize = (ulong)Info.Executable.Text.Length;
+                    ulong ROSize   = (ulong)Info.Executable.RO.Length;
+                    ulong DataSize = (ulong)Info.Executable.Data.Length;
+                    ulong BssSize  = (ulong)Info.Executable.BssSize;
 
-                    long Result = Context.Process.MemoryManager.UnmapProcessCodeMemory(Info.NroMappedAddress, Info.Executable.SourceAddress, Info.TotalSize - Info.Executable.BssSize);
+                    KernelResult Result = KernelResult.Success;
 
-                    if (Result == 0 && Info.Executable.BssSize != 0)
+                    if (Info.Executable.BssSize != 0)
                     {
-                        Result = Context.Process.MemoryManager.UnmapProcessCodeMemory(Info.NroMappedAddress + Info.TotalSize - Info.Executable.BssSize, Info.Executable.BssAddress, Info.Executable.BssSize);
+                        Result = Context.Process.MemoryManager.UnmapProcessCodeMemory(
+                            Info.NroMappedAddress + TextSize + ROSize + DataSize,
+                            Info.Executable.BssAddress,
+                            BssSize);
                     }
 
-                    return Result;
+                    if (Result == KernelResult.Success)
+                    {
+                        Result = Context.Process.MemoryManager.UnmapProcessCodeMemory(
+                            Info.NroMappedAddress         + TextSize + ROSize,
+                            Info.Executable.SourceAddress + TextSize + ROSize,
+                            DataSize);
+
+                        if (Result == KernelResult.Success)
+                        {
+                            Result = Context.Process.MemoryManager.UnmapProcessCodeMemory(
+                                Info.NroMappedAddress,
+                                Info.Executable.SourceAddress,
+                                TextSize + ROSize);
+                        }
+                    }
+
+                    return (long)Result;
                 }
             }
 
@@ -340,12 +454,12 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
             // Zero
             Context.RequestData.ReadUInt64();
 
-            long NroHeapAddress = Context.RequestData.ReadInt64();
-            long NroSize        = Context.RequestData.ReadInt64();
-            long BssHeapAddress = Context.RequestData.ReadInt64();
-            long BssSize        = Context.RequestData.ReadInt64();
+            ulong NroHeapAddress = Context.RequestData.ReadUInt64();
+            ulong NroSize        = Context.RequestData.ReadUInt64();
+            ulong BssHeapAddress = Context.RequestData.ReadUInt64();
+            ulong BssSize        = Context.RequestData.ReadUInt64();
 
-            long NroMappedAddress = 0;
+            ulong NroMappedAddress = 0;
 
             if (IsInitialized)
             {
@@ -374,17 +488,19 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
         {
             long Result = MakeError(ErrorModule.Loader, LoaderErr.BadInitialization);
 
-            long NroMappedAddress = Context.RequestData.ReadInt64();
-            long NroHeapAddress   = Context.RequestData.ReadInt64();
+            // Zero
+            Context.RequestData.ReadUInt64();
+
+            ulong NroMappedAddress = Context.RequestData.ReadUInt64();
 
             if (IsInitialized)
             {
-                if ((NroMappedAddress & 0xFFF) != 0 || (NroHeapAddress & 0xFFF) != 0)
+                if ((NroMappedAddress & 0xFFF) != 0)
                 {
                     return MakeError(ErrorModule.Loader, LoaderErr.UnalignedAddress);
                 }
 
-                Result = RemoveNroInfo(Context, NroMappedAddress, NroHeapAddress);
+                Result = RemoveNroInfo(Context, NroMappedAddress);
             }
 
             return Result;
diff --git a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs
index b8ae11ce..6786d0e2 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs
@@ -205,7 +205,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
             return ((Cmd >> 31) & 1) != 0;
         }
 
-        public static void UnloadProcess(Process Process)
+        public static void UnloadProcess(KProcess Process)
         {
             Fds.DeleteProcess(Process);
 
diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvGpuAS/NvGpuASIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvGpuAS/NvGpuASIoctl.cs
index fed41042..7fe3bbed 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/NvGpuAS/NvGpuASIoctl.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/NvGpuAS/NvGpuASIoctl.cs
@@ -1,6 +1,7 @@
 using ChocolArm64.Memory;
 using Ryujinx.Common.Logging;
 using Ryujinx.Graphics.Memory;
+using Ryujinx.HLE.HOS.Kernel;
 using Ryujinx.HLE.HOS.Services.Nv.NvMap;
 using System;
 using System.Collections.Concurrent;
@@ -13,11 +14,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS
 
         private const int FlagRemapSubRange = 0x100;
 
-        private static ConcurrentDictionary<Process, NvGpuASCtx> ASCtxs;
+        private static ConcurrentDictionary<KProcess, NvGpuASCtx> ASCtxs;
 
         static NvGpuASIoctl()
         {
-            ASCtxs = new ConcurrentDictionary<Process, NvGpuASCtx>();
+            ASCtxs = new ConcurrentDictionary<KProcess, NvGpuASCtx>();
         }
 
         public static int ProcessIoctl(ServiceCtx Context, int Cmd)
@@ -321,7 +322,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS
             return ASCtxs.GetOrAdd(Context.Process, (Key) => new NvGpuASCtx(Context));
         }
 
-        public static void UnloadProcess(Process Process)
+        public static void UnloadProcess(KProcess Process)
         {
             ASCtxs.TryRemove(Process, out _);
         }
diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs
index 39f39d45..d9f5602b 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs
@@ -1,6 +1,7 @@
 using ChocolArm64.Memory;
 using Ryujinx.Common.Logging;
 using Ryujinx.Graphics.Memory;
+using Ryujinx.HLE.HOS.Kernel;
 using Ryujinx.HLE.HOS.Services.Nv.NvGpuAS;
 using System;
 using System.Collections.Concurrent;
@@ -21,11 +22,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel
             }
         }
 
-        private static ConcurrentDictionary<Process, ChannelsPerProcess> Channels;
+        private static ConcurrentDictionary<KProcess, ChannelsPerProcess> Channels;
 
         static NvHostChannelIoctl()
         {
-            Channels = new ConcurrentDictionary<Process, ChannelsPerProcess>();
+            Channels = new ConcurrentDictionary<KProcess, ChannelsPerProcess>();
         }
 
         public static int ProcessIoctlGpu(ServiceCtx Context, int Cmd)
@@ -194,7 +195,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel
             return Cpp.Channels[Channel];
         }
 
-        public static void UnloadProcess(Process Process)
+        public static void UnloadProcess(KProcess Process)
         {
             Channels.TryRemove(Process, out _);
         }
diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs
index 6cb14741..bf92afb4 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs
@@ -1,5 +1,6 @@
 using ChocolArm64.Memory;
 using Ryujinx.Common.Logging;
+using Ryujinx.HLE.HOS.Kernel;
 using System;
 using System.Collections.Concurrent;
 using System.Text;
@@ -9,13 +10,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostCtrl
 {
     class NvHostCtrlIoctl
     {
-        private static ConcurrentDictionary<Process, NvHostCtrlUserCtx> UserCtxs;
+        private static ConcurrentDictionary<KProcess, NvHostCtrlUserCtx> UserCtxs;
 
         private static bool IsProductionMode = true;
 
         static NvHostCtrlIoctl()
         {
-            UserCtxs = new ConcurrentDictionary<Process, NvHostCtrlUserCtx>();
+            UserCtxs = new ConcurrentDictionary<KProcess, NvHostCtrlUserCtx>();
 
             if (Set.NxSettings.Settings.TryGetValue("nv!rmos_set_production_mode", out object ProductionModeSetting))
             {
@@ -390,7 +391,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostCtrl
             return UserCtxs.GetOrAdd(Context.Process, (Key) => new NvHostCtrlUserCtx());
         }
 
-        public static void UnloadProcess(Process Process)
+        public static void UnloadProcess(KProcess Process)
         {
             UserCtxs.TryRemove(Process, out _);
         }
diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvMap/NvMapIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvMap/NvMapIoctl.cs
index f5378ef7..adc523e5 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/NvMap/NvMapIoctl.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/NvMap/NvMapIoctl.cs
@@ -1,6 +1,7 @@
 using ChocolArm64.Memory;
 using Ryujinx.Common.Logging;
 using Ryujinx.Graphics.Memory;
+using Ryujinx.HLE.HOS.Kernel;
 using Ryujinx.HLE.Utilities;
 using System.Collections.Concurrent;
 
@@ -10,11 +11,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap
     {
         private const int FlagNotFreedYet = 1;
 
-        private static ConcurrentDictionary<Process, IdDictionary> Maps;
+        private static ConcurrentDictionary<KProcess, IdDictionary> Maps;
 
         static NvMapIoctl()
         {
-            Maps = new ConcurrentDictionary<Process, IdDictionary>();
+            Maps = new ConcurrentDictionary<KProcess, IdDictionary>();
         }
 
         public static int ProcessIoctl(ServiceCtx Context, int Cmd)
@@ -130,10 +131,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap
                     //When the address is zero, we need to allocate
                     //our own backing memory for the NvMap.
                     //TODO: Is this allocation inside the transfer memory?
-                    if (!Context.Device.Memory.Allocator.TryAllocate((uint)Size, out Address))
-                    {
-                        Result = NvResult.OutOfMemory;
-                    }
+                    Result = NvResult.OutOfMemory;
                 }
 
                 if (Result == NvResult.Success)
@@ -294,7 +292,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap
             return null;
         }
 
-        public static void UnloadProcess(Process Process)
+        public static void UnloadProcess(KProcess Process)
         {
             Maps.TryRemove(Process, out _);
         }
diff --git a/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs b/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs
index 64e0b4a9..facfbe60 100644
--- a/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs
+++ b/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs
@@ -275,8 +275,7 @@ namespace Ryujinx.HLE.HOS.Services.Android
 
         private long MakeReplyParcel(ServiceCtx Context, byte[] Data)
         {
-            long ReplyPos  = Context.Request.ReceiveBuff[0].Position;
-            long ReplySize = Context.Request.ReceiveBuff[0].Size;
+            (long ReplyPos, long ReplySize) = Context.Request.GetBufferType0x22();
 
             byte[] Reply = MakeParcel(Data, new byte[0]);
 
diff --git a/Ryujinx.HLE/Homebrew.npdm b/Ryujinx.HLE/Homebrew.npdm
new file mode 100644
index 00000000..81411614
Binary files /dev/null and b/Ryujinx.HLE/Homebrew.npdm differ
diff --git a/Ryujinx.HLE/Loaders/Compression/BackwardsLz.cs b/Ryujinx.HLE/Loaders/Compression/BackwardsLz.cs
new file mode 100644
index 00000000..43cc601f
--- /dev/null
+++ b/Ryujinx.HLE/Loaders/Compression/BackwardsLz.cs
@@ -0,0 +1,105 @@
+using System;
+using System.IO;
+
+namespace Ryujinx.HLE.Loaders.Compression
+{
+    static class BackwardsLz
+    {
+        private class BackwardsReader
+        {
+            private Stream BaseStream;
+
+            public BackwardsReader(Stream BaseStream)
+            {
+                this.BaseStream = BaseStream;
+            }
+
+            public byte ReadByte()
+            {
+                BaseStream.Seek(-1, SeekOrigin.Current);
+
+                byte Value = (byte)BaseStream.ReadByte();
+
+                BaseStream.Seek(-1, SeekOrigin.Current);
+
+                return Value;
+            }
+
+            public short ReadInt16()
+            {
+                return (short)((ReadByte() << 8) | (ReadByte() << 0));
+            }
+
+            public int ReadInt32()
+            {
+                return ((ReadByte() << 24) |
+                        (ReadByte() << 16) |
+                        (ReadByte() << 8)  |
+                        (ReadByte() << 0));
+            }
+        }
+
+        public static byte[] Decompress(Stream Input, int DecompressedLength)
+        {
+            long End = Input.Position;
+
+            BackwardsReader Reader = new BackwardsReader(Input);
+
+            int AdditionalDecLength = Reader.ReadInt32();
+            int StartOffset         = Reader.ReadInt32();
+            int CompressedLength    = Reader.ReadInt32();
+
+            Input.Seek(12 - StartOffset, SeekOrigin.Current);
+
+            byte[] Dec = new byte[DecompressedLength];
+
+            int DecompressedLengthUnpadded = CompressedLength + AdditionalDecLength;
+
+            int DecompressionStart = DecompressedLength - DecompressedLengthUnpadded;
+
+            int DecPos = Dec.Length;
+
+            byte Mask   = 0;
+            byte Header = 0;
+
+            while (DecPos > DecompressionStart)
+            {
+                if ((Mask >>= 1) == 0)
+                {
+                    Header = Reader.ReadByte();
+                    Mask   = 0x80;
+                }
+
+                if ((Header & Mask) == 0)
+                {
+                    Dec[--DecPos] = Reader.ReadByte();
+                }
+                else
+                {
+                    ushort Pair = (ushort)Reader.ReadInt16();
+
+                    int Length   = (Pair >> 12)   + 3;
+                    int Position = (Pair & 0xfff) + 3;
+
+                    DecPos -= Length;
+
+                    if (Length <= Position)
+                    {
+                        int SrcPos = DecPos + Position;
+
+                        Buffer.BlockCopy(Dec, SrcPos, Dec, DecPos, Length);
+                    }
+                    else
+                    {
+                        for (int Offset = 0; Offset < Length; Offset++)
+                        {
+                            Dec[DecPos + Offset] = Dec[DecPos + Position + Offset];
+                        }
+                    }
+                }
+            }
+
+            return Dec;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/Loaders/Elf/ElfDynamic.cs b/Ryujinx.HLE/Loaders/Elf/ElfDynamic.cs
new file mode 100644
index 00000000..fb0ea53e
--- /dev/null
+++ b/Ryujinx.HLE/Loaders/Elf/ElfDynamic.cs
@@ -0,0 +1,15 @@
+namespace Ryujinx.HLE.Loaders.Elf
+{
+    struct ElfDynamic
+    {
+        public ElfDynamicTag Tag { get; private set; }
+
+        public long Value { get; private set; }
+
+        public ElfDynamic(ElfDynamicTag Tag, long Value)
+        {
+            this.Tag   = Tag;
+            this.Value = Value;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/Loaders/ElfDynTag.cs b/Ryujinx.HLE/Loaders/Elf/ElfDynamicTag.cs
similarity index 97%
rename from Ryujinx.HLE/Loaders/ElfDynTag.cs
rename to Ryujinx.HLE/Loaders/Elf/ElfDynamicTag.cs
index 5915d4d1..9d7ad72e 100644
--- a/Ryujinx.HLE/Loaders/ElfDynTag.cs
+++ b/Ryujinx.HLE/Loaders/Elf/ElfDynamicTag.cs
@@ -1,6 +1,6 @@
-namespace Ryujinx.HLE.Loaders
+namespace Ryujinx.HLE.Loaders.Elf
 {
-    enum ElfDynTag
+    enum ElfDynamicTag
     {
         DT_NULL            = 0,
         DT_NEEDED          = 1,
diff --git a/Ryujinx.HLE/Loaders/Elf/ElfSymbol.cs b/Ryujinx.HLE/Loaders/Elf/ElfSymbol.cs
new file mode 100644
index 00000000..3f3a2a79
--- /dev/null
+++ b/Ryujinx.HLE/Loaders/Elf/ElfSymbol.cs
@@ -0,0 +1,40 @@
+namespace Ryujinx.HLE.Loaders.Elf
+{
+    struct ElfSymbol
+    {
+        public string Name { get; private set; }
+
+        public ElfSymbolType       Type       { get; private set; }
+        public ElfSymbolBinding    Binding    { get; private set; }
+        public ElfSymbolVisibility Visibility { get; private set; }
+
+        public bool IsFuncOrObject =>
+            Type == ElfSymbolType.STT_FUNC ||
+            Type == ElfSymbolType.STT_OBJECT;
+
+        public bool IsGlobalOrWeak =>
+            Binding == ElfSymbolBinding.STB_GLOBAL ||
+            Binding == ElfSymbolBinding.STB_WEAK;
+
+        public int  SHIdx { get; private set; }
+        public long Value { get; private set; }
+        public long Size  { get; private set; }
+
+        public ElfSymbol(
+            string Name,
+            int    Info,
+            int    Other,
+            int    SHIdx,
+            long   Value,
+            long   Size)
+        {
+            this.Name       = Name;
+            this.Type       = (ElfSymbolType)(Info & 0xf);
+            this.Binding    = (ElfSymbolBinding)(Info >> 4);
+            this.Visibility = (ElfSymbolVisibility)Other;
+            this.SHIdx      = SHIdx;
+            this.Value      = Value;
+            this.Size       = Size;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/Loaders/ElfSymBinding.cs b/Ryujinx.HLE/Loaders/Elf/ElfSymbolBinding.cs
similarity index 58%
rename from Ryujinx.HLE/Loaders/ElfSymBinding.cs
rename to Ryujinx.HLE/Loaders/Elf/ElfSymbolBinding.cs
index f1d249ec..3c915311 100644
--- a/Ryujinx.HLE/Loaders/ElfSymBinding.cs
+++ b/Ryujinx.HLE/Loaders/Elf/ElfSymbolBinding.cs
@@ -1,6 +1,6 @@
-namespace Ryujinx.HLE.Loaders
+namespace Ryujinx.HLE.Loaders.Elf
 {
-    enum ElfSymBinding
+    enum ElfSymbolBinding
     {
         STB_LOCAL  = 0,
         STB_GLOBAL = 1,
diff --git a/Ryujinx.HLE/Loaders/ElfSymType.cs b/Ryujinx.HLE/Loaders/Elf/ElfSymbolType.cs
similarity index 76%
rename from Ryujinx.HLE/Loaders/ElfSymType.cs
rename to Ryujinx.HLE/Loaders/Elf/ElfSymbolType.cs
index 478064bc..f22e6c45 100644
--- a/Ryujinx.HLE/Loaders/ElfSymType.cs
+++ b/Ryujinx.HLE/Loaders/Elf/ElfSymbolType.cs
@@ -1,6 +1,6 @@
-namespace Ryujinx.HLE.Loaders
+namespace Ryujinx.HLE.Loaders.Elf
 {
-    enum ElfSymType
+    enum ElfSymbolType
     {
         STT_NOTYPE  = 0,
         STT_OBJECT  = 1,
diff --git a/Ryujinx.HLE/Loaders/ElfSymVisibility.cs b/Ryujinx.HLE/Loaders/Elf/ElfSymbolVisibility.cs
similarity index 65%
rename from Ryujinx.HLE/Loaders/ElfSymVisibility.cs
rename to Ryujinx.HLE/Loaders/Elf/ElfSymbolVisibility.cs
index fe7243a7..4bec50a3 100644
--- a/Ryujinx.HLE/Loaders/ElfSymVisibility.cs
+++ b/Ryujinx.HLE/Loaders/Elf/ElfSymbolVisibility.cs
@@ -1,6 +1,6 @@
-namespace Ryujinx.HLE.Loaders
+namespace Ryujinx.HLE.Loaders.Elf
 {
-    enum ElfSymVisibility
+    enum ElfSymbolVisibility
     {
         STV_DEFAULT   = 0,
         STV_INTERNAL  = 1,
diff --git a/Ryujinx.HLE/Loaders/ElfDyn.cs b/Ryujinx.HLE/Loaders/ElfDyn.cs
deleted file mode 100644
index 3508e6e4..00000000
--- a/Ryujinx.HLE/Loaders/ElfDyn.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace Ryujinx.HLE.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.HLE/Loaders/ElfRel.cs b/Ryujinx.HLE/Loaders/ElfRel.cs
deleted file mode 100644
index cfc31d89..00000000
--- a/Ryujinx.HLE/Loaders/ElfRel.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-namespace Ryujinx.HLE.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.HLE/Loaders/ElfRelType.cs b/Ryujinx.HLE/Loaders/ElfRelType.cs
deleted file mode 100644
index 7da5eec3..00000000
--- a/Ryujinx.HLE/Loaders/ElfRelType.cs
+++ /dev/null
@@ -1,128 +0,0 @@
-namespace Ryujinx.HLE.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.HLE/Loaders/ElfSym.cs b/Ryujinx.HLE/Loaders/ElfSym.cs
deleted file mode 100644
index 869938d3..00000000
--- a/Ryujinx.HLE/Loaders/ElfSym.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-namespace Ryujinx.HLE.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 Value { get; private set; }
-        public long Size  { get; private set; }
-
-        public ElfSym(
-            string Name,
-            int    Info,
-            int    Other,
-            int    SHIdx,
-            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.Value      = Value;
-            this.Size       = Size;
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.HLE/Loaders/Executable.cs b/Ryujinx.HLE/Loaders/Executable.cs
deleted file mode 100644
index d4d79073..00000000
--- a/Ryujinx.HLE/Loaders/Executable.cs
+++ /dev/null
@@ -1,205 +0,0 @@
-using ChocolArm64.Memory;
-using Ryujinx.HLE.HOS;
-using Ryujinx.HLE.HOS.Kernel;
-using Ryujinx.HLE.Loaders.Executables;
-using Ryujinx.HLE.Utilities;
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.IO;
-using System.Linq;
-
-namespace Ryujinx.HLE.Loaders
-{
-    class Executable
-    {
-        private MemoryManager Memory;
-
-        private List<ElfDyn> Dynamic;
-
-        public ReadOnlyCollection<ElfSym> SymbolTable;
-
-        public string Name { get; private set; }
-
-        public string FilePath { get; private set; }
-
-        public long ImageBase { get; private set; }
-        public long ImageEnd  { get; private set; }
-
-        private KMemoryManager MemoryManager;
-
-        public Executable(IExecutable Exe, KMemoryManager MemoryManager, MemoryManager Memory, long ImageBase)
-        {
-            Dynamic = new List<ElfDyn>();
-
-            FilePath = Exe.FilePath;
-
-            if (FilePath != null)
-            {
-                Name = Path.GetFileNameWithoutExtension(FilePath.Replace(Homebrew.TemporaryNroSuffix, ""));
-            }
-
-            this.Memory        = Memory;
-            this.MemoryManager = MemoryManager;
-            this.ImageBase     = ImageBase;
-            this.ImageEnd      = ImageBase;
-
-            long TextPosition = ImageBase + (uint)Exe.TextOffset;
-            long ROPosition   = ImageBase + (uint)Exe.ROOffset;
-            long DataPosition = ImageBase + (uint)Exe.DataOffset;
-
-            long TextSize = (uint)IntUtils.AlignUp(Exe.Text.Length, KMemoryManager.PageSize);
-            long ROSize   = (uint)IntUtils.AlignUp(Exe.RO.Length, KMemoryManager.PageSize);
-            long DataSize = (uint)IntUtils.AlignUp(Exe.Data.Length, KMemoryManager.PageSize);
-            long BssSize  = (uint)IntUtils.AlignUp(Exe.BssSize, KMemoryManager.PageSize);
-
-            long DataAndBssSize = BssSize + DataSize;
-
-            ImageEnd = DataPosition + DataAndBssSize;
-
-            if (Exe.SourceAddress == 0)
-            {
-                MemoryManager.HleMapProcessCode(TextPosition, TextSize + ROSize + DataAndBssSize);
-
-                MemoryManager.SetProcessMemoryPermission(ROPosition, ROSize, MemoryPermission.Read);
-                MemoryManager.SetProcessMemoryPermission(DataPosition, DataAndBssSize, MemoryPermission.ReadAndWrite);
-
-                Memory.WriteBytes(TextPosition, Exe.Text);
-                Memory.WriteBytes(ROPosition, Exe.RO);
-                Memory.WriteBytes(DataPosition, Exe.Data);
-            }
-            else
-            {
-                long Result = MemoryManager.MapProcessCodeMemory(TextPosition, Exe.SourceAddress, TextSize + ROSize + DataSize);
-
-                if (Result != 0)
-                {
-                    throw new InvalidOperationException();
-                }
-
-                MemoryManager.SetProcessMemoryPermission(ROPosition, ROSize, MemoryPermission.Read);
-                MemoryManager.SetProcessMemoryPermission(DataPosition, DataSize, MemoryPermission.ReadAndWrite);
-
-                if (Exe.BssAddress != 0 && Exe.BssSize != 0)
-                {
-                    Result = MemoryManager.MapProcessCodeMemory(DataPosition + DataSize, Exe.BssAddress, BssSize);
-
-                    if (Result != 0)
-                    {
-                        throw new InvalidOperationException();
-                    }
-
-                    MemoryManager.SetProcessMemoryPermission(DataPosition + DataSize, BssSize, MemoryPermission.ReadAndWrite);
-                }
-            }
-
-            if (Exe.Mod0Offset == 0)
-            {
-                return;
-            }
-
-            long Mod0Offset = ImageBase + Exe.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;
-
-            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));
-            }
-
-            long StrTblAddr = ImageBase + GetFirstValue(ElfDynTag.DT_STRTAB);
-            long SymTblAddr = ImageBase + GetFirstValue(ElfDynTag.DT_SYMTAB);
-
-            long SymEntSize = GetFirstValue(ElfDynTag.DT_SYMENT);
-
-            List<ElfSym> Symbols = new List<ElfSym>();
-
-            while ((ulong)SymTblAddr < (ulong)StrTblAddr)
-            {
-                ElfSym Sym = GetSymbol(SymTblAddr, StrTblAddr);
-
-                Symbols.Add(Sym);
-
-                SymTblAddr += SymEntSize;
-            }
-
-            SymbolTable = Array.AsReadOnly(Symbols.OrderBy(x => x.Value).ToArray());
-        }
-
-        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, 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.HLE/Loaders/Executables/IExecutable.cs b/Ryujinx.HLE/Loaders/Executables/IExecutable.cs
index 6f0952ab..d3eefde6 100644
--- a/Ryujinx.HLE/Loaders/Executables/IExecutable.cs
+++ b/Ryujinx.HLE/Loaders/Executables/IExecutable.cs
@@ -1,20 +1,15 @@
 namespace Ryujinx.HLE.Loaders.Executables
 {
-    public interface IExecutable
+    interface IExecutable
     {
-        string FilePath { get; }
-
         byte[] Text { get; }
         byte[] RO   { get; }
         byte[] Data { get; }
 
-        long SourceAddress { get; }
-        long BssAddress    { get; }
-
-        int Mod0Offset { get; }
         int TextOffset { get; }
         int ROOffset   { get; }
         int DataOffset { get; }
+        int BssOffset  { get; }
         int BssSize    { get; }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/Loaders/Executables/KernelInitialProcess.cs b/Ryujinx.HLE/Loaders/Executables/KernelInitialProcess.cs
new file mode 100644
index 00000000..1395d56f
--- /dev/null
+++ b/Ryujinx.HLE/Loaders/Executables/KernelInitialProcess.cs
@@ -0,0 +1,149 @@
+using Ryujinx.HLE.Loaders.Compression;
+using System.IO;
+
+namespace Ryujinx.HLE.Loaders.Executables
+{
+    class KernelInitialProcess : IExecutable
+    {
+        public string Name { get; private set; }
+
+        public long TitleId { get; private set; }
+
+        public int ProcessCategory { get; private set; }
+
+        public byte MainThreadPriority { get; private set; }
+        public byte DefaultProcessorId { get; private set; }
+
+        public bool Is64Bits   { get; private set; }
+        public bool Addr39Bits { get; private set; }
+        public bool IsService  { get; private set; }
+
+        public byte[] Text { get; private set; }
+        public byte[] RO   { get; private set; }
+        public byte[] Data { get; private set; }
+
+        public int TextOffset { get; private set; }
+        public int ROOffset   { get; private set; }
+        public int DataOffset { get; private set; }
+        public int BssOffset  { get; private set; }
+        public int BssSize    { get; private set; }
+
+        public int MainThreadStackSize { get; private set; }
+
+        public int[] Capabilities { get; private set; }
+
+        private struct SegmentHeader
+        {
+            public int Offset           { get; private set; }
+            public int DecompressedSize { get; private set; }
+            public int CompressedSize   { get; private set; }
+            public int Attribute        { get; private set; }
+
+            public SegmentHeader(
+                int Offset,
+                int DecompressedSize,
+                int CompressedSize,
+                int Attribute)
+            {
+                this.Offset           = Offset;
+                this.DecompressedSize = DecompressedSize;
+                this.CompressedSize   = CompressedSize;
+                this.Attribute        = Attribute;
+            }
+        }
+
+        public KernelInitialProcess(Stream Input)
+        {
+            BinaryReader Reader = new BinaryReader(Input);
+
+            string Magic = ReadString(Reader, 4);
+
+            if (Magic != "KIP1")
+            {
+
+            }
+
+            Name = ReadString(Reader, 12);
+
+            TitleId = Reader.ReadInt64();
+
+            ProcessCategory = Reader.ReadInt32();
+
+            MainThreadPriority = Reader.ReadByte();
+            DefaultProcessorId = Reader.ReadByte();
+
+            byte Reserved = Reader.ReadByte();
+            byte Flags    = Reader.ReadByte();
+
+            Is64Bits   = (Flags & 0x08) != 0;
+            Addr39Bits = (Flags & 0x10) != 0;
+            IsService  = (Flags & 0x20) != 0;
+
+            SegmentHeader[] Segments = new SegmentHeader[6];
+
+            for (int Index = 0; Index < Segments.Length; Index++)
+            {
+                Segments[Index] = new SegmentHeader(
+                    Reader.ReadInt32(),
+                    Reader.ReadInt32(),
+                    Reader.ReadInt32(),
+                    Reader.ReadInt32());
+            }
+
+            TextOffset = Segments[0].Offset;
+            ROOffset   = Segments[1].Offset;
+            DataOffset = Segments[2].Offset;
+            BssOffset  = Segments[3].Offset;
+            BssSize    = Segments[3].DecompressedSize;
+
+            MainThreadStackSize = Segments[1].Attribute;
+
+            Capabilities = new int[8];
+
+            for (int Index = 0; Index < Capabilities.Length; Index++)
+            {
+                Capabilities[Index] = Reader.ReadInt32();
+            }
+
+            Input.Seek(0x100, SeekOrigin.Begin);
+
+            Text = ReadSegment(Segments[0], Input);
+            RO   = ReadSegment(Segments[1], Input);
+            Data = ReadSegment(Segments[2], Input);
+        }
+
+        private byte[] ReadSegment(SegmentHeader Header, Stream Input)
+        {
+            long End = Input.Position + Header.CompressedSize;
+
+            Input.Seek(End, SeekOrigin.Begin);
+
+            byte[] Data = BackwardsLz.Decompress(Input, Header.DecompressedSize);
+
+            Input.Seek(End, SeekOrigin.Begin);
+
+            return Data;
+        }
+
+        private static string ReadString(BinaryReader Reader, int MaxSize)
+        {
+            string Value = string.Empty;
+
+            for (int Index = 0; Index < MaxSize; Index++)
+            {
+                char Chr = (char)Reader.ReadByte();
+
+                if (Chr == '\0')
+                {
+                    Reader.BaseStream.Seek(MaxSize - Index - 1, SeekOrigin.Current);
+
+                    break;
+                }
+
+                Value += Chr;
+            }
+
+            return Value;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/Loaders/Executables/Nro.cs b/Ryujinx.HLE/Loaders/Executables/NxRelocatableObject.cs
similarity index 85%
rename from Ryujinx.HLE/Loaders/Executables/Nro.cs
rename to Ryujinx.HLE/Loaders/Executables/NxRelocatableObject.cs
index 6015da21..20de5b5d 100644
--- a/Ryujinx.HLE/Loaders/Executables/Nro.cs
+++ b/Ryujinx.HLE/Loaders/Executables/NxRelocatableObject.cs
@@ -2,10 +2,8 @@ using System.IO;
 
 namespace Ryujinx.HLE.Loaders.Executables
 {
-    class Nro : IExecutable
+    class NxRelocatableObject : IExecutable
     {
-        public string FilePath { get; private set; }
-
         public byte[] Text { get; private set; }
         public byte[] RO   { get; private set; }
         public byte[] Data { get; private set; }
@@ -16,12 +14,13 @@ namespace Ryujinx.HLE.Loaders.Executables
         public int DataOffset { get; private set; }
         public int BssSize    { get; private set; }
 
-        public long SourceAddress { get; private set; }
-        public long BssAddress    { get; private set; }
+        public int BssOffset => DataOffset + Data.Length;
 
-        public Nro(Stream Input, string FilePath, long SourceAddress = 0, long BssAddress = 0)
+        public ulong SourceAddress { get; private set; }
+        public ulong BssAddress    { get; private set; }
+
+        public NxRelocatableObject(Stream Input, ulong SourceAddress = 0, ulong BssAddress = 0)
         {
-            this.FilePath      = FilePath;
             this.SourceAddress = SourceAddress;
             this.BssAddress    = BssAddress;
 
diff --git a/Ryujinx.HLE/Loaders/Executables/Nso.cs b/Ryujinx.HLE/Loaders/Executables/NxStaticObject.cs
similarity index 78%
rename from Ryujinx.HLE/Loaders/Executables/Nso.cs
rename to Ryujinx.HLE/Loaders/Executables/NxStaticObject.cs
index c7b48a5f..9fecb650 100644
--- a/Ryujinx.HLE/Loaders/Executables/Nso.cs
+++ b/Ryujinx.HLE/Loaders/Executables/NxStaticObject.cs
@@ -4,22 +4,18 @@ using System.IO;
 
 namespace Ryujinx.HLE.Loaders.Executables
 {
-    class Nso : IExecutable
+    class NxStaticObject : IExecutable
     {
-        public string FilePath { get; private set; }
-
         public byte[] Text { get; private set; }
         public byte[] RO   { get; private set; }
         public byte[] Data { get; private set; }
 
-        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 long SourceAddress { get; private set; }
-        public long BssAddress    { get; private set; }
+        public int BssOffset => DataOffset + Data.Length;
 
         [Flags]
         private enum NsoFlags
@@ -32,13 +28,8 @@ namespace Ryujinx.HLE.Loaders.Executables
             HasDataHash      = 1 << 5
         }
 
-        public Nso(Stream Input, string FilePath)
+        public NxStaticObject(Stream Input)
         {
-            this.FilePath = FilePath;
-
-            SourceAddress = 0;
-            BssAddress    = 0;
-
             BinaryReader Reader = new BinaryReader(Input);
 
             Input.Seek(0, SeekOrigin.Begin);
@@ -89,7 +80,7 @@ namespace Ryujinx.HLE.Loaders.Executables
 
             Text = Reader.ReadBytes(TextSize);
 
-            if (Flags.HasFlag(NsoFlags.IsTextCompressed) || true)
+            if (Flags.HasFlag(NsoFlags.IsTextCompressed))
             {
                 Text = Lz4.Decompress(Text, TextDecSize);
             }
@@ -99,7 +90,7 @@ namespace Ryujinx.HLE.Loaders.Executables
 
             RO = Reader.ReadBytes(ROSize);
 
-            if (Flags.HasFlag(NsoFlags.IsROCompressed) || true)
+            if (Flags.HasFlag(NsoFlags.IsROCompressed))
             {
                 RO = Lz4.Decompress(RO, RODecSize);
             }
@@ -109,19 +100,10 @@ namespace Ryujinx.HLE.Loaders.Executables
 
             Data = Reader.ReadBytes(DataSize);
 
-            if (Flags.HasFlag(NsoFlags.IsDataCompressed) || true)
+            if (Flags.HasFlag(NsoFlags.IsDataCompressed))
             {
                 Data = Lz4.Decompress(Data, DataDecSize);
             }
-
-            using (MemoryStream TextMS = new MemoryStream(Text))
-            {
-                BinaryReader TextReader = new BinaryReader(TextMS);
-
-                TextMS.Seek(4, SeekOrigin.Begin);
-
-                Mod0Offset = TextReader.ReadInt32();
-            }
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/Loaders/Npdm/ApplicationType.cs b/Ryujinx.HLE/Loaders/Npdm/ApplicationType.cs
deleted file mode 100644
index ad279032..00000000
--- a/Ryujinx.HLE/Loaders/Npdm/ApplicationType.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Ryujinx.HLE.Loaders.Npdm
-{
-    enum ApplicationType
-    {
-        SystemModule,
-        Application,
-        Applet
-    }
-}
diff --git a/Ryujinx.HLE/Loaders/Npdm/FsPermissionBool.cs b/Ryujinx.HLE/Loaders/Npdm/FsPermissionBool.cs
deleted file mode 100644
index 571b7b5a..00000000
--- a/Ryujinx.HLE/Loaders/Npdm/FsPermissionBool.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-namespace Ryujinx.HLE.Loaders.Npdm
-{
-    enum FsPermissionBool : ulong
-    {
-        BisCache                  = 0x8000000000000080,
-        EraseMmc                  = 0x8000000000000080,
-        GameCardCertificate       = 0x8000000000000010,
-        GameCardIdSet             = 0x8000000000000010,
-        GameCardDriver            = 0x8000000000000200,
-        GameCardAsic              = 0x8000000000000200,
-        SaveDataCreate            = 0x8000000000002020,
-        SaveDataDelete0           = 0x8000000000000060,
-        SystemSaveDataCreate0     = 0x8000000000000028,
-        SystemSaveDataCreate1     = 0x8000000000000020,
-        SaveDataDelete1           = 0x8000000000004028,
-        SaveDataIterators0        = 0x8000000000000060,
-        SaveDataIterators1        = 0x8000000000004020,
-        SaveThumbnails            = 0x8000000000020000,
-        PosixTime                 = 0x8000000000000400,
-        SaveDataExtraData         = 0x8000000000004060,
-        GlobalMode                = 0x8000000000080000,
-        SpeedEmulation            = 0x8000000000080000,
-        NULL                      = 0,
-        PaddingFiles              = 0xC000000000800000,
-        SaveData_Debug            = 0xC000000001000000,
-        SaveData_SystemManagement = 0xC000000002000000,
-        Unknown0x16               = 0x8000000004000000,
-        Unknown0x17               = 0x8000000008000000,
-        Unknown0x18               = 0x8000000010000000,
-        Unknown0x19               = 0x8000000000000800,
-        Unknown0x1A               = 0x8000000000004020
-    }
-}
diff --git a/Ryujinx.HLE/Loaders/Npdm/FsPermissionRw.cs b/Ryujinx.HLE/Loaders/Npdm/FsPermissionRw.cs
deleted file mode 100644
index ca21279b..00000000
--- a/Ryujinx.HLE/Loaders/Npdm/FsPermissionRw.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-namespace Ryujinx.HLE.Loaders.Npdm
-{
-    enum FsPermissionRw : ulong
-    {
-        MountContentType2     = 0x8000000000000801,
-        MountContentType5     = 0x8000000000000801,
-        MountContentType3     = 0x8000000000000801,
-        MountContentType4     = 0x8000000000000801,
-        MountContentType6     = 0x8000000000000801,
-        MountContentType7     = 0x8000000000000801,
-        Unknown0x6            = 0x8000000000000000,
-        ContentStorageAccess  = 0x8000000000000800,
-        ImageDirectoryAccess  = 0x8000000000001000,
-        MountBisType28        = 0x8000000000000084,
-        MountBisType29        = 0x8000000000000080,
-        MountBisType30        = 0x8000000000008080,
-        MountBisType31        = 0x8000000000008080,
-        Unknown0xD            = 0x8000000000000080,
-        SdCardAccess          = 0xC000000000200000,
-        GameCardUser          = 0x8000000000000010,
-        SaveDataAccess0       = 0x8000000000040020,
-        SystemSaveDataAccess0 = 0x8000000000000028,
-        SaveDataAccess1       = 0x8000000000000020,
-        SystemSaveDataAccess1 = 0x8000000000000020,
-        BisPartition0         = 0x8000000000010082,
-        BisPartition10        = 0x8000000000010080,
-        BisPartition20        = 0x8000000000010080,
-        BisPartition21        = 0x8000000000010080,
-        BisPartition22        = 0x8000000000010080,
-        BisPartition23        = 0x8000000000010080,
-        BisPartition24        = 0x8000000000010080,
-        BisPartition25        = 0x8000000000010080,
-        BisPartition26        = 0x8000000000000080,
-        BisPartition27        = 0x8000000000000084,
-        BisPartition28        = 0x8000000000000084,
-        BisPartition29        = 0x8000000000000080,
-        BisPartition30        = 0x8000000000000080,
-        BisPartition31        = 0x8000000000000080,
-        BisPartition32        = 0x8000000000000080,
-        Unknown0x23           = 0xC000000000200000,
-        GameCard_System       = 0x8000000000000100,
-        MountContent_System   = 0x8000000000100008,
-        HostAccess            = 0xC000000000400000
-    }
-}
diff --git a/Ryujinx.HLE/Loaders/Npdm/KernelAccessControl.cs b/Ryujinx.HLE/Loaders/Npdm/KernelAccessControl.cs
index 0b45ebfb..cd3d3252 100644
--- a/Ryujinx.HLE/Loaders/Npdm/KernelAccessControl.cs
+++ b/Ryujinx.HLE/Loaders/Npdm/KernelAccessControl.cs
@@ -1,173 +1,23 @@
-using Ryujinx.HLE.Exceptions;
-using System;
-using System.Collections.ObjectModel;
-using System.IO;
+using System.IO;
 
 namespace Ryujinx.HLE.Loaders.Npdm
 {
     class KernelAccessControl
     {
-        public ReadOnlyCollection<KernelAccessControlItem> Items;
+        public int[] Capabilities { get; private set; }
 
         public KernelAccessControl(Stream Stream, int Offset, int Size)
         {
             Stream.Seek(Offset, SeekOrigin.Begin);
 
+            Capabilities = new int[Size / 4];
+
             BinaryReader Reader = new BinaryReader(Stream);
 
-            KernelAccessControlItem[] Items = new KernelAccessControlItem[Size / 4];
-
-            for (int Index = 0; Index < Size / 4; Index++)
+            for (int Index = 0; Index < Capabilities.Length; Index++)
             {
-                uint Descriptor = Reader.ReadUInt32();
-
-                //Ignore the descriptor.
-                if (Descriptor == 0xffffffff)
-                {
-                    continue;
-                }
-
-                Items[Index] = new KernelAccessControlItem();
-
-                int LowBits = 0;
-
-                while ((Descriptor & 1) != 0)
-                {
-                    Descriptor >>= 1;
-
-                    LowBits++;
-                }
-
-                Descriptor >>= 1;
-
-                switch (LowBits)
-                {
-                    //Kernel flags.
-                    case 3:
-                    {
-                        Items[Index].HasKernelFlags = true;
-
-                        Items[Index].HighestThreadPriority = (Descriptor >> 0)  & 0x3f;
-                        Items[Index].LowestThreadPriority  = (Descriptor >> 6)  & 0x3f;
-                        Items[Index].LowestCpuId           = (Descriptor >> 12) & 0xff;
-                        Items[Index].HighestCpuId          = (Descriptor >> 20) & 0xff;
-
-                        break;
-                    }
-
-                    //Syscall mask.
-                    case 4:
-                    {
-                        Items[Index].HasSvcFlags = true;
-
-                        Items[Index].AllowedSvcs = new bool[0x80];
-
-                        int SysCallBase = (int)(Descriptor >> 24) * 0x18;
-
-                        for (int SysCall = 0; SysCall < 0x18 && SysCallBase + SysCall < 0x80; SysCall++)
-                        {
-                            Items[Index].AllowedSvcs[SysCallBase + SysCall] = (Descriptor & 1) != 0;
-
-                            Descriptor >>= 1;
-                        }
-
-                        break;
-                    }
-
-                    //Map IO/Normal.
-                    case 6:
-                    {
-                        ulong Address = (Descriptor & 0xffffff) << 12;
-                        bool  IsRo    = (Descriptor >> 24) != 0;
-
-                        if (Index == Size / 4 - 1)
-                        {
-                            throw new InvalidNpdmException("Invalid Kernel Access Control Descriptors!");
-                        }
-
-                        Descriptor = Reader.ReadUInt32();
-
-                        if ((Descriptor & 0x7f) != 0x3f)
-                        {
-                            throw new InvalidNpdmException("Invalid Kernel Access Control Descriptors!");
-                        }
-
-                        Descriptor >>= 7;
-
-                        ulong MmioSize = (Descriptor & 0xffffff) << 12;
-                        bool  IsNormal = (Descriptor >> 24) != 0;
-
-                        Items[Index].NormalMmio.Add(new KernelAccessControlMmio(Address, MmioSize, IsRo, IsNormal));
-
-                        Index++;
-
-                        break;
-                    }
-
-                    //Map Normal Page.
-                    case 7:
-                    {
-                        ulong Address = Descriptor << 12;
-
-                        Items[Index].PageMmio.Add(new KernelAccessControlMmio(Address, 0x1000, false, false));
-
-                        break;
-                    }
-
-                    //IRQ Pair.
-                    case 11:
-                    {
-                        Items[Index].Irq.Add(new KernelAccessControlIrq(
-                            (Descriptor >> 0)  & 0x3ff,
-                            (Descriptor >> 10) & 0x3ff));
-
-                        break;
-                    }
-
-                    //Application Type.
-                    case 13:
-                    {
-                        Items[Index].HasApplicationType = true;
-
-                        Items[Index].ApplicationType = (int)Descriptor & 7;
-
-                        break;
-                    }
-
-                    //Kernel Release Version.
-                    case 14:
-                    {
-                        Items[Index].HasKernelVersion = true;
-
-                        Items[Index].KernelVersionRelease = (int)Descriptor;
-
-                        break;
-                    }
-
-                    //Handle Table Size.
-                    case 15:
-                    {
-                        Items[Index].HasHandleTableSize = true;
-
-                        Items[Index].HandleTableSize = (int)Descriptor;
-
-                        break;
-                    }
-
-                    //Debug Flags.
-                    case 16:
-                    {
-                        Items[Index].HasDebugFlags = true;
-
-                        Items[Index].AllowDebug = ((Descriptor >> 0) & 1) != 0;
-                        Items[Index].ForceDebug = ((Descriptor >> 1) & 1) != 0;
-
-                        break;
-                    }
-                }
+                Capabilities[Index] = Reader.ReadInt32();
             }
-
-            this.Items = Array.AsReadOnly(Items);
         }
     }
 }
diff --git a/Ryujinx.HLE/Loaders/Npdm/KernelAccessControlIrq.cs b/Ryujinx.HLE/Loaders/Npdm/KernelAccessControlIrq.cs
deleted file mode 100644
index 63671331..00000000
--- a/Ryujinx.HLE/Loaders/Npdm/KernelAccessControlIrq.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace Ryujinx.HLE.Loaders.Npdm
-{
-    struct KernelAccessControlIrq
-    {
-        public uint Irq0 { get; private set; }
-        public uint Irq1 { get; private set; }
-
-        public KernelAccessControlIrq(uint Irq0, uint Irq1)
-        {
-            this.Irq0 = Irq0;
-            this.Irq1 = Irq1;
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.HLE/Loaders/Npdm/KernelAccessControlMmio.cs b/Ryujinx.HLE/Loaders/Npdm/KernelAccessControlMmio.cs
deleted file mode 100644
index 1ec79c88..00000000
--- a/Ryujinx.HLE/Loaders/Npdm/KernelAccessControlMmio.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-namespace Ryujinx.HLE.Loaders.Npdm
-{
-    struct KernelAccessControlMmio
-    {
-        public ulong Address  { get; private set; }
-        public ulong Size     { get; private set; }
-        public bool  IsRo     { get; private set; }
-        public bool  IsNormal { get; private set; }
-
-        public KernelAccessControlMmio(
-            ulong Address,
-            ulong Size,
-            bool  IsRo,
-            bool  IsNormal)
-        {
-            this.Address  = Address;
-            this.Size     = Size;
-            this.IsRo     = IsRo;
-            this.IsNormal = IsNormal;
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.HLE/Loaders/Npdm/KernelAccessItem.cs b/Ryujinx.HLE/Loaders/Npdm/KernelAccessItem.cs
deleted file mode 100644
index 42015c3e..00000000
--- a/Ryujinx.HLE/Loaders/Npdm/KernelAccessItem.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using System.Collections.Generic;
-
-namespace Ryujinx.HLE.Loaders.Npdm
-{
-    struct KernelAccessControlItem
-    {
-        public bool HasKernelFlags        { get; set; }
-        public uint LowestThreadPriority  { get; set; }
-        public uint HighestThreadPriority { get; set; }
-        public uint LowestCpuId           { get; set; }
-        public uint HighestCpuId          { get; set; }
-
-        public bool   HasSvcFlags { get; set; }
-        public bool[] AllowedSvcs { get; set; }
-
-        public List<KernelAccessControlMmio> NormalMmio { get; set; }
-        public List<KernelAccessControlMmio> PageMmio   { get; set; }
-        public List<KernelAccessControlIrq>  Irq        { get; set; }
-
-        public bool HasApplicationType { get; set; }
-        public int  ApplicationType    { get; set; }
-
-        public bool HasKernelVersion     { get; set; }
-        public int  KernelVersionRelease { get; set; }
-
-        public bool HasHandleTableSize { get; set; }
-        public int  HandleTableSize    { get; set; }
-
-        public bool HasDebugFlags { get; set; }
-        public bool AllowDebug    { get; set; }
-        public bool ForceDebug    { get; set; }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.HLE/Loaders/Npdm/Npdm.cs b/Ryujinx.HLE/Loaders/Npdm/Npdm.cs
index 8aacfd99..9c2fdb38 100644
--- a/Ryujinx.HLE/Loaders/Npdm/Npdm.cs
+++ b/Ryujinx.HLE/Loaders/Npdm/Npdm.cs
@@ -1,5 +1,4 @@
 using Ryujinx.HLE.Exceptions;
-using Ryujinx.HLE.Utilities;
 using System.IO;
 using System.Text;
 
@@ -12,15 +11,15 @@ namespace Ryujinx.HLE.Loaders.Npdm
     {
         private const int MetaMagic = 'M' << 0 | 'E' << 8 | 'T' << 16 | 'A' << 24;
 
-        public bool   Is64Bits                { get; private set; }
-        public int    AddressSpaceWidth       { get; private set; }
-        public byte   MainThreadPriority      { get; private set; }
-        public byte   DefaultCpuId            { get; private set; }
-        public int    SystemResourceSize      { get; private set; }
-        public int    ProcessCategory         { get; private set; }
-        public int    MainEntrypointStackSize { get; private set; }
-        public string TitleName               { get; private set; }
-        public byte[] ProductCode             { get; private set; }
+        public byte   MmuFlags            { get; private set; }
+        public bool   Is64Bits            { get; private set; }
+        public byte   MainThreadPriority  { get; private set; }
+        public byte   DefaultCpuId        { get; private set; }
+        public int    PersonalMmHeapSize  { get; private set; }
+        public int    ProcessCategory     { get; private set; }
+        public int    MainThreadStackSize { get; private set; }
+        public string TitleName           { get; private set; }
+        public byte[] ProductCode         { get; private set; }
 
         public ACI0 ACI0 { get; private set; }
         public ACID ACID { get; private set; }
@@ -36,27 +35,22 @@ namespace Ryujinx.HLE.Loaders.Npdm
 
             Reader.ReadInt64();
 
-            //MmuFlags, bit0: 64-bit instructions, bits1-3: address space width (1=64-bit, 2=32-bit). Needs to be <= 0xF.
-            byte MmuFlags = Reader.ReadByte();
+            MmuFlags = Reader.ReadByte();
 
-            Is64Bits          = (MmuFlags & 1) != 0;
-            AddressSpaceWidth = (MmuFlags >> 1) & 7;
+            Is64Bits = (MmuFlags & 1) != 0;
 
             Reader.ReadByte();
 
-            MainThreadPriority = Reader.ReadByte(); //(0-63).
+            MainThreadPriority = Reader.ReadByte();
             DefaultCpuId       = Reader.ReadByte();
 
             Reader.ReadInt32();
 
-            //System resource size (max size as of 5.x: 534773760).
-            SystemResourceSize = EndianSwap.Swap32(Reader.ReadInt32());
+            PersonalMmHeapSize = Reader.ReadInt32();
 
-            //ProcessCategory (0: regular title, 1: kernel built-in). Should be 0 here.
-            ProcessCategory = EndianSwap.Swap32(Reader.ReadInt32());
+            ProcessCategory = Reader.ReadInt32();
 
-            //Main entrypoint stack size.
-            MainEntrypointStackSize = Reader.ReadInt32();
+            MainThreadStackSize = Reader.ReadInt32();
 
             byte[] TempTitleName = Reader.ReadBytes(0x10);
 
diff --git a/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs b/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs
index 910eacb3..b18538e5 100644
--- a/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs
+++ b/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs
@@ -28,8 +28,8 @@ namespace Ryujinx.HLE.Loaders.Npdm
                     break;
                 }
 
-                int  Length          = ((ControlByte & 0x07)) + 1;
-                bool RegisterAllowed = ((ControlByte & 0x80) != 0);
+                int  Length          = (ControlByte & 0x07) + 1;
+                bool RegisterAllowed = (ControlByte & 0x80) != 0;
 
                 Services.Add(Encoding.ASCII.GetString(Reader.ReadBytes(Length), 0, Length), RegisterAllowed);
 
diff --git a/Ryujinx.HLE/Loaders/Npdm/SvcName.cs b/Ryujinx.HLE/Loaders/Npdm/SvcName.cs
deleted file mode 100644
index e519e05e..00000000
--- a/Ryujinx.HLE/Loaders/Npdm/SvcName.cs
+++ /dev/null
@@ -1,134 +0,0 @@
-namespace Ryujinx.HLE.Loaders.Npdm
-{
-    enum SvcName
-    {
-        Reserved0,
-        SetHeapSize,
-        SetMemoryPermission,
-        SetMemoryAttribute,
-        MapMemory,
-        UnmapMemory,
-        QueryMemory,
-        ExitProcess,
-        CreateThread,
-        StartThread,
-        ExitThread,
-        SleepThread,
-        GetThreadPriority,
-        SetThreadPriority,
-        GetThreadCoreMask,
-        SetThreadCoreMask,
-        GetCurrentProcessorNumber,
-        SignalEvent,
-        ClearEvent,
-        MapSharedMemory,
-        UnmapSharedMemory,
-        CreateTransferMemory,
-        CloseHandle,
-        ResetSignal,
-        WaitSynchronization,
-        CancelSynchronization,
-        ArbitrateLock,
-        ArbitrateUnlock,
-        WaitProcessWideKeyAtomic,
-        SignalProcessWideKey,
-        GetSystemTick,
-        ConnectToNamedPort,
-        SendSyncRequestLight,
-        SendSyncRequest,
-        SendSyncRequestWithUserBuffer,
-        SendAsyncRequestWithUserBuffer,
-        GetProcessId,
-        GetThreadId,
-        Break,
-        OutputDebugString,
-        ReturnFromException,
-        GetInfo,
-        FlushEntireDataCache,
-        FlushDataCache,
-        MapPhysicalMemory,
-        UnmapPhysicalMemory,
-        GetFutureThreadInfo,
-        GetLastThreadInfo,
-        GetResourceLimitLimitValue,
-        GetResourceLimitCurrentValue,
-        SetThreadActivity,
-        GetThreadContext3,
-        WaitForAddress,
-        SignalToAddress,
-        Reserved1,
-        Reserved2,
-        Reserved3,
-        Reserved4,
-        Reserved5,
-        Reserved6,
-        DumpInfo,
-        DumpInfoNew,
-        Reserved7,
-        Reserved8,
-        CreateSession,
-        AcceptSession,
-        ReplyAndReceiveLight,
-        ReplyAndReceive,
-        ReplyAndReceiveWithUserBuffer,
-        CreateEvent,
-        Reserved9,
-        Reserved10,
-        MapPhysicalMemoryUnsafe,
-        UnmapPhysicalMemoryUnsafe,
-        SetUnsafeLimit,
-        CreateCodeMemory,
-        ControlCodeMemory,
-        SleepSystem,
-        ReadWriteRegister,
-        SetProcessActivity,
-        CreateSharedMemory,
-        MapTransferMemory,
-        UnmapTransferMemory,
-        CreateInterruptEvent,
-        QueryPhysicalAddress,
-        QueryIoMapping,
-        CreateDeviceAddressSpace,
-        AttachDeviceAddressSpace,
-        DetachDeviceAddressSpace,
-        MapDeviceAddressSpaceByForce,
-        MapDeviceAddressSpaceAligned,
-        MapDeviceAddressSpace,
-        UnmapDeviceAddressSpace,
-        InvalidateProcessDataCache,
-        StoreProcessDataCache,
-        FlushProcessDataCache,
-        DebugActiveProcess,
-        BreakDebugProcess,
-        TerminateDebugProcess,
-        GetDebugEvent,
-        ContinueDebugEvent,
-        GetProcessList,
-        GetThreadList,
-        GetDebugThreadContext,
-        SetDebugThreadContext,
-        QueryDebugProcessMemory,
-        ReadDebugProcessMemory,
-        WriteDebugProcessMemory,
-        SetHardwareBreakPoint,
-        GetDebugThreadParam,
-        Reserved11,
-        GetSystemInfo,
-        CreatePort,
-        ManageNamedPort,
-        ConnectToPort,
-        SetProcessMemoryPermission,
-        MapProcessMemory,
-        UnmapProcessMemory,
-        QueryProcessMemory,
-        MapProcessCodeMemory,
-        UnmapProcessCodeMemory,
-        CreateProcess,
-        StartProcess,
-        TerminateProcess,
-        GetProcessInfo,
-        CreateResourceLimit,
-        SetResourceLimitLimitValue,
-        CallSecureMonitor
-    }
-}
diff --git a/Ryujinx.HLE/Memory/ArenaAllocator.cs b/Ryujinx.HLE/Memory/ArenaAllocator.cs
deleted file mode 100644
index 9bcb7873..00000000
--- a/Ryujinx.HLE/Memory/ArenaAllocator.cs
+++ /dev/null
@@ -1,150 +0,0 @@
-using System.Collections.Generic;
-
-namespace Ryujinx.HLE.Memory
-{
-    class ArenaAllocator
-    {
-        private class Region
-        {
-            public long Position { get; set; }
-            public long Size     { get; set; }
-
-            public Region(long Position, long Size)
-            {
-                this.Position = Position;
-                this.Size     = Size;
-            }
-        }
-
-        private LinkedList<Region> FreeRegions;
-
-        public long TotalAvailableSize { get; private set; }
-        public long TotalUsedSize      { get; private set; }
-
-        public ArenaAllocator(long ArenaSize)
-        {
-            TotalAvailableSize = ArenaSize;
-
-            FreeRegions = new LinkedList<Region>();
-
-            FreeRegions.AddFirst(new Region(0, ArenaSize));
-        }
-
-        public bool TryAllocate(long Size, out long Position)
-        {
-            LinkedListNode<Region> Node = FreeRegions.First;
-
-            while (Node != null)
-            {
-                Region Rg = Node.Value;
-
-                if ((ulong)Rg.Size >= (ulong)Size)
-                {
-                    Position = Rg.Position;
-
-                    Rg.Position += Size;
-                    Rg.Size     -= Size;
-
-                    if (Rg.Size == 0)
-                    {
-                        //Region is empty, just remove it.
-                        FreeRegions.Remove(Node);
-                    }
-                    else if (Node.Previous != null)
-                    {
-                        //Re-sort based on size (smaller first).
-                        Node = Node.Previous;
-
-                        FreeRegions.Remove(Node.Next);
-
-                        while (Node != null && (ulong)Node.Value.Size > (ulong)Rg.Size)
-                        {
-                            Node = Node.Previous;
-                        }
-
-                        if (Node != null)
-                        {
-                            FreeRegions.AddAfter(Node, Rg);
-                        }
-                        else
-                        {
-                            FreeRegions.AddFirst(Rg);
-                        }
-                    }
-
-                    TotalUsedSize += Size;
-
-                    return true;
-                }
-
-                Node = Node.Next;
-            }
-
-            Position = 0;
-
-            return false;
-        }
-
-        public void Free(long Position, long Size)
-        {
-            long End = Position + Size;
-
-            Region NewRg = new Region(Position, Size);
-
-            LinkedListNode<Region> Node   = FreeRegions.First;
-            LinkedListNode<Region> PrevSz = null;
-
-            while (Node != null)
-            {
-                LinkedListNode<Region> NextNode = Node.Next;
-
-                Region Rg = Node.Value;
-
-                long RgEnd = Rg.Position + Rg.Size;
-
-                if (Rg.Position == End)
-                {
-                    //Current region position matches the end of the freed region,
-                    //just merge the two and remove the current region from the list.
-                    NewRg.Size += Rg.Size;
-
-                    FreeRegions.Remove(Node);
-                }
-                else if (RgEnd == Position)
-                {
-                    //End of the current region matches the position of the freed region,
-                    //just merge the two and remove the current region from the list.
-                    NewRg.Position  = Rg.Position;
-                    NewRg.Size     += Rg.Size;
-
-                    FreeRegions.Remove(Node);
-                }
-                else
-                {
-                    if (PrevSz == null)
-                    {
-                        PrevSz = Node;
-                    }
-                    else if ((ulong)Rg.Size < (ulong)NewRg.Size &&
-                             (ulong)Rg.Size > (ulong)PrevSz.Value.Size)
-                    {
-                        PrevSz = Node;
-                    }
-                }
-
-                Node = NextNode;
-            }
-
-            if (PrevSz != null && (ulong)PrevSz.Value.Size < (ulong)Size)
-            {
-                FreeRegions.AddAfter(PrevSz, NewRg);
-            }
-            else
-            {
-                FreeRegions.AddFirst(NewRg);
-            }
-
-            TotalUsedSize -= Size;
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.HLE/Ryujinx.HLE.csproj b/Ryujinx.HLE/Ryujinx.HLE.csproj
index 71a0cf1c..6285825a 100644
--- a/Ryujinx.HLE/Ryujinx.HLE.csproj
+++ b/Ryujinx.HLE/Ryujinx.HLE.csproj
@@ -14,10 +14,12 @@
   </PropertyGroup>
 
   <ItemGroup>
+    <None Remove="Homebrew.npdm" />
     <None Remove="RyujinxProfileImage.jpg" />
   </ItemGroup>
 
   <ItemGroup>
+    <EmbeddedResource Include="Homebrew.npdm" />
     <EmbeddedResource Include="RyujinxProfileImage.jpg" />
   </ItemGroup>
 
diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs
index 8de49ca4..5b3b36d0 100644
--- a/Ryujinx.HLE/Switch.cs
+++ b/Ryujinx.HLE/Switch.cs
@@ -4,7 +4,6 @@ using Ryujinx.Graphics.Gal;
 using Ryujinx.HLE.FileSystem;
 using Ryujinx.HLE.HOS;
 using Ryujinx.HLE.Input;
-using Ryujinx.HLE.Memory;
 using System;
 using System.Threading;
 
@@ -56,7 +55,7 @@ namespace Ryujinx.HLE
 
             Statistics = new PerformanceStatistics();
 
-            Hid = new Hid(this, System.HidSharedMem.PA);
+            Hid = new Hid(this, System.HidBaseAddress);
 
             VsyncEvent = new AutoResetEvent(true);
         }
diff --git a/Ryujinx.HLE/Utilities/WSAError.cs b/Ryujinx.HLE/Utilities/WSAError.cs
index 55c04f22..ff0896d4 100644
--- a/Ryujinx.HLE/Utilities/WSAError.cs
+++ b/Ryujinx.HLE/Utilities/WSAError.cs
@@ -1,17 +1,13 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace Ryujinx.HLE.Utilities
+namespace Ryujinx.HLE.Utilities
 {
-    public enum WSAError
+    enum WSAError
     {
         /*
         * All Windows Sockets error constants are biased by WSABASEERR from
         * the "normal"
         */
         WSABASEERR                 = 10000,
-    
+
         /*
         * Windows Sockets definitions of regular Microsoft C error constants
         */
diff --git a/Ryujinx/Ui/ConsoleLog.cs b/Ryujinx/Ui/ConsoleLog.cs
index 1ecd4cde..67eaa0e3 100644
--- a/Ryujinx/Ui/ConsoleLog.cs
+++ b/Ryujinx/Ui/ConsoleLog.cs
@@ -26,7 +26,7 @@ namespace Ryujinx
                 { LogLevel.Error,   ConsoleColor.Red      }
             };
 
-            _messageQueue = new BlockingCollection<LogEventArgs>();
+            _messageQueue = new BlockingCollection<LogEventArgs>(10);
 
             _consoleLock = new object();
 
@@ -58,7 +58,7 @@ namespace Ryujinx
             string formattedTime = e.Time.ToString(@"hh\:mm\:ss\.fff");
 
             string currentThread = Thread.CurrentThread.ManagedThreadId.ToString("d4");
-            
+
             string message = formattedTime + " | " + currentThread + " " + e.Message;
 
             if (_logColors.TryGetValue(e.Level, out ConsoleColor color))