diff --git a/src/Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs b/src/Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs
index df3f8dc93..05fb29ac7 100644
--- a/src/Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs
+++ b/src/Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs
@@ -4,7 +4,7 @@ using System.Threading;
 
 namespace Ryujinx.Common.Memory
 {
-    public sealed partial class ByteMemoryPool
+    public partial class ByteMemoryPool
     {
         /// <summary>
         /// Represents a <see cref="IMemoryOwner{Byte}"/> that wraps an array rented from
diff --git a/src/Ryujinx.Common/Memory/ByteMemoryPool.cs b/src/Ryujinx.Common/Memory/ByteMemoryPool.cs
index 071f56b13..6fd6a98aa 100644
--- a/src/Ryujinx.Common/Memory/ByteMemoryPool.cs
+++ b/src/Ryujinx.Common/Memory/ByteMemoryPool.cs
@@ -6,24 +6,8 @@ namespace Ryujinx.Common.Memory
     /// <summary>
     /// Provides a pool of re-usable byte array instances.
     /// </summary>
-    public sealed partial class ByteMemoryPool
+    public static partial class ByteMemoryPool
     {
-        private static readonly ByteMemoryPool _shared = new();
-
-        /// <summary>
-        /// Constructs a <see cref="ByteMemoryPool"/> instance. Private to force access through
-        /// the <see cref="ByteMemoryPool.Shared"/> instance.
-        /// </summary>
-        private ByteMemoryPool()
-        {
-            // No implementation
-        }
-
-        /// <summary>
-        /// Retrieves a shared <see cref="ByteMemoryPool"/> instance.
-        /// </summary>
-        public static ByteMemoryPool Shared => _shared;
-
         /// <summary>
         /// Returns the maximum buffer size supported by this pool.
         /// </summary>
@@ -95,6 +79,20 @@ namespace Ryujinx.Common.Memory
             return buffer;
         }
 
+        /// <summary>
+        /// Copies <paramref name="buffer"/> into a newly rented byte memory buffer.
+        /// </summary>
+        /// <param name="buffer">The byte buffer to copy</param>
+        /// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory with <paramref name="buffer"/> copied to it</returns>
+        public static IMemoryOwner<byte> RentCopy(ReadOnlySpan<byte> buffer)
+        {
+            var copy = RentImpl(buffer.Length);
+
+            buffer.CopyTo(copy.Memory.Span);
+
+            return copy;
+        }
+
         private static ByteMemoryPoolBuffer RentImpl(int length)
         {
             if ((uint)length > Array.MaxLength)
diff --git a/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs b/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs
index 80f7c8a1f..0c2e5f33a 100644
--- a/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs
+++ b/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs
@@ -3,10 +3,10 @@ using Ryujinx.Memory;
 using Ryujinx.Memory.Range;
 using Ryujinx.Memory.Tracking;
 using System;
+using System.Buffers;
 using System.Collections.Generic;
 using System.Linq;
 using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
 using System.Runtime.Versioning;
 
 namespace Ryujinx.Cpu.AppleHv
@@ -15,7 +15,7 @@ namespace Ryujinx.Cpu.AppleHv
     /// Represents a CPU memory manager which maps guest virtual memory directly onto the Hypervisor page table.
     /// </summary>
     [SupportedOSPlatform("macos")]
-    public class HvMemoryManager : VirtualMemoryManagerRefCountedBase<ulong, ulong>, IMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
+    public sealed class HvMemoryManager : VirtualMemoryManagerRefCountedBase, IMemoryManager, IVirtualMemoryManagerTracked
     {
         private readonly InvalidAccessHandler _invalidAccessHandler;
 
@@ -96,12 +96,6 @@ namespace Ryujinx.Cpu.AppleHv
             }
         }
 
-        /// <inheritdoc/>
-        public void MapForeign(ulong va, nuint hostPointer, ulong size)
-        {
-            throw new NotSupportedException();
-        }
-
         /// <inheritdoc/>
         public void Unmap(ulong va, ulong size)
         {
@@ -126,20 +120,11 @@ namespace Ryujinx.Cpu.AppleHv
             }
         }
 
-        /// <inheritdoc/>
-        public T Read<T>(ulong va) where T : unmanaged
-        {
-            return MemoryMarshal.Cast<byte, T>(GetSpan(va, Unsafe.SizeOf<T>()))[0];
-        }
-
-        /// <inheritdoc/>
-        public T ReadTracked<T>(ulong va) where T : unmanaged
+        public override T ReadTracked<T>(ulong va)
         {
             try
             {
-                SignalMemoryTracking(va, (ulong)Unsafe.SizeOf<T>(), false);
-
-                return Read<T>(va);
+                return base.ReadTracked<T>(va);
             }
             catch (InvalidMemoryRegionException)
             {
@@ -152,7 +137,6 @@ namespace Ryujinx.Cpu.AppleHv
             }
         }
 
-        /// <inheritdoc/>
         public override void Read(ulong va, Span<byte> data)
         {
             try
@@ -168,101 +152,11 @@ namespace Ryujinx.Cpu.AppleHv
             }
         }
 
-        /// <inheritdoc/>
-        public void Write<T>(ulong va, T value) where T : unmanaged
-        {
-            Write(va, MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref value, 1)));
-        }
-
-        /// <inheritdoc/>
-        public void Write(ulong va, ReadOnlySpan<byte> data)
-        {
-            if (data.Length == 0)
-            {
-                return;
-            }
-
-            SignalMemoryTracking(va, (ulong)data.Length, true);
-
-            WriteImpl(va, data);
-        }
-
-        /// <inheritdoc/>
-        public void WriteUntracked(ulong va, ReadOnlySpan<byte> data)
-        {
-            if (data.Length == 0)
-            {
-                return;
-            }
-
-            WriteImpl(va, data);
-        }
-
-        /// <inheritdoc/>
-        public bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan<byte> data)
-        {
-            if (data.Length == 0)
-            {
-                return false;
-            }
-
-            SignalMemoryTracking(va, (ulong)data.Length, false);
-
-            if (IsContiguousAndMapped(va, data.Length))
-            {
-                var target = _backingMemory.GetSpan(GetPhysicalAddressInternal(va), data.Length);
-
-                bool changed = !data.SequenceEqual(target);
-
-                if (changed)
-                {
-                    data.CopyTo(target);
-                }
-
-                return changed;
-            }
-            else
-            {
-                WriteImpl(va, data);
-
-                return true;
-            }
-        }
-
-        private void WriteImpl(ulong va, ReadOnlySpan<byte> data)
+        public override void Write(ulong va, ReadOnlySpan<byte> data)
         {
             try
             {
-                AssertValidAddressAndSize(va, (ulong)data.Length);
-
-                if (IsContiguousAndMapped(va, data.Length))
-                {
-                    data.CopyTo(_backingMemory.GetSpan(GetPhysicalAddressInternal(va), data.Length));
-                }
-                else
-                {
-                    int offset = 0, size;
-
-                    if ((va & PageMask) != 0)
-                    {
-                        ulong pa = GetPhysicalAddressChecked(va);
-
-                        size = Math.Min(data.Length, PageSize - (int)(va & PageMask));
-
-                        data[..size].CopyTo(_backingMemory.GetSpan(pa, size));
-
-                        offset += size;
-                    }
-
-                    for (; offset < data.Length; offset += size)
-                    {
-                        ulong pa = GetPhysicalAddressChecked(va + (ulong)offset);
-
-                        size = Math.Min(data.Length - offset, PageSize);
-
-                        data.Slice(offset, size).CopyTo(_backingMemory.GetSpan(pa, size));
-                    }
-                }
+                base.Write(va, data);
             }
             catch (InvalidMemoryRegionException)
             {
@@ -273,61 +167,38 @@ namespace Ryujinx.Cpu.AppleHv
             }
         }
 
-        /// <inheritdoc/>
-        public ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false)
+        public override void WriteUntracked(ulong va, ReadOnlySpan<byte> data)
         {
-            if (size == 0)
+            try
             {
-                return ReadOnlySpan<byte>.Empty;
+                base.WriteUntracked(va, data);
             }
-
-            if (tracked)
+            catch (InvalidMemoryRegionException)
             {
-                SignalMemoryTracking(va, (ulong)size, false);
-            }
-
-            if (IsContiguousAndMapped(va, size))
-            {
-                return _backingMemory.GetSpan(GetPhysicalAddressInternal(va), size);
-            }
-            else
-            {
-                Span<byte> data = new byte[size];
-
-                base.Read(va, data);
-
-                return data;
+                if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
+                {
+                    throw;
+                }
             }
         }
 
-        /// <inheritdoc/>
-        public WritableRegion GetWritableRegion(ulong va, int size, bool tracked = false)
+        public override ReadOnlySequence<byte> GetReadOnlySequence(ulong va, int size, bool tracked = false)
         {
-            if (size == 0)
+            try
             {
-                return new WritableRegion(null, va, Memory<byte>.Empty);
+                return base.GetReadOnlySequence(va, size, tracked);
             }
-
-            if (tracked)
+            catch (InvalidMemoryRegionException)
             {
-                SignalMemoryTracking(va, (ulong)size, true);
-            }
+                if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
+                {
+                    throw;
+                }
 
-            if (IsContiguousAndMapped(va, size))
-            {
-                return new WritableRegion(null, va, _backingMemory.GetMemory(GetPhysicalAddressInternal(va), size));
-            }
-            else
-            {
-                Memory<byte> memory = new byte[size];
-
-                base.Read(va, memory.Span);
-
-                return new WritableRegion(this, va, memory);
+                return ReadOnlySequence<byte>.Empty;
             }
         }
 
-        /// <inheritdoc/>
         public ref T GetRef<T>(ulong va) where T : unmanaged
         {
             if (!IsContiguous(va, Unsafe.SizeOf<T>()))
@@ -340,9 +211,8 @@ namespace Ryujinx.Cpu.AppleHv
             return ref _backingMemory.GetRef<T>(GetPhysicalAddressChecked(va));
         }
 
-        /// <inheritdoc/>
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public bool IsMapped(ulong va)
+        public override bool IsMapped(ulong va)
         {
             return ValidateAddress(va) && _pages.IsMapped(va);
         }
@@ -355,39 +225,6 @@ namespace Ryujinx.Cpu.AppleHv
             return _pages.IsRangeMapped(va, size);
         }
 
-        private static void ThrowMemoryNotContiguous() => throw new MemoryNotContiguousException();
-
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private bool IsContiguousAndMapped(ulong va, int size) => IsContiguous(va, size) && IsMapped(va);
-
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private bool IsContiguous(ulong va, int size)
-        {
-            if (!ValidateAddress(va) || !ValidateAddressAndSize(va, (ulong)size))
-            {
-                return false;
-            }
-
-            int pages = GetPagesCount(va, (uint)size, out va);
-
-            for (int page = 0; page < pages - 1; page++)
-            {
-                if (!ValidateAddress(va + PageSize))
-                {
-                    return false;
-                }
-
-                if (GetPhysicalAddressInternal(va) + PageSize != GetPhysicalAddressInternal(va + PageSize))
-                {
-                    return false;
-                }
-
-                va += PageSize;
-            }
-
-            return true;
-        }
-
         /// <inheritdoc/>
         public IEnumerable<HostMemoryRange> GetHostRegions(ulong va, ulong size)
         {
@@ -464,11 +301,10 @@ namespace Ryujinx.Cpu.AppleHv
             return regions;
         }
 
-        /// <inheritdoc/>
         /// <remarks>
         /// This function also validates that the given range is both valid and mapped, and will throw if it is not.
         /// </remarks>
-        public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
+        public override void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
         {
             AssertValidAddressAndSize(va, size);
 
@@ -481,24 +317,6 @@ namespace Ryujinx.Cpu.AppleHv
             _pages.SignalMemoryTracking(Tracking, va, size, write, exemptId);
         }
 
-        /// <summary>
-        /// Computes the number of pages in a virtual address range.
-        /// </summary>
-        /// <param name="va">Virtual address of the range</param>
-        /// <param name="size">Size of the range</param>
-        /// <param name="startVa">The virtual address of the beginning of the first page</param>
-        /// <remarks>This function does not differentiate between allocated and unallocated pages.</remarks>
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private static int GetPagesCount(ulong va, ulong size, out ulong startVa)
-        {
-            // WARNING: Always check if ulong does not overflow during the operations.
-            startVa = va & ~(ulong)PageMask;
-            ulong vaSpan = (va - startVa + size + PageMask) & ~(ulong)PageMask;
-
-            return (int)(vaSpan / PageSize);
-        }
-
-        /// <inheritdoc/>
         public void Reprotect(ulong va, ulong size, MemoryPermission protection)
         {
             // TODO
@@ -535,7 +353,7 @@ namespace Ryujinx.Cpu.AppleHv
             return Tracking.BeginSmartGranularTracking(address, size, granularity, id);
         }
 
-        private ulong GetPhysicalAddressChecked(ulong va)
+        private nuint GetPhysicalAddressChecked(ulong va)
         {
             if (!IsMapped(va))
             {
@@ -545,9 +363,9 @@ namespace Ryujinx.Cpu.AppleHv
             return GetPhysicalAddressInternal(va);
         }
 
-        private ulong GetPhysicalAddressInternal(ulong va)
+        private nuint GetPhysicalAddressInternal(ulong va)
         {
-            return _pageTable.Read(va) + (va & PageMask);
+            return (nuint)(_pageTable.Read(va) + (va & PageMask));
         }
 
         /// <summary>
@@ -558,10 +376,17 @@ namespace Ryujinx.Cpu.AppleHv
             _addressSpace.Dispose();
         }
 
-        protected override Span<byte> GetPhysicalAddressSpan(ulong pa, int size)
+        protected override Memory<byte> GetPhysicalAddressMemory(nuint pa, int size)
+            => _backingMemory.GetMemory(pa, size);
+
+        protected override Span<byte> GetPhysicalAddressSpan(nuint pa, int size)
             => _backingMemory.GetSpan(pa, size);
 
-        protected override ulong TranslateVirtualAddressForRead(ulong va)
+        protected override nuint TranslateVirtualAddressChecked(ulong va)
             => GetPhysicalAddressChecked(va);
+
+        protected override nuint TranslateVirtualAddressUnchecked(ulong va)
+            => GetPhysicalAddressInternal(va);
+
     }
 }
diff --git a/src/Ryujinx.Cpu/Jit/MemoryManager.cs b/src/Ryujinx.Cpu/Jit/MemoryManager.cs
index c87c8b8cc..dfa5b9353 100644
--- a/src/Ryujinx.Cpu/Jit/MemoryManager.cs
+++ b/src/Ryujinx.Cpu/Jit/MemoryManager.cs
@@ -3,6 +3,7 @@ using Ryujinx.Memory;
 using Ryujinx.Memory.Range;
 using Ryujinx.Memory.Tracking;
 using System;
+using System.Buffers;
 using System.Collections.Generic;
 using System.Linq;
 using System.Runtime.CompilerServices;
@@ -14,7 +15,7 @@ namespace Ryujinx.Cpu.Jit
     /// <summary>
     /// Represents a CPU memory manager.
     /// </summary>
-    public sealed class MemoryManager : VirtualMemoryManagerRefCountedBase<ulong, ulong>, IMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
+    public sealed class MemoryManager : VirtualMemoryManagerRefCountedBase, IMemoryManager, IVirtualMemoryManagerTracked
     {
         private const int PteSize = 8;
 
@@ -97,12 +98,6 @@ namespace Ryujinx.Cpu.Jit
             Tracking.Map(oVa, size);
         }
 
-        /// <inheritdoc/>
-        public void MapForeign(ulong va, nuint hostPointer, ulong size)
-        {
-            throw new NotSupportedException();
-        }
-
         /// <inheritdoc/>
         public void Unmap(ulong va, ulong size)
         {
@@ -128,20 +123,11 @@ namespace Ryujinx.Cpu.Jit
             }
         }
 
-        /// <inheritdoc/>
-        public T Read<T>(ulong va) where T : unmanaged
-        {
-            return MemoryMarshal.Cast<byte, T>(GetSpan(va, Unsafe.SizeOf<T>()))[0];
-        }
-
-        /// <inheritdoc/>
-        public T ReadTracked<T>(ulong va) where T : unmanaged
+        public override T ReadTracked<T>(ulong va)
         {
             try
             {
-                SignalMemoryTracking(va, (ulong)Unsafe.SizeOf<T>(), false);
-
-                return Read<T>(va);
+                return base.ReadTracked<T>(va);
             }
             catch (InvalidMemoryRegionException)
             {
@@ -190,117 +176,11 @@ namespace Ryujinx.Cpu.Jit
             }
         }
 
-        /// <inheritdoc/>
-        public void Write<T>(ulong va, T value) where T : unmanaged
-        {
-            Write(va, MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref value, 1)));
-        }
-
-        /// <inheritdoc/>
-        public void Write(ulong va, ReadOnlySpan<byte> data)
-        {
-            if (data.Length == 0)
-            {
-                return;
-            }
-
-            SignalMemoryTracking(va, (ulong)data.Length, true);
-
-            WriteImpl(va, data);
-        }
-
-        /// <inheritdoc/>
-        public void WriteGuest<T>(ulong va, T value) where T : unmanaged
-        {
-            Span<byte> data = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref value, 1));
-
-            SignalMemoryTrackingImpl(va, (ulong)data.Length, true, true);
-
-            WriteImpl(va, data);
-        }
-
-        /// <inheritdoc/>
-        public void WriteUntracked(ulong va, ReadOnlySpan<byte> data)
-        {
-            if (data.Length == 0)
-            {
-                return;
-            }
-
-            WriteImpl(va, data);
-        }
-
-        /// <inheritdoc/>
-        public bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan<byte> data)
-        {
-            if (data.Length == 0)
-            {
-                return false;
-            }
-
-            SignalMemoryTracking(va, (ulong)data.Length, false);
-
-            if (IsContiguousAndMapped(va, data.Length))
-            {
-                var target = _backingMemory.GetSpan(GetPhysicalAddressInternal(va), data.Length);
-
-                bool changed = !data.SequenceEqual(target);
-
-                if (changed)
-                {
-                    data.CopyTo(target);
-                }
-
-                return changed;
-            }
-            else
-            {
-                WriteImpl(va, data);
-
-                return true;
-            }
-        }
-
-        /// <summary>
-        /// Writes data to CPU mapped memory.
-        /// </summary>
-        /// <param name="va">Virtual address to write the data into</param>
-        /// <param name="data">Data to be written</param>
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private void WriteImpl(ulong va, ReadOnlySpan<byte> data)
+        public override void Write(ulong va, ReadOnlySpan<byte> data)
         {
             try
             {
-                AssertValidAddressAndSize(va, (ulong)data.Length);
-
-                if (IsContiguousAndMapped(va, data.Length))
-                {
-                    data.CopyTo(_backingMemory.GetSpan(GetPhysicalAddressInternal(va), data.Length));
-                }
-                else
-                {
-                    int offset = 0, size;
-
-                    if ((va & PageMask) != 0)
-                    {
-                        ulong pa = GetPhysicalAddressInternal(va);
-
-                        size = Math.Min(data.Length, PageSize - (int)(va & PageMask));
-
-                        data[..size].CopyTo(_backingMemory.GetSpan(pa, size));
-
-                        offset += size;
-                    }
-
-                    for (; offset < data.Length; offset += size)
-                    {
-                        ulong pa = GetPhysicalAddressInternal(va + (ulong)offset);
-
-                        size = Math.Min(data.Length - offset, PageSize);
-
-                        data.Slice(offset, size).CopyTo(_backingMemory.GetSpan(pa, size));
-                    }
-                }
+                base.Write(va, data);
             }
             catch (InvalidMemoryRegionException)
             {
@@ -312,60 +192,47 @@ namespace Ryujinx.Cpu.Jit
         }
 
         /// <inheritdoc/>
-        public ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false)
+        public void WriteGuest<T>(ulong va, T value) where T : unmanaged
         {
-            if (size == 0)
+            Span<byte> data = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref value, 1));
+
+            SignalMemoryTrackingImpl(va, (ulong)data.Length, true, true);
+
+            Write(va, data);
+        }
+
+        public override void WriteUntracked(ulong va, ReadOnlySpan<byte> data)
+        {
+            try
             {
-                return ReadOnlySpan<byte>.Empty;
+                base.WriteUntracked(va, data);
             }
-
-            if (tracked)
+            catch (InvalidMemoryRegionException)
             {
-                SignalMemoryTracking(va, (ulong)size, false);
-            }
-
-            if (IsContiguousAndMapped(va, size))
-            {
-                return _backingMemory.GetSpan(GetPhysicalAddressInternal(va), size);
-            }
-            else
-            {
-                Span<byte> data = new byte[size];
-
-                base.Read(va, data);
-
-                return data;
+                if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
+                {
+                    throw;
+                }
             }
         }
 
-        /// <inheritdoc/>
-        public WritableRegion GetWritableRegion(ulong va, int size, bool tracked = false)
+        public override ReadOnlySequence<byte> GetReadOnlySequence(ulong va, int size, bool tracked = false)
         {
-            if (size == 0)
+            try
             {
-                return new WritableRegion(null, va, Memory<byte>.Empty);
+                return base.GetReadOnlySequence(va, size, tracked);
             }
-
-            if (IsContiguousAndMapped(va, size))
+            catch (InvalidMemoryRegionException)
             {
-                if (tracked)
+                if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
                 {
-                    SignalMemoryTracking(va, (ulong)size, true);
+                    throw;
                 }
 
-                return new WritableRegion(null, va, _backingMemory.GetMemory(GetPhysicalAddressInternal(va), size));
-            }
-            else
-            {
-                Memory<byte> memory = new byte[size];
-
-                GetSpan(va, size).CopyTo(memory.Span);
-
-                return new WritableRegion(this, va, memory, tracked);
+                return ReadOnlySequence<byte>.Empty;
             }
         }
 
-        /// <inheritdoc/>
         public ref T GetRef<T>(ulong va) where T : unmanaged
         {
             if (!IsContiguous(va, Unsafe.SizeOf<T>()))
@@ -378,56 +245,6 @@ namespace Ryujinx.Cpu.Jit
             return ref _backingMemory.GetRef<T>(GetPhysicalAddressInternal(va));
         }
 
-        /// <summary>
-        /// Computes the number of pages in a virtual address range.
-        /// </summary>
-        /// <param name="va">Virtual address of the range</param>
-        /// <param name="size">Size of the range</param>
-        /// <param name="startVa">The virtual address of the beginning of the first page</param>
-        /// <remarks>This function does not differentiate between allocated and unallocated pages.</remarks>
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private static int GetPagesCount(ulong va, uint size, out ulong startVa)
-        {
-            // WARNING: Always check if ulong does not overflow during the operations.
-            startVa = va & ~(ulong)PageMask;
-            ulong vaSpan = (va - startVa + size + PageMask) & ~(ulong)PageMask;
-
-            return (int)(vaSpan / PageSize);
-        }
-
-        private static void ThrowMemoryNotContiguous() => throw new MemoryNotContiguousException();
-
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private bool IsContiguousAndMapped(ulong va, int size) => IsContiguous(va, size) && IsMapped(va);
-
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private bool IsContiguous(ulong va, int size)
-        {
-            if (!ValidateAddress(va) || !ValidateAddressAndSize(va, (ulong)size))
-            {
-                return false;
-            }
-
-            int pages = GetPagesCount(va, (uint)size, out va);
-
-            for (int page = 0; page < pages - 1; page++)
-            {
-                if (!ValidateAddress(va + PageSize))
-                {
-                    return false;
-                }
-
-                if (GetPhysicalAddressInternal(va) + PageSize != GetPhysicalAddressInternal(va + PageSize))
-                {
-                    return false;
-                }
-
-                va += PageSize;
-            }
-
-            return true;
-        }
-
         /// <inheritdoc/>
         public IEnumerable<HostMemoryRange> GetHostRegions(ulong va, ulong size)
         {
@@ -532,9 +349,8 @@ namespace Ryujinx.Cpu.Jit
             return true;
         }
 
-        /// <inheritdoc/>
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public bool IsMapped(ulong va)
+        public override bool IsMapped(ulong va)
         {
             if (!ValidateAddress(va))
             {
@@ -544,9 +360,9 @@ namespace Ryujinx.Cpu.Jit
             return _pageTable.Read<ulong>((va / PageSize) * PteSize) != 0;
         }
 
-        private ulong GetPhysicalAddressInternal(ulong va)
+        private nuint GetPhysicalAddressInternal(ulong va)
         {
-            return PteToPa(_pageTable.Read<ulong>((va / PageSize) * PteSize) & ~(0xffffUL << 48)) + (va & PageMask);
+            return (nuint)(PteToPa(_pageTable.Read<ulong>((va / PageSize) * PteSize) & ~(0xffffUL << 48)) + (va & PageMask));
         }
 
         /// <inheritdoc/>
@@ -643,9 +459,7 @@ namespace Ryujinx.Cpu.Jit
                 {
                     ref long pageRef = ref _pageTable.GetRef<long>(pageStart * PteSize);
 
-                    long pte;
-
-                    pte = Volatile.Read(ref pageRef);
+                    long pte = Volatile.Read(ref pageRef);
 
                     if ((pte & tag) != 0)
                     {
@@ -663,7 +477,7 @@ namespace Ryujinx.Cpu.Jit
         }
 
         /// <inheritdoc/>
-        public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
+        public override void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
         {
             SignalMemoryTrackingImpl(va, size, write, false, precise, exemptId);
         }
@@ -683,10 +497,16 @@ namespace Ryujinx.Cpu.Jit
         /// </summary>
         protected override void Destroy() => _pageTable.Dispose();
 
-        protected override Span<byte> GetPhysicalAddressSpan(ulong pa, int size)
+        protected override Memory<byte> GetPhysicalAddressMemory(nuint pa, int size)
+            => _backingMemory.GetMemory(pa, size);
+
+        protected override Span<byte> GetPhysicalAddressSpan(nuint pa, int size)
             => _backingMemory.GetSpan(pa, size);
 
-        protected override ulong TranslateVirtualAddressForRead(ulong va)
+        protected override nuint TranslateVirtualAddressChecked(ulong va)
+            => GetPhysicalAddressInternal(va);
+
+        protected override nuint TranslateVirtualAddressUnchecked(ulong va)
             => GetPhysicalAddressInternal(va);
     }
 }
diff --git a/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs b/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs
index f410d02e9..c60ab6b24 100644
--- a/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs
+++ b/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs
@@ -3,6 +3,7 @@ using Ryujinx.Memory;
 using Ryujinx.Memory.Range;
 using Ryujinx.Memory.Tracking;
 using System;
+using System.Buffers;
 using System.Collections.Generic;
 using System.Linq;
 using System.Runtime.CompilerServices;
@@ -12,7 +13,7 @@ namespace Ryujinx.Cpu.Jit
     /// <summary>
     /// Represents a CPU memory manager which maps guest virtual memory directly onto a host virtual region.
     /// </summary>
-    public sealed class MemoryManagerHostMapped : VirtualMemoryManagerRefCountedBase<ulong, ulong>, IMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
+    public sealed class MemoryManagerHostMapped : VirtualMemoryManagerRefCountedBase, IMemoryManager, IVirtualMemoryManagerTracked
     {
         private readonly InvalidAccessHandler _invalidAccessHandler;
         private readonly bool _unsafeMode;
@@ -96,12 +97,6 @@ namespace Ryujinx.Cpu.Jit
             Tracking.Map(va, size);
         }
 
-        /// <inheritdoc/>
-        public void MapForeign(ulong va, nuint hostPointer, ulong size)
-        {
-            throw new NotSupportedException();
-        }
-
         /// <inheritdoc/>
         public void Unmap(ulong va, ulong size)
         {
@@ -138,8 +133,7 @@ namespace Ryujinx.Cpu.Jit
             }
         }
 
-        /// <inheritdoc/>
-        public T Read<T>(ulong va) where T : unmanaged
+        public override T Read<T>(ulong va)
         {
             try
             {
@@ -158,14 +152,11 @@ namespace Ryujinx.Cpu.Jit
             }
         }
 
-        /// <inheritdoc/>
-        public T ReadTracked<T>(ulong va) where T : unmanaged
+        public override T ReadTracked<T>(ulong va)
         {
             try
             {
-                SignalMemoryTracking(va, (ulong)Unsafe.SizeOf<T>(), false);
-
-                return Read<T>(va);
+                return base.ReadTracked<T>(va);
             }
             catch (InvalidMemoryRegionException)
             {
@@ -178,7 +169,6 @@ namespace Ryujinx.Cpu.Jit
             }
         }
 
-        /// <inheritdoc/>
         public override void Read(ulong va, Span<byte> data)
         {
             try
@@ -196,9 +186,7 @@ namespace Ryujinx.Cpu.Jit
             }
         }
 
-
-        /// <inheritdoc/>
-        public void Write<T>(ulong va, T value) where T : unmanaged
+        public override void Write<T>(ulong va, T value)
         {
             try
             {
@@ -215,8 +203,7 @@ namespace Ryujinx.Cpu.Jit
             }
         }
 
-        /// <inheritdoc/>
-        public void Write(ulong va, ReadOnlySpan<byte> data)
+        public override void Write(ulong va, ReadOnlySpan<byte> data)
         {
             try
             {
@@ -233,8 +220,7 @@ namespace Ryujinx.Cpu.Jit
             }
         }
 
-        /// <inheritdoc/>
-        public void WriteUntracked(ulong va, ReadOnlySpan<byte> data)
+        public override void WriteUntracked(ulong va, ReadOnlySpan<byte> data)
         {
             try
             {
@@ -251,8 +237,7 @@ namespace Ryujinx.Cpu.Jit
             }
         }
 
-        /// <inheritdoc/>
-        public bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan<byte> data)
+        public override bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan<byte> data)
         {
             try
             {
@@ -279,8 +264,21 @@ namespace Ryujinx.Cpu.Jit
             }
         }
 
-        /// <inheritdoc/>
-        public ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false)
+        public override ReadOnlySequence<byte> GetReadOnlySequence(ulong va, int size, bool tracked = false)
+        {
+            if (tracked)
+            {
+                SignalMemoryTracking(va, (ulong)size, write: false);
+            }
+            else
+            {
+                AssertMapped(va, (ulong)size);
+            }
+
+            return new ReadOnlySequence<byte>(_addressSpace.Mirror.GetMemory(va, size));
+        }
+
+        public override ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false)
         {
             if (tracked)
             {
@@ -294,8 +292,7 @@ namespace Ryujinx.Cpu.Jit
             return _addressSpace.Mirror.GetSpan(va, size);
         }
 
-        /// <inheritdoc/>
-        public WritableRegion GetWritableRegion(ulong va, int size, bool tracked = false)
+        public override WritableRegion GetWritableRegion(ulong va, int size, bool tracked = false)
         {
             if (tracked)
             {
@@ -309,7 +306,6 @@ namespace Ryujinx.Cpu.Jit
             return _addressSpace.Mirror.GetWritableRegion(va, size);
         }
 
-        /// <inheritdoc/>
         public ref T GetRef<T>(ulong va) where T : unmanaged
         {
             SignalMemoryTracking(va, (ulong)Unsafe.SizeOf<T>(), true);
@@ -317,9 +313,8 @@ namespace Ryujinx.Cpu.Jit
             return ref _addressSpace.Mirror.GetRef<T>(va);
         }
 
-        /// <inheritdoc/>
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public bool IsMapped(ulong va)
+        public override bool IsMapped(ulong va)
         {
             return ValidateAddress(va) && _pages.IsMapped(va);
         }
@@ -390,11 +385,10 @@ namespace Ryujinx.Cpu.Jit
             return _pageTable.Read(va) + (va & PageMask);
         }
 
-        /// <inheritdoc/>
         /// <remarks>
         /// This function also validates that the given range is both valid and mapped, and will throw if it is not.
         /// </remarks>
-        public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
+        public override void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
         {
             AssertValidAddressAndSize(va, size);
 
@@ -407,23 +401,6 @@ namespace Ryujinx.Cpu.Jit
             _pages.SignalMemoryTracking(Tracking, va, size, write, exemptId);
         }
 
-        /// <summary>
-        /// Computes the number of pages in a virtual address range.
-        /// </summary>
-        /// <param name="va">Virtual address of the range</param>
-        /// <param name="size">Size of the range</param>
-        /// <param name="startVa">The virtual address of the beginning of the first page</param>
-        /// <remarks>This function does not differentiate between allocated and unallocated pages.</remarks>
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private static int GetPagesCount(ulong va, ulong size, out ulong startVa)
-        {
-            // WARNING: Always check if ulong does not overflow during the operations.
-            startVa = va & ~(ulong)PageMask;
-            ulong vaSpan = (va - startVa + size + PageMask) & ~(ulong)PageMask;
-
-            return (int)(vaSpan / PageSize);
-        }
-
         /// <inheritdoc/>
         public void Reprotect(ulong va, ulong size, MemoryPermission protection)
         {
@@ -470,10 +447,16 @@ namespace Ryujinx.Cpu.Jit
             _memoryEh.Dispose();
         }
 
-        protected override Span<byte> GetPhysicalAddressSpan(ulong pa, int size)
+        protected override Memory<byte> GetPhysicalAddressMemory(nuint pa, int size)
+            => _addressSpace.Mirror.GetMemory(pa, size);
+
+        protected override Span<byte> GetPhysicalAddressSpan(nuint pa, int size)
             => _addressSpace.Mirror.GetSpan(pa, size);
 
-        protected override ulong TranslateVirtualAddressForRead(ulong va)
-            => va;
+        protected override nuint TranslateVirtualAddressChecked(ulong va)
+            => (nuint)GetPhysicalAddressChecked(va);
+
+        protected override nuint TranslateVirtualAddressUnchecked(ulong va)
+            => (nuint)GetPhysicalAddressInternal(va);
     }
 }
diff --git a/src/Ryujinx.Cpu/Jit/MemoryManagerHostTracked.cs b/src/Ryujinx.Cpu/Jit/MemoryManagerHostTracked.cs
index 18404bcc7..b2964cd29 100644
--- a/src/Ryujinx.Cpu/Jit/MemoryManagerHostTracked.cs
+++ b/src/Ryujinx.Cpu/Jit/MemoryManagerHostTracked.cs
@@ -8,14 +8,13 @@ using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
 
 namespace Ryujinx.Cpu.Jit
 {
     /// <summary>
     /// Represents a CPU memory manager which maps guest virtual memory directly onto a host virtual region.
     /// </summary>
-    public sealed class MemoryManagerHostTracked : VirtualMemoryManagerRefCountedBase<ulong, ulong>, IWritableBlock, IMemoryManager, IVirtualMemoryManagerTracked
+    public sealed class MemoryManagerHostTracked : VirtualMemoryManagerRefCountedBase, IMemoryManager, IVirtualMemoryManagerTracked
     {
         private readonly InvalidAccessHandler _invalidAccessHandler;
         private readonly bool _unsafeMode;
@@ -100,12 +99,6 @@ namespace Ryujinx.Cpu.Jit
             Tracking.Map(va, size);
         }
 
-        /// <inheritdoc/>
-        public void MapForeign(ulong va, nuint hostPointer, ulong size)
-        {
-            throw new NotSupportedException();
-        }
-
         /// <inheritdoc/>
         public void Unmap(ulong va, ulong size)
         {
@@ -120,18 +113,11 @@ namespace Ryujinx.Cpu.Jit
             _nativePageTable.Unmap(va, size);
         }
 
-        public T Read<T>(ulong va) where T : unmanaged
-        {
-            return MemoryMarshal.Cast<byte, T>(GetSpan(va, Unsafe.SizeOf<T>()))[0];
-        }
-
-        public T ReadTracked<T>(ulong va) where T : unmanaged
+        public override T ReadTracked<T>(ulong va)
         {
             try
             {
-                SignalMemoryTracking(va, (ulong)Unsafe.SizeOf<T>(), false);
-
-                return Read<T>(va);
+                return base.ReadTracked<T>(va);
             }
             catch (InvalidMemoryRegionException)
             {
@@ -145,38 +131,39 @@ namespace Ryujinx.Cpu.Jit
         }
 
         public override void Read(ulong va, Span<byte> data)
-        {
-            ReadImpl(va, data);
-        }
-
-        public void Write<T>(ulong va, T value) where T : unmanaged
-        {
-            Write(va, MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref value, 1)));
-        }
-
-        public void Write(ulong va, ReadOnlySpan<byte> data)
         {
             if (data.Length == 0)
             {
                 return;
             }
 
-            SignalMemoryTracking(va, (ulong)data.Length, true);
-
-            WriteImpl(va, data);
-        }
-
-        public void WriteUntracked(ulong va, ReadOnlySpan<byte> data)
-        {
-            if (data.Length == 0)
+            try
             {
-                return;
-            }
+                AssertValidAddressAndSize(va, (ulong)data.Length);
 
-            WriteImpl(va, data);
+                ulong endVa = va + (ulong)data.Length;
+                int offset = 0;
+
+                while (va < endVa)
+                {
+                    (MemoryBlock memory, ulong rangeOffset, ulong copySize) = GetMemoryOffsetAndSize(va, (ulong)(data.Length - offset));
+
+                    memory.GetSpan(rangeOffset, (int)copySize).CopyTo(data.Slice(offset, (int)copySize));
+
+                    va += copySize;
+                    offset += (int)copySize;
+                }
+            }
+            catch (InvalidMemoryRegionException)
+            {
+                if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
+                {
+                    throw;
+                }
+            }
         }
 
-        public bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan<byte> data)
+        public override bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan<byte> data)
         {
             if (data.Length == 0)
             {
@@ -206,35 +193,7 @@ namespace Ryujinx.Cpu.Jit
             }
         }
 
-        private void WriteImpl(ulong va, ReadOnlySpan<byte> data)
-        {
-            try
-            {
-                AssertValidAddressAndSize(va, (ulong)data.Length);
-
-                ulong endVa = va + (ulong)data.Length;
-                int offset = 0;
-
-                while (va < endVa)
-                {
-                    (MemoryBlock memory, ulong rangeOffset, ulong copySize) = GetMemoryOffsetAndSize(va, (ulong)(data.Length - offset));
-
-                    data.Slice(offset, (int)copySize).CopyTo(memory.GetSpan(rangeOffset, (int)copySize));
-
-                    va += copySize;
-                    offset += (int)copySize;
-                }
-            }
-            catch (InvalidMemoryRegionException)
-            {
-                if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
-                {
-                    throw;
-                }
-            }
-        }
-
-        public ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false)
+        public override ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false)
         {
             if (size == 0)
             {
@@ -254,13 +213,13 @@ namespace Ryujinx.Cpu.Jit
             {
                 Span<byte> data = new byte[size];
 
-                ReadImpl(va, data);
+                Read(va, data);
 
                 return data;
             }
         }
 
-        public WritableRegion GetWritableRegion(ulong va, int size, bool tracked = false)
+        public override WritableRegion GetWritableRegion(ulong va, int size, bool tracked = false)
         {
             if (size == 0)
             {
@@ -280,7 +239,7 @@ namespace Ryujinx.Cpu.Jit
             {
                 Memory<byte> memory = new byte[size];
 
-                ReadImpl(va, memory.Span);
+                Read(va, memory.Span);
 
                 return new WritableRegion(this, va, memory);
             }
@@ -299,7 +258,7 @@ namespace Ryujinx.Cpu.Jit
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public bool IsMapped(ulong va)
+        public override bool IsMapped(ulong va)
         {
             return ValidateAddress(va) && _pages.IsMapped(va);
         }
@@ -311,8 +270,6 @@ namespace Ryujinx.Cpu.Jit
             return _pages.IsRangeMapped(va, size);
         }
 
-        private static void ThrowMemoryNotContiguous() => throw new MemoryNotContiguousException();
-
         private bool TryGetVirtualContiguous(ulong va, int size, out MemoryBlock memory, out ulong offset)
         {
             if (_addressSpace.HasAnyPrivateAllocation(va, (ulong)size, out PrivateRange range))
@@ -491,44 +448,11 @@ namespace Ryujinx.Cpu.Jit
             return regions;
         }
 
-        private void ReadImpl(ulong va, Span<byte> data)
-        {
-            if (data.Length == 0)
-            {
-                return;
-            }
-
-            try
-            {
-                AssertValidAddressAndSize(va, (ulong)data.Length);
-
-                ulong endVa = va + (ulong)data.Length;
-                int offset = 0;
-
-                while (va < endVa)
-                {
-                    (MemoryBlock memory, ulong rangeOffset, ulong copySize) = GetMemoryOffsetAndSize(va, (ulong)(data.Length - offset));
-
-                    memory.GetSpan(rangeOffset, (int)copySize).CopyTo(data.Slice(offset, (int)copySize));
-
-                    va += copySize;
-                    offset += (int)copySize;
-                }
-            }
-            catch (InvalidMemoryRegionException)
-            {
-                if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
-                {
-                    throw;
-                }
-            }
-        }
-
         /// <inheritdoc/>
         /// <remarks>
         /// This function also validates that the given range is both valid and mapped, and will throw if it is not.
         /// </remarks>
-        public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
+        public override void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
         {
             AssertValidAddressAndSize(va, size);
 
@@ -543,23 +467,6 @@ namespace Ryujinx.Cpu.Jit
             _pages.SignalMemoryTracking(Tracking, va, size, write, exemptId);
         }
 
-        /// <summary>
-        /// Computes the number of pages in a virtual address range.
-        /// </summary>
-        /// <param name="va">Virtual address of the range</param>
-        /// <param name="size">Size of the range</param>
-        /// <param name="startVa">The virtual address of the beginning of the first page</param>
-        /// <remarks>This function does not differentiate between allocated and unallocated pages.</remarks>
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private int GetPagesCount(ulong va, ulong size, out ulong startVa)
-        {
-            // WARNING: Always check if ulong does not overflow during the operations.
-            startVa = va & ~(ulong)PageMask;
-            ulong vaSpan = (va - startVa + size + PageMask) & ~(ulong)PageMask;
-
-            return (int)(vaSpan / PageSize);
-        }
-
         public RegionHandle BeginTracking(ulong address, ulong size, int id, RegionFlags flags = RegionFlags.None)
         {
             return Tracking.BeginTracking(address, size, id, flags);
@@ -618,10 +525,44 @@ namespace Ryujinx.Cpu.Jit
             _nativePageTable.Dispose();
         }
 
-        protected override Span<byte> GetPhysicalAddressSpan(ulong pa, int size)
+        protected override Memory<byte> GetPhysicalAddressMemory(nuint pa, int size)
+            => _backingMemory.GetMemory(pa, size);
+
+        protected override Span<byte> GetPhysicalAddressSpan(nuint pa, int size)
             => _backingMemory.GetSpan(pa, size);
 
-        protected override ulong TranslateVirtualAddressForRead(ulong va)
-            => GetPhysicalAddressInternal(va);
+        protected override void WriteImpl(ulong va, ReadOnlySpan<byte> data)
+        {
+            try
+            {
+                AssertValidAddressAndSize(va, (ulong)data.Length);
+
+                ulong endVa = va + (ulong)data.Length;
+                int offset = 0;
+
+                while (va < endVa)
+                {
+                    (MemoryBlock memory, ulong rangeOffset, ulong copySize) = GetMemoryOffsetAndSize(va, (ulong)(data.Length - offset));
+
+                    data.Slice(offset, (int)copySize).CopyTo(memory.GetSpan(rangeOffset, (int)copySize));
+
+                    va += copySize;
+                    offset += (int)copySize;
+                }
+            }
+            catch (InvalidMemoryRegionException)
+            {
+                if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
+                {
+                    throw;
+                }
+            }
+        }
+
+        protected override nuint TranslateVirtualAddressChecked(ulong va)
+            => (nuint)GetPhysicalAddressChecked(va);
+
+        protected override nuint TranslateVirtualAddressUnchecked(ulong va)
+            => (nuint)GetPhysicalAddressInternal(va);
     }
 }
diff --git a/src/Ryujinx.Cpu/VirtualMemoryManagerRefCountedBase.cs b/src/Ryujinx.Cpu/VirtualMemoryManagerRefCountedBase.cs
index c2d8cfb1a..3c7b33805 100644
--- a/src/Ryujinx.Cpu/VirtualMemoryManagerRefCountedBase.cs
+++ b/src/Ryujinx.Cpu/VirtualMemoryManagerRefCountedBase.cs
@@ -1,13 +1,10 @@
 using Ryujinx.Memory;
 using System.Diagnostics;
-using System.Numerics;
 using System.Threading;
 
 namespace Ryujinx.Cpu
 {
-    public abstract class VirtualMemoryManagerRefCountedBase<TVirtual, TPhysical> : VirtualMemoryManagerBase<TVirtual, TPhysical>, IRefCounted
-        where TVirtual : IBinaryInteger<TVirtual>
-        where TPhysical : IBinaryInteger<TPhysical>
+    public abstract class VirtualMemoryManagerRefCountedBase : VirtualMemoryManagerBase, IRefCounted
     {
         private int _referenceCount;
 
diff --git a/src/Ryujinx.Memory/AddressSpaceManager.cs b/src/Ryujinx.Memory/AddressSpaceManager.cs
index f19b45b65..f089c8573 100644
--- a/src/Ryujinx.Memory/AddressSpaceManager.cs
+++ b/src/Ryujinx.Memory/AddressSpaceManager.cs
@@ -3,7 +3,6 @@ using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
 
 namespace Ryujinx.Memory
 {
@@ -11,7 +10,7 @@ namespace Ryujinx.Memory
     /// Represents a address space manager.
     /// Supports virtual memory region mapping, address translation and read/write access to mapped regions.
     /// </summary>
-    public sealed class AddressSpaceManager : VirtualMemoryManagerBase<ulong, nuint>, IVirtualMemoryManager, IWritableBlock
+    public sealed class AddressSpaceManager : VirtualMemoryManagerBase, IVirtualMemoryManager
     {
         /// <inheritdoc/>
         public bool Supports4KBPages => true;
@@ -63,8 +62,7 @@ namespace Ryujinx.Memory
             }
         }
 
-        /// <inheritdoc/>
-        public void MapForeign(ulong va, nuint hostPointer, ulong size)
+        public override void MapForeign(ulong va, nuint hostPointer, ulong size)
         {
             AssertValidAddressAndSize(va, size);
 
@@ -92,106 +90,6 @@ namespace Ryujinx.Memory
             }
         }
 
-        /// <inheritdoc/>
-        public T Read<T>(ulong va) where T : unmanaged
-        {
-            return MemoryMarshal.Cast<byte, T>(GetSpan(va, Unsafe.SizeOf<T>()))[0];
-        }
-
-        /// <inheritdoc/>
-        public void Write<T>(ulong va, T value) where T : unmanaged
-        {
-            Write(va, MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref value, 1)));
-        }
-
-        /// <inheritdoc/>
-        public void Write(ulong va, ReadOnlySpan<byte> data)
-        {
-            if (data.Length == 0)
-            {
-                return;
-            }
-
-            AssertValidAddressAndSize(va, (ulong)data.Length);
-
-            if (IsContiguousAndMapped(va, data.Length))
-            {
-                data.CopyTo(GetHostSpanContiguous(va, data.Length));
-            }
-            else
-            {
-                int offset = 0, size;
-
-                if ((va & PageMask) != 0)
-                {
-                    size = Math.Min(data.Length, PageSize - (int)(va & PageMask));
-
-                    data[..size].CopyTo(GetHostSpanContiguous(va, size));
-
-                    offset += size;
-                }
-
-                for (; offset < data.Length; offset += size)
-                {
-                    size = Math.Min(data.Length - offset, PageSize);
-
-                    data.Slice(offset, size).CopyTo(GetHostSpanContiguous(va + (ulong)offset, size));
-                }
-            }
-        }
-
-        /// <inheritdoc/>
-        public bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan<byte> data)
-        {
-            Write(va, data);
-
-            return true;
-        }
-
-        /// <inheritdoc/>
-        public ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false)
-        {
-            if (size == 0)
-            {
-                return ReadOnlySpan<byte>.Empty;
-            }
-
-            if (IsContiguousAndMapped(va, size))
-            {
-                return GetHostSpanContiguous(va, size);
-            }
-            else
-            {
-                Span<byte> data = new byte[size];
-
-                Read(va, data);
-
-                return data;
-            }
-        }
-
-        /// <inheritdoc/>
-        public unsafe WritableRegion GetWritableRegion(ulong va, int size, bool tracked = false)
-        {
-            if (size == 0)
-            {
-                return new WritableRegion(null, va, Memory<byte>.Empty);
-            }
-
-            if (IsContiguousAndMapped(va, size))
-            {
-                return new WritableRegion(null, va, new NativeMemoryManager<byte>((byte*)GetHostAddress(va), size).Memory);
-            }
-            else
-            {
-                Memory<byte> memory = new byte[size];
-
-                GetSpan(va, size).CopyTo(memory.Span);
-
-                return new WritableRegion(this, va, memory);
-            }
-        }
-
         /// <inheritdoc/>
         public unsafe ref T GetRef<T>(ulong va) where T : unmanaged
         {
@@ -203,50 +101,6 @@ namespace Ryujinx.Memory
             return ref *(T*)GetHostAddress(va);
         }
 
-        /// <inheritdoc/>
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private static int GetPagesCount(ulong va, uint size, out ulong startVa)
-        {
-            // WARNING: Always check if ulong does not overflow during the operations.
-            startVa = va & ~(ulong)PageMask;
-            ulong vaSpan = (va - startVa + size + PageMask) & ~(ulong)PageMask;
-
-            return (int)(vaSpan / PageSize);
-        }
-
-        private static void ThrowMemoryNotContiguous() => throw new MemoryNotContiguousException();
-
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private bool IsContiguousAndMapped(ulong va, int size) => IsContiguous(va, size) && IsMapped(va);
-
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private bool IsContiguous(ulong va, int size)
-        {
-            if (!ValidateAddress(va) || !ValidateAddressAndSize(va, (ulong)size))
-            {
-                return false;
-            }
-
-            int pages = GetPagesCount(va, (uint)size, out va);
-
-            for (int page = 0; page < pages - 1; page++)
-            {
-                if (!ValidateAddress(va + PageSize))
-                {
-                    return false;
-                }
-
-                if (GetHostAddress(va) + PageSize != GetHostAddress(va + PageSize))
-                {
-                    return false;
-                }
-
-                va += PageSize;
-            }
-
-            return true;
-        }
-
         /// <inheritdoc/>
         public IEnumerable<HostMemoryRange> GetHostRegions(ulong va, ulong size)
         {
@@ -304,7 +158,7 @@ namespace Ryujinx.Memory
                 return null;
             }
 
-            int pages = GetPagesCount(va, (uint)size, out va);
+            int pages = GetPagesCount(va, size, out va);
 
             var regions = new List<HostMemoryRange>();
 
@@ -336,9 +190,8 @@ namespace Ryujinx.Memory
             return regions;
         }
 
-        /// <inheritdoc/>
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public bool IsMapped(ulong va)
+        public override bool IsMapped(ulong va)
         {
             if (!ValidateAddress(va))
             {
@@ -351,7 +204,7 @@ namespace Ryujinx.Memory
         /// <inheritdoc/>
         public bool IsRangeMapped(ulong va, ulong size)
         {
-            if (size == 0UL)
+            if (size == 0)
             {
                 return true;
             }
@@ -376,11 +229,6 @@ namespace Ryujinx.Memory
             return true;
         }
 
-        private unsafe Span<byte> GetHostSpanContiguous(ulong va, int size)
-        {
-            return new Span<byte>((void*)GetHostAddress(va), size);
-        }
-
         private nuint GetHostAddress(ulong va)
         {
             return _pageTable.Read(va) + (nuint)(va & PageMask);
@@ -397,16 +245,16 @@ namespace Ryujinx.Memory
             throw new NotImplementedException();
         }
 
-        /// <inheritdoc/>
-        public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
-        {
-            // Only the ARM Memory Manager has tracking for now.
-        }
+        protected unsafe override Memory<byte> GetPhysicalAddressMemory(nuint pa, int size)
+            => new NativeMemoryManager<byte>((byte*)pa, size).Memory;
 
         protected override unsafe Span<byte> GetPhysicalAddressSpan(nuint pa, int size)
-            => new((void*)pa, size);
+            => new Span<byte>((void*)pa, size);
 
-        protected override nuint TranslateVirtualAddressForRead(ulong va)
+        protected override nuint TranslateVirtualAddressChecked(ulong va)
+            => GetHostAddress(va);
+
+        protected override nuint TranslateVirtualAddressUnchecked(ulong va)
             => GetHostAddress(va);
     }
 }
diff --git a/src/Ryujinx.Memory/BytesReadOnlySequenceSegment.cs b/src/Ryujinx.Memory/BytesReadOnlySequenceSegment.cs
new file mode 100644
index 000000000..5fe8d936c
--- /dev/null
+++ b/src/Ryujinx.Memory/BytesReadOnlySequenceSegment.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Buffers;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Memory
+{
+    /// <summary>
+    /// A concrete implementation of <seealso cref="ReadOnlySequence{Byte}"/>,
+    /// with methods to help build a full sequence.
+    /// </summary>
+    public sealed class BytesReadOnlySequenceSegment : ReadOnlySequenceSegment<byte>
+    {
+        public BytesReadOnlySequenceSegment(Memory<byte> memory) => Memory = memory;
+
+        public BytesReadOnlySequenceSegment Append(Memory<byte> memory)
+        {
+            var nextSegment = new BytesReadOnlySequenceSegment(memory)
+            {
+                RunningIndex = RunningIndex + Memory.Length
+            };
+
+            Next = nextSegment;
+
+            return nextSegment;
+        }
+
+        /// <summary>
+        /// Attempts to determine if the current <seealso cref="Memory{Byte}"/> and <paramref name="other"/> are contiguous.
+        /// Only works if both were created by a <seealso cref="NativeMemoryManager{Byte}"/>.
+        /// </summary>
+        /// <param name="other">The segment to check if continuous with the current one</param>
+        /// <param name="contiguousStart">The starting address of the contiguous segment</param>
+        /// <param name="contiguousSize">The size of the contiguous segment</param>
+        /// <returns>True if the segments are contiguous, otherwise false</returns>
+        public unsafe bool IsContiguousWith(Memory<byte> other, out nuint contiguousStart, out int contiguousSize)
+        {
+            if (MemoryMarshal.TryGetMemoryManager<byte, NativeMemoryManager<byte>>(Memory, out var thisMemoryManager) &&
+                MemoryMarshal.TryGetMemoryManager<byte, NativeMemoryManager<byte>>(other, out var otherMemoryManager) &&
+                thisMemoryManager.Pointer + thisMemoryManager.Length == otherMemoryManager.Pointer)
+            {
+                contiguousStart = (nuint)thisMemoryManager.Pointer;
+                contiguousSize = thisMemoryManager.Length + otherMemoryManager.Length;
+                return true;
+            }
+            else
+            {
+                contiguousStart = 0;
+                contiguousSize = 0;
+                return false;
+            }
+        }
+
+        /// <summary>
+        /// Replaces the current <seealso cref="Memory{Byte}"/> value with the one provided.
+        /// </summary>
+        /// <param name="memory">The new segment to hold in this <seealso cref="BytesReadOnlySequenceSegment"/></param>
+        public void Replace(Memory<byte> memory)
+            => Memory = memory;
+    }
+}
diff --git a/src/Ryujinx.Memory/IVirtualMemoryManager.cs b/src/Ryujinx.Memory/IVirtualMemoryManager.cs
index 557da2f26..96d3e8579 100644
--- a/src/Ryujinx.Memory/IVirtualMemoryManager.cs
+++ b/src/Ryujinx.Memory/IVirtualMemoryManager.cs
@@ -124,6 +124,16 @@ namespace Ryujinx.Memory
             }
         }
 
+        /// <summary>
+        /// Gets a read-only sequence of read-only memory blocks from CPU mapped memory.
+        /// </summary>
+        /// <param name="va">Virtual address of the data</param>
+        /// <param name="size">Size of the data</param>
+        /// <param name="tracked">True if read tracking is triggered on the memory</param>
+        /// <returns>A read-only sequence of read-only memory of the data</returns>
+        /// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
+        ReadOnlySequence<byte> GetReadOnlySequence(ulong va, int size, bool tracked = false);
+
         /// <summary>
         /// Gets a read-only span of data from CPU mapped memory.
         /// </summary>
diff --git a/src/Ryujinx.Memory/NativeMemoryManager.cs b/src/Ryujinx.Memory/NativeMemoryManager.cs
index fe718bda8..9ca632938 100644
--- a/src/Ryujinx.Memory/NativeMemoryManager.cs
+++ b/src/Ryujinx.Memory/NativeMemoryManager.cs
@@ -14,6 +14,10 @@ namespace Ryujinx.Memory
             _length = length;
         }
 
+        public unsafe T* Pointer => _pointer;
+
+        public int Length => _length;
+
         public override Span<T> GetSpan()
         {
             return new Span<T>((void*)_pointer, _length);
diff --git a/src/Ryujinx.Memory/VirtualMemoryManagerBase.cs b/src/Ryujinx.Memory/VirtualMemoryManagerBase.cs
index cbec88cc5..506e25f66 100644
--- a/src/Ryujinx.Memory/VirtualMemoryManagerBase.cs
+++ b/src/Ryujinx.Memory/VirtualMemoryManagerBase.cs
@@ -1,34 +1,171 @@
+using Ryujinx.Common.Memory;
 using System;
-using System.Numerics;
+using System.Buffers;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 
 namespace Ryujinx.Memory
 {
-    public abstract class VirtualMemoryManagerBase<TVirtual, TPhysical>
-        where TVirtual : IBinaryInteger<TVirtual>
-        where TPhysical : IBinaryInteger<TPhysical>
+    public abstract class VirtualMemoryManagerBase : IWritableBlock
     {
         public const int PageBits = 12;
         public const int PageSize = 1 << PageBits;
         public const int PageMask = PageSize - 1;
 
-        protected abstract TVirtual AddressSpaceSize { get; }
+        protected abstract ulong AddressSpaceSize { get; }
 
-        public virtual void Read(TVirtual va, Span<byte> data)
+        public virtual ReadOnlySequence<byte> GetReadOnlySequence(ulong va, int size, bool tracked = false)
+        {
+            if (size == 0)
+            {
+                return ReadOnlySequence<byte>.Empty;
+            }
+
+            if (tracked)
+            {
+                SignalMemoryTracking(va, (ulong)size, false);
+            }
+
+            if (IsContiguousAndMapped(va, size))
+            {
+                nuint pa = TranslateVirtualAddressUnchecked(va);
+
+                return new ReadOnlySequence<byte>(GetPhysicalAddressMemory(pa, size));
+            }
+            else
+            {
+                AssertValidAddressAndSize(va, size);
+
+                int offset = 0, segmentSize;
+
+                BytesReadOnlySequenceSegment first = null, last = null;
+
+                if ((va & PageMask) != 0)
+                {
+                    nuint pa = TranslateVirtualAddressChecked(va);
+
+                    segmentSize = Math.Min(size, PageSize - (int)(va & PageMask));
+
+                    Memory<byte> memory = GetPhysicalAddressMemory(pa, segmentSize);
+
+                    first = last = new BytesReadOnlySequenceSegment(memory);
+
+                    offset += segmentSize;
+                }
+
+                for (; offset < size; offset += segmentSize)
+                {
+                    nuint pa = TranslateVirtualAddressChecked(va + (ulong)offset);
+
+                    segmentSize = Math.Min(size - offset, PageSize);
+
+                    Memory<byte> memory = GetPhysicalAddressMemory(pa, segmentSize);
+
+                    if (first is null)
+                    {
+                        first = last = new BytesReadOnlySequenceSegment(memory);
+                    }
+                    else
+                    {
+                        if (last.IsContiguousWith(memory, out nuint contiguousStart, out int contiguousSize))
+                        {
+                            last.Replace(GetPhysicalAddressMemory(contiguousStart, contiguousSize));
+                        }
+                        else
+                        {
+                            last = last.Append(memory);
+                        }
+                    }
+                }
+
+                return new ReadOnlySequence<byte>(first, 0, last, (int)(size - last.RunningIndex));
+            }
+        }
+
+        public virtual ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false)
+        {
+            if (size == 0)
+            {
+                return ReadOnlySpan<byte>.Empty;
+            }
+
+            if (tracked)
+            {
+                SignalMemoryTracking(va, (ulong)size, false);
+            }
+
+            if (IsContiguousAndMapped(va, size))
+            {
+                nuint pa = TranslateVirtualAddressUnchecked(va);
+
+                return GetPhysicalAddressSpan(pa, size);
+            }
+            else
+            {
+                Span<byte> data = new byte[size];
+
+                Read(va, data);
+
+                return data;
+            }
+        }
+
+        public virtual WritableRegion GetWritableRegion(ulong va, int size, bool tracked = false)
+        {
+            if (size == 0)
+            {
+                return new WritableRegion(null, va, Memory<byte>.Empty);
+            }
+
+            if (tracked)
+            {
+                SignalMemoryTracking(va, (ulong)size, true);
+            }
+
+            if (IsContiguousAndMapped(va, size))
+            {
+                nuint pa = TranslateVirtualAddressUnchecked(va);
+
+                return new WritableRegion(null, va, GetPhysicalAddressMemory(pa, size));
+            }
+            else
+            {
+                IMemoryOwner<byte> memoryOwner = ByteMemoryPool.Rent(size);
+
+                Read(va, memoryOwner.Memory.Span);
+
+                return new WritableRegion(this, va, memoryOwner);
+            }
+        }
+
+        public abstract bool IsMapped(ulong va);
+
+        public virtual void MapForeign(ulong va, nuint hostPointer, ulong size)
+        {
+            throw new NotSupportedException();
+        }
+
+        public virtual T Read<T>(ulong va) where T : unmanaged
+        {
+            return MemoryMarshal.Cast<byte, T>(GetSpan(va, Unsafe.SizeOf<T>()))[0];
+        }
+
+        public virtual void Read(ulong va, Span<byte> data)
         {
             if (data.Length == 0)
             {
                 return;
             }
 
-            AssertValidAddressAndSize(va, TVirtual.CreateChecked(data.Length));
+            AssertValidAddressAndSize(va, data.Length);
 
             int offset = 0, size;
 
-            if ((int.CreateTruncating(va) & PageMask) != 0)
+            if ((va & PageMask) != 0)
             {
-                TPhysical pa = TranslateVirtualAddressForRead(va);
+                nuint pa = TranslateVirtualAddressChecked(va);
 
-                size = Math.Min(data.Length, PageSize - ((int.CreateTruncating(va) & PageMask)));
+                size = Math.Min(data.Length, PageSize - (int)(va & PageMask));
 
                 GetPhysicalAddressSpan(pa, size).CopyTo(data[..size]);
 
@@ -37,7 +174,7 @@ namespace Ryujinx.Memory
 
             for (; offset < data.Length; offset += size)
             {
-                TPhysical pa = TranslateVirtualAddressForRead(va + TVirtual.CreateChecked(offset));
+                nuint pa = TranslateVirtualAddressChecked(va + (ulong)offset);
 
                 size = Math.Min(data.Length - offset, PageSize);
 
@@ -45,13 +182,84 @@ namespace Ryujinx.Memory
             }
         }
 
+        public virtual T ReadTracked<T>(ulong va) where T : unmanaged
+        {
+            SignalMemoryTracking(va, (ulong)Unsafe.SizeOf<T>(), false);
+
+            return Read<T>(va);
+        }
+
+        public virtual void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
+        {
+            // No default implementation
+        }
+
+        public virtual void Write(ulong va, ReadOnlySpan<byte> data)
+        {
+            if (data.Length == 0)
+            {
+                return;
+            }
+
+            SignalMemoryTracking(va, (ulong)data.Length, true);
+
+            WriteImpl(va, data);
+        }
+
+        public virtual void Write<T>(ulong va, T value) where T : unmanaged
+        {
+            Write(va, MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref value, 1)));
+        }
+
+        public virtual void WriteUntracked(ulong va, ReadOnlySpan<byte> data)
+        {
+            if (data.Length == 0)
+            {
+                return;
+            }
+
+            WriteImpl(va, data);
+        }
+
+        public virtual bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan<byte> data)
+        {
+            if (data.Length == 0)
+            {
+                return false;
+            }
+
+            if (IsContiguousAndMapped(va, data.Length))
+            {
+                SignalMemoryTracking(va, (ulong)data.Length, false);
+
+                nuint pa = TranslateVirtualAddressChecked(va);
+
+                var target = GetPhysicalAddressSpan(pa, data.Length);
+
+                bool changed = !data.SequenceEqual(target);
+
+                if (changed)
+                {
+                    data.CopyTo(target);
+                }
+
+                return changed;
+            }
+            else
+            {
+                Write(va, data);
+
+                return true;
+            }
+        }
+
         /// <summary>
         /// Ensures the combination of virtual address and size is part of the addressable space.
         /// </summary>
         /// <param name="va">Virtual address of the range</param>
         /// <param name="size">Size of the range in bytes</param>
         /// <exception cref="InvalidMemoryRegionException">Throw when the memory region specified outside the addressable space</exception>
-        protected void AssertValidAddressAndSize(TVirtual va, TVirtual size)
+        protected void AssertValidAddressAndSize(ulong va, ulong size)
         {
             if (!ValidateAddressAndSize(va, size))
             {
@@ -59,16 +267,82 @@ namespace Ryujinx.Memory
             }
         }
 
-        protected abstract Span<byte> GetPhysicalAddressSpan(TPhysical pa, int size);
+        /// <summary>
+        /// Ensures the combination of virtual address and size is part of the addressable space.
+        /// </summary>
+        /// <param name="va">Virtual address of the range</param>
+        /// <param name="size">Size of the range in bytes</param>
+        /// <exception cref="InvalidMemoryRegionException">Throw when the memory region specified outside the addressable space</exception>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        protected void AssertValidAddressAndSize(ulong va, int size)
+            => AssertValidAddressAndSize(va, (ulong)size);
 
-        protected abstract TPhysical TranslateVirtualAddressForRead(TVirtual va);
+        /// <summary>
+        /// Computes the number of pages in a virtual address range.
+        /// </summary>
+        /// <param name="va">Virtual address of the range</param>
+        /// <param name="size">Size of the range</param>
+        /// <param name="startVa">The virtual address of the beginning of the first page</param>
+        /// <remarks>This function does not differentiate between allocated and unallocated pages.</remarks>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        protected static int GetPagesCount(ulong va, ulong size, out ulong startVa)
+        {
+            // WARNING: Always check if ulong does not overflow during the operations.
+            startVa = va & ~(ulong)PageMask;
+            ulong vaSpan = (va - startVa + size + PageMask) & ~(ulong)PageMask;
+
+            return (int)(vaSpan / PageSize);
+        }
+
+        protected abstract Memory<byte> GetPhysicalAddressMemory(nuint pa, int size);
+
+        protected abstract Span<byte> GetPhysicalAddressSpan(nuint pa, int size);
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        protected bool IsContiguous(ulong va, int size) => IsContiguous(va, (ulong)size);
+
+        protected virtual bool IsContiguous(ulong va, ulong size)
+        {
+            if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size))
+            {
+                return false;
+            }
+
+            int pages = GetPagesCount(va, size, out va);
+
+            for (int page = 0; page < pages - 1; page++)
+            {
+                if (!ValidateAddress(va + PageSize))
+                {
+                    return false;
+                }
+
+                if (TranslateVirtualAddressUnchecked(va) + PageSize != TranslateVirtualAddressUnchecked(va + PageSize))
+                {
+                    return false;
+                }
+
+                va += PageSize;
+            }
+
+            return true;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        protected bool IsContiguousAndMapped(ulong va, int size)
+            => IsContiguous(va, size) && IsMapped(va);
+
+        protected abstract nuint TranslateVirtualAddressChecked(ulong va);
+
+        protected abstract nuint TranslateVirtualAddressUnchecked(ulong va);
 
         /// <summary>
         /// Checks if the virtual address is part of the addressable space.
         /// </summary>
         /// <param name="va">Virtual address</param>
         /// <returns>True if the virtual address is part of the addressable space</returns>
-        protected bool ValidateAddress(TVirtual va)
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        protected bool ValidateAddress(ulong va)
         {
             return va < AddressSpaceSize;
         }
@@ -79,13 +353,53 @@ namespace Ryujinx.Memory
         /// <param name="va">Virtual address of the range</param>
         /// <param name="size">Size of the range in bytes</param>
         /// <returns>True if the combination of virtual address and size is part of the addressable space</returns>
-        protected bool ValidateAddressAndSize(TVirtual va, TVirtual size)
+        protected bool ValidateAddressAndSize(ulong va, ulong size)
         {
-            TVirtual endVa = va + size;
+            ulong endVa = va + size;
             return endVa >= va && endVa >= size && endVa <= AddressSpaceSize;
         }
 
         protected static void ThrowInvalidMemoryRegionException(string message)
             => throw new InvalidMemoryRegionException(message);
+
+        protected static void ThrowMemoryNotContiguous()
+            => throw new MemoryNotContiguousException();
+
+        protected virtual void WriteImpl(ulong va, ReadOnlySpan<byte> data)
+        {
+            AssertValidAddressAndSize(va, data.Length);
+
+            if (IsContiguousAndMapped(va, data.Length))
+            {
+                nuint pa = TranslateVirtualAddressUnchecked(va);
+
+                data.CopyTo(GetPhysicalAddressSpan(pa, data.Length));
+            }
+            else
+            {
+                int offset = 0, size;
+
+                if ((va & PageMask) != 0)
+                {
+                    nuint pa = TranslateVirtualAddressChecked(va);
+
+                    size = Math.Min(data.Length, PageSize - (int)(va & PageMask));
+
+                    data[..size].CopyTo(GetPhysicalAddressSpan(pa, size));
+
+                    offset += size;
+                }
+
+                for (; offset < data.Length; offset += size)
+                {
+                    nuint pa = TranslateVirtualAddressChecked(va + (ulong)offset);
+
+                    size = Math.Min(data.Length - offset, PageSize);
+
+                    data.Slice(offset, size).CopyTo(GetPhysicalAddressSpan(pa, size));
+                }
+            }
+        }
+
     }
 }
diff --git a/src/Ryujinx.Memory/WritableRegion.cs b/src/Ryujinx.Memory/WritableRegion.cs
index 666c8a99b..2c21ef4e8 100644
--- a/src/Ryujinx.Memory/WritableRegion.cs
+++ b/src/Ryujinx.Memory/WritableRegion.cs
@@ -1,4 +1,5 @@
 using System;
+using System.Buffers;
 
 namespace Ryujinx.Memory
 {
@@ -6,6 +7,7 @@ namespace Ryujinx.Memory
     {
         private readonly IWritableBlock _block;
         private readonly ulong _va;
+        private readonly IMemoryOwner<byte> _memoryOwner;
         private readonly bool _tracked;
 
         private bool NeedsWriteback => _block != null;
@@ -20,6 +22,12 @@ namespace Ryujinx.Memory
             Memory = memory;
         }
 
+        public WritableRegion(IWritableBlock block, ulong va, IMemoryOwner<byte> memoryOwner, bool tracked = false)
+            : this(block, va, memoryOwner.Memory, tracked)
+        {
+            _memoryOwner = memoryOwner;
+        }
+
         public void Dispose()
         {
             if (NeedsWriteback)
@@ -33,6 +41,8 @@ namespace Ryujinx.Memory
                     _block.WriteUntracked(_va, Memory.Span);
                 }
             }
+
+            _memoryOwner?.Dispose();
         }
     }
 }
diff --git a/src/Ryujinx.Tests.Memory/MockVirtualMemoryManager.cs b/src/Ryujinx.Tests.Memory/MockVirtualMemoryManager.cs
index 85a1ac02b..15e7d9b89 100644
--- a/src/Ryujinx.Tests.Memory/MockVirtualMemoryManager.cs
+++ b/src/Ryujinx.Tests.Memory/MockVirtualMemoryManager.cs
@@ -1,6 +1,7 @@
 using Ryujinx.Memory;
 using Ryujinx.Memory.Range;
 using System;
+using System.Buffers;
 using System.Collections.Generic;
 
 namespace Ryujinx.Tests.Memory
@@ -57,6 +58,11 @@ namespace Ryujinx.Tests.Memory
             throw new NotImplementedException();
         }
 
+        public ReadOnlySequence<byte> GetReadOnlySequence(ulong va, int size, bool tracked = false)
+        {
+            throw new NotImplementedException();
+        }
+
         public ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false)
         {
             throw new NotImplementedException();