From 4c2ed2706e3579ec1304907dad0d45673768e1fc Mon Sep 17 00:00:00 2001
From: Lioncash <mathew1800@gmail.com>
Date: Tue, 26 Nov 2019 12:33:20 -0500
Subject: [PATCH 01/13] core/memory: Introduce skeleton of Memory class

Currently, the main memory management code is one of the remaining
places where we have global state. The next series of changes will aim
to rectify this.

This change simply introduces the main skeleton of the class that will
contain all the necessary state.
---
 src/core/core.cpp   | 16 +++++++++++++---
 src/core/core.h     | 10 ++++++++++
 src/core/memory.cpp | 12 ++++++++++++
 src/core/memory.h   | 21 +++++++++++++++++++++
 4 files changed, 56 insertions(+), 3 deletions(-)

diff --git a/src/core/core.cpp b/src/core/core.cpp
index eba17218a3..c45fb960c7 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -39,6 +39,7 @@
 #include "core/hle/service/service.h"
 #include "core/hle/service/sm/sm.h"
 #include "core/loader/loader.h"
+#include "core/memory.h"
 #include "core/memory/cheat_engine.h"
 #include "core/perf_stats.h"
 #include "core/reporter.h"
@@ -112,8 +113,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
 }
 struct System::Impl {
     explicit Impl(System& system)
-        : kernel{system}, fs_controller{system}, cpu_core_manager{system}, reporter{system},
-          applet_manager{system} {}
+        : kernel{system}, fs_controller{system}, memory{system},
+          cpu_core_manager{system}, reporter{system}, applet_manager{system} {}
 
     Cpu& CurrentCpuCore() {
         return cpu_core_manager.GetCurrentCore();
@@ -341,7 +342,8 @@ struct System::Impl {
     std::unique_ptr<VideoCore::RendererBase> renderer;
     std::unique_ptr<Tegra::GPU> gpu_core;
     std::shared_ptr<Tegra::DebugContext> debug_context;
-    std::unique_ptr<Core::Hardware::InterruptManager> interrupt_manager;
+    std::unique_ptr<Hardware::InterruptManager> interrupt_manager;
+    Memory::Memory memory;
     CpuCoreManager cpu_core_manager;
     bool is_powered_on = false;
     bool exit_lock = false;
@@ -498,6 +500,14 @@ const ExclusiveMonitor& System::Monitor() const {
     return impl->cpu_core_manager.GetExclusiveMonitor();
 }
 
+Memory::Memory& System::Memory() {
+    return impl->memory;
+}
+
+const Memory::Memory& System::Memory() const {
+    return impl->memory;
+}
+
 Tegra::GPU& System::GPU() {
     return *impl->gpu_core;
 }
diff --git a/src/core/core.h b/src/core/core.h
index f9b1a28664..91184e433a 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -86,6 +86,10 @@ namespace Core::Hardware {
 class InterruptManager;
 }
 
+namespace Memory {
+class Memory;
+}
+
 namespace Core {
 
 class ARM_Interface;
@@ -225,6 +229,12 @@ public:
     /// Gets a constant reference to the exclusive monitor
     const ExclusiveMonitor& Monitor() const;
 
+    /// Gets a mutable reference to the system memory instance.
+    Memory::Memory& Memory();
+
+    /// Gets a constant reference to the system memory instance.
+    const Memory::Memory& Memory() const;
+
     /// Gets a mutable reference to the GPU interface
     Tegra::GPU& GPU();
 
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index fa49f3dd0f..2098f13f75 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -24,6 +24,18 @@ namespace Memory {
 
 static Common::PageTable* current_page_table = nullptr;
 
+// Implementation class used to keep the specifics of the memory subsystem hidden
+// from outside classes. This also allows modification to the internals of the memory
+// subsystem without needing to rebuild all files that make use of the memory interface.
+struct Memory::Impl {
+    explicit Impl(Core::System& system_) : system{system_} {}
+
+    Core::System& system;
+};
+
+Memory::Memory(Core::System& system) : impl{std::make_unique<Impl>(system)} {}
+Memory::~Memory() = default;
+
 void SetCurrentPageTable(Kernel::Process& process) {
     current_page_table = &process.VMManager().page_table;
 
diff --git a/src/core/memory.h b/src/core/memory.h
index 09008e1dd4..c690df3c31 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -8,6 +8,10 @@
 #include <string>
 #include "common/common_types.h"
 
+namespace Core {
+class System;
+}
+
 namespace Kernel {
 class Process;
 }
@@ -36,6 +40,23 @@ enum : VAddr {
     KERNEL_REGION_END = KERNEL_REGION_VADDR + KERNEL_REGION_SIZE,
 };
 
+/// Central class that handles all memory operations and state.
+class Memory {
+public:
+    explicit Memory(Core::System& system);
+    ~Memory();
+
+    Memory(const Memory&) = delete;
+    Memory& operator=(const Memory&) = delete;
+
+    Memory(Memory&&) = default;
+    Memory& operator=(Memory&&) = default;
+
+private:
+    struct Impl;
+    std::unique_ptr<Impl> impl;
+};
+
 /// Changes the currently active page table to that of
 /// the given process instance.
 void SetCurrentPageTable(Kernel::Process& process);

From 323680e5ad3ca0e27f2dd1de26816741b3243bed Mon Sep 17 00:00:00 2001
From: Lioncash <mathew1800@gmail.com>
Date: Tue, 26 Nov 2019 13:09:12 -0500
Subject: [PATCH 02/13] core/memory: Migrate over memory mapping functions to
 the new Memory class

Migrates all of the direct mapping facilities over to the new memory
class. In the process, this also obsoletes the need for memory_setup.h,
so we can remove it entirely from the project.
---
 src/core/CMakeLists.txt                |   1 -
 src/core/hle/kernel/vm_manager.cpp     |  13 +-
 src/core/memory.cpp                    | 191 +++++++++++++++----------
 src/core/memory.h                      |  59 ++++++++
 src/core/memory_setup.h                |  43 ------
 src/tests/core/arm/arm_test_common.cpp |  15 +-
 6 files changed, 187 insertions(+), 135 deletions(-)
 delete mode 100644 src/core/memory_setup.h

diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index f2e774a6b3..2dfdcb0d79 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -509,7 +509,6 @@ add_library(core STATIC
     memory/dmnt_cheat_vm.h
     memory.cpp
     memory.h
-    memory_setup.h
     perf_stats.cpp
     perf_stats.h
     reporter.cpp
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index e6eee09d7a..a9a20ef76d 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -16,7 +16,6 @@
 #include "core/hle/kernel/resource_limit.h"
 #include "core/hle/kernel/vm_manager.h"
 #include "core/memory.h"
-#include "core/memory_setup.h"
 
 namespace Kernel {
 namespace {
@@ -786,19 +785,21 @@ void VMManager::MergeAdjacentVMA(VirtualMemoryArea& left, const VirtualMemoryAre
 }
 
 void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
+    auto& memory = system.Memory();
+
     switch (vma.type) {
     case VMAType::Free:
-        Memory::UnmapRegion(page_table, vma.base, vma.size);
+        memory.UnmapRegion(page_table, vma.base, vma.size);
         break;
     case VMAType::AllocatedMemoryBlock:
-        Memory::MapMemoryRegion(page_table, vma.base, vma.size,
-                                vma.backing_block->data() + vma.offset);
+        memory.MapMemoryRegion(page_table, vma.base, vma.size,
+                               vma.backing_block->data() + vma.offset);
         break;
     case VMAType::BackingMemory:
-        Memory::MapMemoryRegion(page_table, vma.base, vma.size, vma.backing_memory);
+        memory.MapMemoryRegion(page_table, vma.base, vma.size, vma.backing_memory);
         break;
     case VMAType::MMIO:
-        Memory::MapIoRegion(page_table, vma.base, vma.size, vma.mmio_handler);
+        memory.MapIoRegion(page_table, vma.base, vma.size, vma.mmio_handler);
         break;
     }
 }
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 2098f13f75..28b65ca5eb 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -17,7 +17,6 @@
 #include "core/hle/kernel/process.h"
 #include "core/hle/kernel/vm_manager.h"
 #include "core/memory.h"
-#include "core/memory_setup.h"
 #include "video_core/gpu.h"
 
 namespace Memory {
@@ -30,12 +29,125 @@ static Common::PageTable* current_page_table = nullptr;
 struct Memory::Impl {
     explicit Impl(Core::System& system_) : system{system_} {}
 
+    void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target) {
+        ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
+        ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
+        MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory);
+    }
+
+    void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size,
+                     Common::MemoryHookPointer mmio_handler) {
+        ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
+        ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
+        MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr,
+                 Common::PageType::Special);
+
+        const auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
+        const Common::SpecialRegion region{Common::SpecialRegion::Type::IODevice,
+                                           std::move(mmio_handler)};
+        page_table.special_regions.add(
+            std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
+    }
+
+    void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
+        ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
+        ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
+        MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr,
+                 Common::PageType::Unmapped);
+
+        const auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
+        page_table.special_regions.erase(interval);
+    }
+
+    void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
+                      Common::MemoryHookPointer hook) {
+        const auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
+        const Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)};
+        page_table.special_regions.add(
+            std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
+    }
+
+    void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
+                         Common::MemoryHookPointer hook) {
+        const auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
+        const Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)};
+        page_table.special_regions.subtract(
+            std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
+    }
+
+    /**
+     * Maps a region of pages as a specific type.
+     *
+     * @param page_table The page table to use to perform the mapping.
+     * @param base       The base address to begin mapping at.
+     * @param size       The total size of the range in bytes.
+     * @param memory     The memory to map.
+     * @param type       The page type to map the memory as.
+     */
+    void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* memory,
+                  Common::PageType type) {
+        LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE,
+                  (base + size) * PAGE_SIZE);
+
+        // During boot, current_page_table might not be set yet, in which case we need not flush
+        if (system.IsPoweredOn()) {
+            auto& gpu = system.GPU();
+            for (u64 i = 0; i < size; i++) {
+                const auto page = base + i;
+                if (page_table.attributes[page] == Common::PageType::RasterizerCachedMemory) {
+                    gpu.FlushAndInvalidateRegion(page << PAGE_BITS, PAGE_SIZE);
+                }
+            }
+        }
+
+        const VAddr end = base + size;
+        ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}",
+                   base + page_table.pointers.size());
+
+        std::fill(page_table.attributes.begin() + base, page_table.attributes.begin() + end, type);
+
+        if (memory == nullptr) {
+            std::fill(page_table.pointers.begin() + base, page_table.pointers.begin() + end,
+                      memory);
+        } else {
+            while (base != end) {
+                page_table.pointers[base] = memory;
+
+                base += 1;
+                memory += PAGE_SIZE;
+            }
+        }
+    }
+
     Core::System& system;
 };
 
 Memory::Memory(Core::System& system) : impl{std::make_unique<Impl>(system)} {}
 Memory::~Memory() = default;
 
+void Memory::MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target) {
+    impl->MapMemoryRegion(page_table, base, size, target);
+}
+
+void Memory::MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size,
+                         Common::MemoryHookPointer mmio_handler) {
+    impl->MapIoRegion(page_table, base, size, std::move(mmio_handler));
+}
+
+void Memory::UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
+    impl->UnmapRegion(page_table, base, size);
+}
+
+void Memory::AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
+                          Common::MemoryHookPointer hook) {
+    impl->AddDebugHook(page_table, base, size, std::move(hook));
+}
+
+void Memory::RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
+                             Common::MemoryHookPointer hook) {
+    impl->RemoveDebugHook(page_table, base, size, std::move(hook));
+}
+
 void SetCurrentPageTable(Kernel::Process& process) {
     current_page_table = &process.VMManager().page_table;
 
@@ -48,83 +160,6 @@ void SetCurrentPageTable(Kernel::Process& process) {
     system.ArmInterface(3).PageTableChanged(*current_page_table, address_space_width);
 }
 
-static void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* memory,
-                     Common::PageType type) {
-    LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE,
-              (base + size) * PAGE_SIZE);
-
-    // During boot, current_page_table might not be set yet, in which case we need not flush
-    if (Core::System::GetInstance().IsPoweredOn()) {
-        auto& gpu = Core::System::GetInstance().GPU();
-        for (u64 i = 0; i < size; i++) {
-            const auto page = base + i;
-            if (page_table.attributes[page] == Common::PageType::RasterizerCachedMemory) {
-                gpu.FlushAndInvalidateRegion(page << PAGE_BITS, PAGE_SIZE);
-            }
-        }
-    }
-
-    VAddr end = base + size;
-    ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}",
-               base + page_table.pointers.size());
-
-    std::fill(page_table.attributes.begin() + base, page_table.attributes.begin() + end, type);
-
-    if (memory == nullptr) {
-        std::fill(page_table.pointers.begin() + base, page_table.pointers.begin() + end, memory);
-    } else {
-        while (base != end) {
-            page_table.pointers[base] = memory;
-
-            base += 1;
-            memory += PAGE_SIZE;
-        }
-    }
-}
-
-void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target) {
-    ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
-    ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
-    MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory);
-}
-
-void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size,
-                 Common::MemoryHookPointer mmio_handler) {
-    ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
-    ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
-    MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, Common::PageType::Special);
-
-    auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
-    Common::SpecialRegion region{Common::SpecialRegion::Type::IODevice, std::move(mmio_handler)};
-    page_table.special_regions.add(
-        std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
-}
-
-void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
-    ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
-    ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
-    MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, Common::PageType::Unmapped);
-
-    auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
-    page_table.special_regions.erase(interval);
-}
-
-void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
-                  Common::MemoryHookPointer hook) {
-    auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
-    Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)};
-    page_table.special_regions.add(
-        std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
-}
-
-void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
-                     Common::MemoryHookPointer hook) {
-    auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
-    Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)};
-    page_table.special_regions.subtract(
-        std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
-}
-
 /**
  * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned)
  * using a VMA from the current process
diff --git a/src/core/memory.h b/src/core/memory.h
index c690df3c31..87ed3b6960 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -5,8 +5,14 @@
 #pragma once
 
 #include <cstddef>
+#include <memory>
 #include <string>
 #include "common/common_types.h"
+#include "common/memory_hook.h"
+
+namespace Common {
+struct PageTable;
+}
 
 namespace Core {
 class System;
@@ -52,6 +58,59 @@ public:
     Memory(Memory&&) = default;
     Memory& operator=(Memory&&) = default;
 
+    /**
+     * Maps an allocated buffer onto a region of the emulated process address space.
+     *
+     * @param page_table The page table of the emulated process.
+     * @param base       The address to start mapping at. Must be page-aligned.
+     * @param size       The amount of bytes to map. Must be page-aligned.
+     * @param target     Buffer with the memory backing the mapping. Must be of length at least
+     *                   `size`.
+     */
+    void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target);
+
+    /**
+     * Maps a region of the emulated process address space as a IO region.
+     *
+     * @param page_table   The page table of the emulated process.
+     * @param base         The address to start mapping at. Must be page-aligned.
+     * @param size         The amount of bytes to map. Must be page-aligned.
+     * @param mmio_handler The handler that backs the mapping.
+     */
+    void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size,
+                     Common::MemoryHookPointer mmio_handler);
+
+    /**
+     * Unmaps a region of the emulated process address space.
+     *
+     * @param page_table The page table of the emulated process.
+     * @param base       The address to begin unmapping at.
+     * @param size       The amount of bytes to unmap.
+     */
+    void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size);
+
+    /**
+     * Adds a memory hook to intercept reads and writes to given region of memory.
+     *
+     * @param page_table The page table of the emulated process
+     * @param base       The starting address to apply the hook to.
+     * @param size       The size of the memory region to apply the hook to, in bytes.
+     * @param hook       The hook to apply to the region of memory.
+     */
+    void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
+                      Common::MemoryHookPointer hook);
+
+    /**
+     * Removes a memory hook from a given range of memory.
+     *
+     * @param page_table The page table of the emulated process.
+     * @param base       The starting address to remove the hook from.
+     * @param size       The size of the memory region to remove the hook from, in bytes.
+     * @param hook       The hook to remove from the specified region of memory.
+     */
+    void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
+                         Common::MemoryHookPointer hook);
+
 private:
     struct Impl;
     std::unique_ptr<Impl> impl;
diff --git a/src/core/memory_setup.h b/src/core/memory_setup.h
deleted file mode 100644
index 5225ee8e21..0000000000
--- a/src/core/memory_setup.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2015 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "common/common_types.h"
-#include "common/memory_hook.h"
-
-namespace Common {
-struct PageTable;
-}
-
-namespace Memory {
-
-/**
- * Maps an allocated buffer onto a region of the emulated process address space.
- *
- * @param page_table The page table of the emulated process.
- * @param base The address to start mapping at. Must be page-aligned.
- * @param size The amount of bytes to map. Must be page-aligned.
- * @param target Buffer with the memory backing the mapping. Must be of length at least `size`.
- */
-void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target);
-
-/**
- * Maps a region of the emulated process address space as a IO region.
- * @param page_table The page table of the emulated process.
- * @param base The address to start mapping at. Must be page-aligned.
- * @param size The amount of bytes to map. Must be page-aligned.
- * @param mmio_handler The handler that backs the mapping.
- */
-void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size,
-                 Common::MemoryHookPointer mmio_handler);
-
-void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size);
-
-void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
-                  Common::MemoryHookPointer hook);
-void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
-                     Common::MemoryHookPointer hook);
-
-} // namespace Memory
diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp
index ac7ae3e520..17043346b8 100644
--- a/src/tests/core/arm/arm_test_common.cpp
+++ b/src/tests/core/arm/arm_test_common.cpp
@@ -8,7 +8,6 @@
 #include "core/core.h"
 #include "core/hle/kernel/process.h"
 #include "core/memory.h"
-#include "core/memory_setup.h"
 #include "tests/core/arm/arm_test_common.h"
 
 namespace ArmTests {
@@ -16,8 +15,9 @@ namespace ArmTests {
 TestEnvironment::TestEnvironment(bool mutable_memory_)
     : mutable_memory(mutable_memory_),
       test_memory(std::make_shared<TestMemory>(this)), kernel{Core::System::GetInstance()} {
-    auto process = Kernel::Process::Create(Core::System::GetInstance(), "",
-                                           Kernel::Process::ProcessType::Userland);
+    auto& system = Core::System::GetInstance();
+
+    auto process = Kernel::Process::Create(system, "", Kernel::Process::ProcessType::Userland);
     page_table = &process->VMManager().page_table;
 
     std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr);
@@ -25,15 +25,16 @@ TestEnvironment::TestEnvironment(bool mutable_memory_)
     std::fill(page_table->attributes.begin(), page_table->attributes.end(),
               Common::PageType::Unmapped);
 
-    Memory::MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory);
-    Memory::MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory);
+    system.Memory().MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory);
+    system.Memory().MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory);
 
     kernel.MakeCurrentProcess(process.get());
 }
 
 TestEnvironment::~TestEnvironment() {
-    Memory::UnmapRegion(*page_table, 0x80000000, 0x80000000);
-    Memory::UnmapRegion(*page_table, 0x00000000, 0x80000000);
+    auto& system = Core::System::GetInstance();
+    system.Memory().UnmapRegion(*page_table, 0x80000000, 0x80000000);
+    system.Memory().UnmapRegion(*page_table, 0x00000000, 0x80000000);
 }
 
 void TestEnvironment::SetMemory64(VAddr vaddr, u64 value) {

From e58748fd802dc069e90928d12d4db9ff994a869d Mon Sep 17 00:00:00 2001
From: Lioncash <mathew1800@gmail.com>
Date: Tue, 26 Nov 2019 13:46:41 -0500
Subject: [PATCH 03/13] core/memory: Migrate over address checking functions to
 the new Memory class

A fairly straightforward migration. These member functions can just be
mostly moved verbatim with minor changes. We already have the necessary
plumbing in places that they're used.

IsKernelVirtualAddress() can remain a non-member function, since it
doesn't rely on class state in any form.
---
 src/core/gdbstub/gdbstub.cpp            | 18 +++++----
 src/core/hle/kernel/address_arbiter.cpp |  8 ++--
 src/core/hle/kernel/svc.cpp             |  4 +-
 src/core/hle/kernel/thread.cpp          |  4 +-
 src/core/memory.cpp                     | 51 +++++++++++++++----------
 src/core/memory.h                       | 24 ++++++++++--
 6 files changed, 70 insertions(+), 39 deletions(-)

diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index 54ed680db9..78e44f3bdb 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -969,7 +969,8 @@ static void ReadMemory() {
         SendReply("E01");
     }
 
-    if (!Memory::IsValidVirtualAddress(addr)) {
+    const auto& memory = Core::System::GetInstance().Memory();
+    if (!memory.IsValidVirtualAddress(addr)) {
         return SendReply("E00");
     }
 
@@ -984,22 +985,23 @@ static void ReadMemory() {
 /// Modify location in memory with data received from the gdb client.
 static void WriteMemory() {
     auto start_offset = command_buffer + 1;
-    auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
-    VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
+    const auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
+    const VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
 
     start_offset = addr_pos + 1;
-    auto len_pos = std::find(start_offset, command_buffer + command_length, ':');
-    u64 len = HexToLong(start_offset, static_cast<u64>(len_pos - start_offset));
+    const auto len_pos = std::find(start_offset, command_buffer + command_length, ':');
+    const u64 len = HexToLong(start_offset, static_cast<u64>(len_pos - start_offset));
 
-    if (!Memory::IsValidVirtualAddress(addr)) {
+    auto& system = Core::System::GetInstance();
+    const auto& memory = system.Memory();
+    if (!memory.IsValidVirtualAddress(addr)) {
         return SendReply("E00");
     }
 
     std::vector<u8> data(len);
-
     GdbHexToMem(data.data(), len_pos + 1, len);
     Memory::WriteBlock(addr, data.data(), len);
-    Core::System::GetInstance().InvalidateCpuInstructionCaches();
+    system.InvalidateCpuInstructionCaches();
     SendReply("OK");
 }
 
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index 4859954cbd..7f9a559d2f 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -68,7 +68,7 @@ ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) {
 ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value,
                                                               s32 num_to_wake) {
     // Ensure that we can write to the address.
-    if (!Memory::IsValidVirtualAddress(address)) {
+    if (!system.Memory().IsValidVirtualAddress(address)) {
         return ERR_INVALID_ADDRESS_STATE;
     }
 
@@ -83,7 +83,7 @@ ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32
 ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
                                                                          s32 num_to_wake) {
     // Ensure that we can write to the address.
-    if (!Memory::IsValidVirtualAddress(address)) {
+    if (!system.Memory().IsValidVirtualAddress(address)) {
         return ERR_INVALID_ADDRESS_STATE;
     }
 
@@ -135,7 +135,7 @@ ResultCode AddressArbiter::WaitForAddress(VAddr address, ArbitrationType type, s
 ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout,
                                                     bool should_decrement) {
     // Ensure that we can read the address.
-    if (!Memory::IsValidVirtualAddress(address)) {
+    if (!system.Memory().IsValidVirtualAddress(address)) {
         return ERR_INVALID_ADDRESS_STATE;
     }
 
@@ -158,7 +158,7 @@ ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s6
 
 ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
     // Ensure that we can read the address.
-    if (!Memory::IsValidVirtualAddress(address)) {
+    if (!system.Memory().IsValidVirtualAddress(address)) {
         return ERR_INVALID_ADDRESS_STATE;
     }
     // Only wait for the address if equal.
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 9928b3a26d..eddafaf601 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -332,7 +332,7 @@ static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_ad
 /// Connect to an OS service given the port name, returns the handle to the port to out
 static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle,
                                      VAddr port_name_address) {
-    if (!Memory::IsValidVirtualAddress(port_name_address)) {
+    if (!system.Memory().IsValidVirtualAddress(port_name_address)) {
         LOG_ERROR(Kernel_SVC,
                   "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}",
                   port_name_address);
@@ -452,7 +452,7 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr
     LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}",
               handles_address, handle_count, nano_seconds);
 
-    if (!Memory::IsValidVirtualAddress(handles_address)) {
+    if (!system.Memory().IsValidVirtualAddress(handles_address)) {
         LOG_ERROR(Kernel_SVC,
                   "Handle address is not a valid virtual address, handle_address=0x{:016X}",
                   handles_address);
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 735019d962..e84e5ce0d0 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -162,13 +162,13 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::strin
         return ERR_INVALID_PROCESSOR_ID;
     }
 
-    if (!Memory::IsValidVirtualAddress(owner_process, entry_point)) {
+    auto& system = Core::System::GetInstance();
+    if (!system.Memory().IsValidVirtualAddress(owner_process, entry_point)) {
         LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point);
         // TODO (bunnei): Find the correct error code to use here
         return RESULT_UNKNOWN;
     }
 
-    auto& system = Core::System::GetInstance();
     std::shared_ptr<Thread> thread = std::make_shared<Thread>(kernel);
 
     thread->thread_id = kernel.CreateNewThreadID();
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 28b65ca5eb..4c13ea1e7a 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -75,6 +75,29 @@ struct Memory::Impl {
             std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
     }
 
+    bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) const {
+        const auto& page_table = process.VMManager().page_table;
+
+        const u8* const page_pointer = page_table.pointers[vaddr >> PAGE_BITS];
+        if (page_pointer != nullptr) {
+            return true;
+        }
+
+        if (page_table.attributes[vaddr >> PAGE_BITS] == Common::PageType::RasterizerCachedMemory) {
+            return true;
+        }
+
+        if (page_table.attributes[vaddr >> PAGE_BITS] != Common::PageType::Special) {
+            return false;
+        }
+
+        return false;
+    }
+
+    bool IsValidVirtualAddress(VAddr vaddr) const {
+        return IsValidVirtualAddress(*system.CurrentProcess(), vaddr);
+    }
+
     /**
      * Maps a region of pages as a specific type.
      *
@@ -148,6 +171,14 @@ void Memory::RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size
     impl->RemoveDebugHook(page_table, base, size, std::move(hook));
 }
 
+bool Memory::IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) const {
+    return impl->IsValidVirtualAddress(process, vaddr);
+}
+
+bool Memory::IsValidVirtualAddress(const VAddr vaddr) const {
+    return impl->IsValidVirtualAddress(vaddr);
+}
+
 void SetCurrentPageTable(Kernel::Process& process) {
     current_page_table = &process.VMManager().page_table;
 
@@ -256,26 +287,6 @@ void Write(const VAddr vaddr, const T data) {
     }
 }
 
-bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) {
-    const auto& page_table = process.VMManager().page_table;
-
-    const u8* page_pointer = page_table.pointers[vaddr >> PAGE_BITS];
-    if (page_pointer)
-        return true;
-
-    if (page_table.attributes[vaddr >> PAGE_BITS] == Common::PageType::RasterizerCachedMemory)
-        return true;
-
-    if (page_table.attributes[vaddr >> PAGE_BITS] != Common::PageType::Special)
-        return false;
-
-    return false;
-}
-
-bool IsValidVirtualAddress(const VAddr vaddr) {
-    return IsValidVirtualAddress(*Core::System::GetInstance().CurrentProcess(), vaddr);
-}
-
 bool IsKernelVirtualAddress(const VAddr vaddr) {
     return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END;
 }
diff --git a/src/core/memory.h b/src/core/memory.h
index 87ed3b6960..cacf4fb1a6 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -111,6 +111,27 @@ public:
     void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
                          Common::MemoryHookPointer hook);
 
+    /**
+     * Checks whether or not the supplied address is a valid virtual
+     * address for the given process.
+     *
+     * @param process The emulated process to check the address against.
+     * @param vaddr   The virtual address to check the validity of.
+     *
+     * @returns True if the given virtual address is valid, false otherwise.
+     */
+    bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr) const;
+
+    /**
+     * Checks whether or not the supplied address is a valid virtual
+     * address for the current process.
+     *
+     * @param vaddr The virtual address to check the validity of.
+     *
+     * @returns True if the given virtual address is valid, false otherwise.
+     */
+    bool IsValidVirtualAddress(VAddr vaddr) const;
+
 private:
     struct Impl;
     std::unique_ptr<Impl> impl;
@@ -120,9 +141,6 @@ private:
 /// the given process instance.
 void SetCurrentPageTable(Kernel::Process& process);
 
-/// Determines if the given VAddr is valid for the specified process.
-bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr);
-bool IsValidVirtualAddress(VAddr vaddr);
 /// Determines if the given VAddr is a kernel address
 bool IsKernelVirtualAddress(VAddr vaddr);
 

From fc7d0a17b6ec7dfc44a56f3e4a8bd97108f1c596 Mon Sep 17 00:00:00 2001
From: Lioncash <mathew1800@gmail.com>
Date: Tue, 26 Nov 2019 13:58:03 -0500
Subject: [PATCH 04/13] core/memory: Move memory read/write implementation
 functions into an anonymous namespace

These will eventually be migrated into the main Memory class, but for
now, we put them in an anonymous namespace, so that the other functions
that use them, can be migrated over separately.
---
 src/core/memory.cpp | 195 ++++++++++++++++++++++----------------------
 1 file changed, 98 insertions(+), 97 deletions(-)

diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 4c13ea1e7a..0170336139 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -20,8 +20,105 @@
 #include "video_core/gpu.h"
 
 namespace Memory {
+namespace {
+Common::PageTable* current_page_table = nullptr;
 
-static Common::PageTable* current_page_table = nullptr;
+/**
+ * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned)
+ * using a VMA from the current process
+ */
+u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) {
+    const auto& vm_manager = process.VMManager();
+
+    const auto it = vm_manager.FindVMA(vaddr);
+    DEBUG_ASSERT(vm_manager.IsValidHandle(it));
+
+    u8* direct_pointer = nullptr;
+    const auto& vma = it->second;
+    switch (vma.type) {
+    case Kernel::VMAType::AllocatedMemoryBlock:
+        direct_pointer = vma.backing_block->data() + vma.offset;
+        break;
+    case Kernel::VMAType::BackingMemory:
+        direct_pointer = vma.backing_memory;
+        break;
+    case Kernel::VMAType::Free:
+        return nullptr;
+    default:
+        UNREACHABLE();
+    }
+
+    return direct_pointer + (vaddr - vma.base);
+}
+
+/**
+ * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned)
+ * using a VMA from the current process.
+ */
+u8* GetPointerFromVMA(VAddr vaddr) {
+    return ::Memory::GetPointerFromVMA(*Core::System::GetInstance().CurrentProcess(), vaddr);
+}
+
+template <typename T>
+T Read(const VAddr vaddr) {
+    const u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
+    if (page_pointer != nullptr) {
+        // NOTE: Avoid adding any extra logic to this fast-path block
+        T value;
+        std::memcpy(&value, &page_pointer[vaddr & PAGE_MASK], sizeof(T));
+        return value;
+    }
+
+    const Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
+    switch (type) {
+    case Common::PageType::Unmapped:
+        LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr);
+        return 0;
+    case Common::PageType::Memory:
+        ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
+        break;
+    case Common::PageType::RasterizerCachedMemory: {
+        const u8* const host_ptr{GetPointerFromVMA(vaddr)};
+        Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), sizeof(T));
+        T value;
+        std::memcpy(&value, host_ptr, sizeof(T));
+        return value;
+    }
+    default:
+        UNREACHABLE();
+    }
+    return {};
+}
+
+template <typename T>
+void Write(const VAddr vaddr, const T data) {
+    u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
+    if (page_pointer != nullptr) {
+        // NOTE: Avoid adding any extra logic to this fast-path block
+        std::memcpy(&page_pointer[vaddr & PAGE_MASK], &data, sizeof(T));
+        return;
+    }
+
+    Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
+    switch (type) {
+    case Common::PageType::Unmapped:
+        LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
+                  static_cast<u32>(data), vaddr);
+        return;
+    case Common::PageType::Memory:
+        ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
+        break;
+    case Common::PageType::RasterizerCachedMemory: {
+        u8* const host_ptr{GetPointerFromVMA(vaddr)};
+        Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), sizeof(T));
+        std::memcpy(host_ptr, &data, sizeof(T));
+        break;
+    }
+    default:
+        UNREACHABLE();
+    }
+}
+} // Anonymous namespace
 
 // Implementation class used to keep the specifics of the memory subsystem hidden
 // from outside classes. This also allows modification to the internals of the memory
@@ -191,102 +288,6 @@ void SetCurrentPageTable(Kernel::Process& process) {
     system.ArmInterface(3).PageTableChanged(*current_page_table, address_space_width);
 }
 
-/**
- * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned)
- * using a VMA from the current process
- */
-static u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) {
-    const auto& vm_manager = process.VMManager();
-
-    const auto it = vm_manager.FindVMA(vaddr);
-    DEBUG_ASSERT(vm_manager.IsValidHandle(it));
-
-    u8* direct_pointer = nullptr;
-    const auto& vma = it->second;
-    switch (vma.type) {
-    case Kernel::VMAType::AllocatedMemoryBlock:
-        direct_pointer = vma.backing_block->data() + vma.offset;
-        break;
-    case Kernel::VMAType::BackingMemory:
-        direct_pointer = vma.backing_memory;
-        break;
-    case Kernel::VMAType::Free:
-        return nullptr;
-    default:
-        UNREACHABLE();
-    }
-
-    return direct_pointer + (vaddr - vma.base);
-}
-
-/**
- * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned)
- * using a VMA from the current process.
- */
-static u8* GetPointerFromVMA(VAddr vaddr) {
-    return GetPointerFromVMA(*Core::System::GetInstance().CurrentProcess(), vaddr);
-}
-
-template <typename T>
-T Read(const VAddr vaddr) {
-    const u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
-    if (page_pointer) {
-        // NOTE: Avoid adding any extra logic to this fast-path block
-        T value;
-        std::memcpy(&value, &page_pointer[vaddr & PAGE_MASK], sizeof(T));
-        return value;
-    }
-
-    Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
-    switch (type) {
-    case Common::PageType::Unmapped:
-        LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr);
-        return 0;
-    case Common::PageType::Memory:
-        ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
-        break;
-    case Common::PageType::RasterizerCachedMemory: {
-        auto host_ptr{GetPointerFromVMA(vaddr)};
-        Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), sizeof(T));
-        T value;
-        std::memcpy(&value, host_ptr, sizeof(T));
-        return value;
-    }
-    default:
-        UNREACHABLE();
-    }
-    return {};
-}
-
-template <typename T>
-void Write(const VAddr vaddr, const T data) {
-    u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
-    if (page_pointer) {
-        // NOTE: Avoid adding any extra logic to this fast-path block
-        std::memcpy(&page_pointer[vaddr & PAGE_MASK], &data, sizeof(T));
-        return;
-    }
-
-    Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
-    switch (type) {
-    case Common::PageType::Unmapped:
-        LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
-                  static_cast<u32>(data), vaddr);
-        return;
-    case Common::PageType::Memory:
-        ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
-        break;
-    case Common::PageType::RasterizerCachedMemory: {
-        auto host_ptr{GetPointerFromVMA(vaddr)};
-        Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), sizeof(T));
-        std::memcpy(host_ptr, &data, sizeof(T));
-        break;
-    }
-    default:
-        UNREACHABLE();
-    }
-}
-
 bool IsKernelVirtualAddress(const VAddr vaddr) {
     return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END;
 }

From 536fc7f0ea77e08d68c760f387c307d258804e3b Mon Sep 17 00:00:00 2001
From: Lioncash <mathew1800@gmail.com>
Date: Tue, 26 Nov 2019 14:10:49 -0500
Subject: [PATCH 05/13] core: Prepare various classes for memory read/write
 migration

Amends a few interfaces to be able to handle the migration over to the
new Memory class by passing the class by reference as a function
parameter where necessary.

Notably, within the filesystem services, this eliminates two ReadBlock()
calls by using the helper functions of HLERequestContext to do that for
us.
---
 src/audio_core/audio_renderer.cpp             | 24 ++++++++++---------
 src/audio_core/audio_renderer.h               | 10 ++++++--
 src/core/arm/arm_interface.cpp                |  3 +--
 src/core/arm/arm_interface.h                  |  8 ++++++-
 src/core/arm/dynarmic/arm_dynarmic.cpp        |  8 ++++---
 src/core/arm/dynarmic/arm_dynarmic.h          |  1 -
 src/core/arm/unicorn/arm_unicorn.cpp          |  2 +-
 src/core/arm/unicorn/arm_unicorn.h            |  1 -
 src/core/hle/kernel/client_session.cpp        |  4 ++--
 src/core/hle/kernel/client_session.h          |  6 ++++-
 src/core/hle/kernel/server_session.cpp        |  3 ++-
 src/core/hle/kernel/server_session.h          |  9 ++++++-
 src/core/hle/kernel/svc.cpp                   |  2 +-
 src/core/hle/service/audio/audren_u.cpp       |  5 ++--
 src/core/hle/service/filesystem/fsp_srv.cpp   |  7 ++----
 src/core/hle/service/lm/lm.cpp                | 13 ++++++----
 src/core/reporter.cpp                         | 14 +++++------
 src/core/tools/freezer.cpp                    | 14 +++++------
 src/core/tools/freezer.h                      |  7 +++++-
 src/video_core/rasterizer_accelerated.cpp     |  3 ++-
 src/video_core/rasterizer_accelerated.h       |  9 +++++--
 .../renderer_opengl/gl_rasterizer.cpp         |  5 ++--
 .../renderer_vulkan/vk_buffer_cache.cpp       |  4 +++-
 .../renderer_vulkan/vk_buffer_cache.h         |  7 +++++-
 24 files changed, 108 insertions(+), 61 deletions(-)

diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
index 6b0167acde..12e2b79019 100644
--- a/src/audio_core/audio_renderer.cpp
+++ b/src/audio_core/audio_renderer.cpp
@@ -36,9 +36,9 @@ public:
     }
 
     void SetWaveIndex(std::size_t index);
-    std::vector<s16> DequeueSamples(std::size_t sample_count);
+    std::vector<s16> DequeueSamples(std::size_t sample_count, Memory::Memory& memory);
     void UpdateState();
-    void RefreshBuffer();
+    void RefreshBuffer(Memory::Memory& memory);
 
 private:
     bool is_in_use{};
@@ -66,17 +66,18 @@ public:
         return info;
     }
 
-    void UpdateState();
+    void UpdateState(Memory::Memory& memory);
 
 private:
     EffectOutStatus out_status{};
     EffectInStatus info{};
 };
-AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, AudioRendererParameter params,
+AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Memory::Memory& memory_,
+                             AudioRendererParameter params,
                              std::shared_ptr<Kernel::WritableEvent> buffer_event,
                              std::size_t instance_number)
     : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count),
-      effects(params.effect_count) {
+      effects(params.effect_count), memory{memory_} {
 
     audio_out = std::make_unique<AudioCore::AudioOut>();
     stream = audio_out->OpenStream(core_timing, STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS,
@@ -162,7 +163,7 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
     }
 
     for (auto& effect : effects) {
-        effect.UpdateState();
+        effect.UpdateState(memory);
     }
 
     // Release previous buffers and queue next ones for playback
@@ -206,13 +207,14 @@ void AudioRenderer::VoiceState::SetWaveIndex(std::size_t index) {
     is_refresh_pending = true;
 }
 
-std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(std::size_t sample_count) {
+std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(std::size_t sample_count,
+                                                           Memory::Memory& memory) {
     if (!IsPlaying()) {
         return {};
     }
 
     if (is_refresh_pending) {
-        RefreshBuffer();
+        RefreshBuffer(memory);
     }
 
     const std::size_t max_size{samples.size() - offset};
@@ -256,7 +258,7 @@ void AudioRenderer::VoiceState::UpdateState() {
     is_in_use = info.is_in_use;
 }
 
-void AudioRenderer::VoiceState::RefreshBuffer() {
+void AudioRenderer::VoiceState::RefreshBuffer(Memory::Memory& memory) {
     std::vector<s16> new_samples(info.wave_buffer[wave_index].buffer_sz / sizeof(s16));
     Memory::ReadBlock(info.wave_buffer[wave_index].buffer_addr, new_samples.data(),
                       info.wave_buffer[wave_index].buffer_sz);
@@ -307,7 +309,7 @@ void AudioRenderer::VoiceState::RefreshBuffer() {
     is_refresh_pending = false;
 }
 
-void AudioRenderer::EffectState::UpdateState() {
+void AudioRenderer::EffectState::UpdateState(Memory::Memory& memory) {
     if (info.is_new) {
         out_status.state = EffectStatus::New;
     } else {
@@ -340,7 +342,7 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
         std::size_t offset{};
         s64 samples_remaining{BUFFER_SIZE};
         while (samples_remaining > 0) {
-            const std::vector<s16> samples{voice.DequeueSamples(samples_remaining)};
+            const std::vector<s16> samples{voice.DequeueSamples(samples_remaining, memory)};
 
             if (samples.empty()) {
                 break;
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h
index abed224bb4..be1b019f17 100644
--- a/src/audio_core/audio_renderer.h
+++ b/src/audio_core/audio_renderer.h
@@ -22,6 +22,10 @@ namespace Kernel {
 class WritableEvent;
 }
 
+namespace Memory {
+class Memory;
+}
+
 namespace AudioCore {
 
 class AudioOut;
@@ -217,7 +221,8 @@ static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size
 
 class AudioRenderer {
 public:
-    AudioRenderer(Core::Timing::CoreTiming& core_timing, AudioRendererParameter params,
+    AudioRenderer(Core::Timing::CoreTiming& core_timing, Memory::Memory& memory_,
+                  AudioRendererParameter params,
                   std::shared_ptr<Kernel::WritableEvent> buffer_event, std::size_t instance_number);
     ~AudioRenderer();
 
@@ -238,7 +243,8 @@ private:
     std::vector<VoiceState> voices;
     std::vector<EffectState> effects;
     std::unique_ptr<AudioOut> audio_out;
-    AudioCore::StreamPtr stream;
+    StreamPtr stream;
+    Memory::Memory& memory;
 };
 
 } // namespace AudioCore
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 372612c9b3..dea192869f 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -13,7 +13,6 @@
 #include "core/memory.h"
 
 namespace Core {
-
 namespace {
 
 constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
@@ -156,7 +155,7 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
     }
 
     std::map<VAddr, std::string> modules;
-    auto& loader{System::GetInstance().GetAppLoader()};
+    auto& loader{system.GetAppLoader()};
     if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) {
         return {};
     }
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 45e94e6257..47b964eb70 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -17,11 +17,13 @@ enum class VMAPermission : u8;
 }
 
 namespace Core {
+class System;
 
 /// Generic ARMv8 CPU interface
 class ARM_Interface : NonCopyable {
 public:
-    virtual ~ARM_Interface() {}
+    explicit ARM_Interface(System& system_) : system{system_} {}
+    virtual ~ARM_Interface() = default;
 
     struct ThreadContext {
         std::array<u64, 31> cpu_registers;
@@ -163,6 +165,10 @@ public:
     /// fp+0 : pointer to previous frame record
     /// fp+8 : value of lr for frame
     void LogBacktrace() const;
+
+protected:
+    /// System context that this ARM interface is running under.
+    System& system;
 };
 
 } // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index a0705b2b89..2b396f1d67 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -28,6 +28,7 @@ public:
     explicit ARM_Dynarmic_Callbacks(ARM_Dynarmic& parent) : parent(parent) {}
 
     u8 MemoryRead8(u64 vaddr) override {
+        auto& s = parent.system;
         return Memory::Read8(vaddr);
     }
     u16 MemoryRead16(u64 vaddr) override {
@@ -171,9 +172,10 @@ void ARM_Dynarmic::Step() {
 
 ARM_Dynarmic::ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor,
                            std::size_t core_index)
-    : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{system},
-      core_index{core_index}, system{system},
-      exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
+    : ARM_Interface{system},
+      cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{system},
+      core_index{core_index}, exclusive_monitor{
+                                  dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
 
 ARM_Dynarmic::~ARM_Dynarmic() = default;
 
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index 504d46c689..d08de475f2 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -58,7 +58,6 @@ private:
     ARM_Unicorn inner_unicorn;
 
     std::size_t core_index;
-    System& system;
     DynarmicExclusiveMonitor& exclusive_monitor;
 };
 
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp
index 9698172dbf..48182c99ae 100644
--- a/src/core/arm/unicorn/arm_unicorn.cpp
+++ b/src/core/arm/unicorn/arm_unicorn.cpp
@@ -60,7 +60,7 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si
     return false;
 }
 
-ARM_Unicorn::ARM_Unicorn(System& system) : system{system} {
+ARM_Unicorn::ARM_Unicorn(System& system) : ARM_Interface{system} {
     CHECKED(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc));
 
     auto fpv = 3 << 20;
diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h
index b39426ea0b..3c5b155f93 100644
--- a/src/core/arm/unicorn/arm_unicorn.h
+++ b/src/core/arm/unicorn/arm_unicorn.h
@@ -45,7 +45,6 @@ private:
     static void InterruptHook(uc_engine* uc, u32 int_no, void* user_data);
 
     uc_engine* uc{};
-    System& system;
     GDBStub::BreakpointAddress last_bkpt{};
     bool last_bkpt_hit = false;
 };
diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp
index 5995a65569..9849dbe914 100644
--- a/src/core/hle/kernel/client_session.cpp
+++ b/src/core/hle/kernel/client_session.cpp
@@ -21,10 +21,10 @@ ClientSession::~ClientSession() {
     }
 }
 
-ResultCode ClientSession::SendSyncRequest(Thread* thread) {
+ResultCode ClientSession::SendSyncRequest(Thread* thread, Memory::Memory& memory) {
     // Signal the server session that new data is available
     if (auto server = parent->server.lock()) {
-        return server->HandleSyncRequest(SharedFrom(thread));
+        return server->HandleSyncRequest(SharedFrom(thread), memory);
     }
 
     return ERR_SESSION_CLOSED_BY_REMOTE;
diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h
index 5ae41db29c..484dd7bc90 100644
--- a/src/core/hle/kernel/client_session.h
+++ b/src/core/hle/kernel/client_session.h
@@ -10,6 +10,10 @@
 
 union ResultCode;
 
+namespace Memory {
+class Memory;
+}
+
 namespace Kernel {
 
 class KernelCore;
@@ -37,7 +41,7 @@ public:
         return HANDLE_TYPE;
     }
 
-    ResultCode SendSyncRequest(Thread* thread);
+    ResultCode SendSyncRequest(Thread* thread, Memory::Memory& memory);
 
 private:
     /// The parent session, which links to the server endpoint.
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index c7db21eb21..57878514d2 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -127,7 +127,8 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con
     return RESULT_SUCCESS;
 }
 
-ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread) {
+ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread,
+                                            Memory::Memory& memory) {
     // The ServerSession received a sync request, this means that there's new data available
     // from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or
     // similar.
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h
index 8a65647b6c..641709a45a 100644
--- a/src/core/hle/kernel/server_session.h
+++ b/src/core/hle/kernel/server_session.h
@@ -13,6 +13,10 @@
 #include "core/hle/kernel/wait_object.h"
 #include "core/hle/result.h"
 
+namespace Memory {
+class Memory;
+}
+
 namespace Kernel {
 
 class ClientPort;
@@ -85,10 +89,13 @@ public:
 
     /**
      * Handle a sync request from the emulated application.
+     *
      * @param thread Thread that initiated the request.
+     * @param memory Memory context to handle the sync request under.
+     *
      * @returns ResultCode from the operation.
      */
-    ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread);
+    ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory);
 
     bool ShouldWait(const Thread* thread) const override;
 
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index eddafaf601..68bff11ec2 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -383,7 +383,7 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
 
     // TODO(Subv): svcSendSyncRequest should put the caller thread to sleep while the server
     // responds and cause a reschedule.
-    return session->SendSyncRequest(system.CurrentScheduler().GetCurrentThread());
+    return session->SendSyncRequest(system.CurrentScheduler().GetCurrentThread(), system.Memory());
 }
 
 /// Get the ID for the specified thread.
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 4ea7ade6eb..82a5dbf141 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -49,8 +49,9 @@ public:
 
         system_event =
             Kernel::WritableEvent::CreateEventPair(system.Kernel(), "IAudioRenderer:SystemEvent");
-        renderer = std::make_unique<AudioCore::AudioRenderer>(
-            system.CoreTiming(), audren_params, system_event.writable, instance_number);
+        renderer = std::make_unique<AudioCore::AudioRenderer>(system.CoreTiming(), system.Memory(),
+                                                              audren_params, system_event.writable,
+                                                              instance_number);
     }
 
 private:
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index 5874ed6bda..89e1957f9c 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -391,13 +391,10 @@ public:
     }
 
     void RenameFile(Kernel::HLERequestContext& ctx) {
-        std::vector<u8> buffer;
-        buffer.resize(ctx.BufferDescriptorX()[0].Size());
-        Memory::ReadBlock(ctx.BufferDescriptorX()[0].Address(), buffer.data(), buffer.size());
+        std::vector<u8> buffer = ctx.ReadBuffer(0);
         const std::string src_name = Common::StringFromBuffer(buffer);
 
-        buffer.resize(ctx.BufferDescriptorX()[1].Size());
-        Memory::ReadBlock(ctx.BufferDescriptorX()[1].Address(), buffer.data(), buffer.size());
+        buffer = ctx.ReadBuffer(1);
         const std::string dst_name = Common::StringFromBuffer(buffer);
 
         LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", src_name, dst_name);
diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp
index 435f2d2865..74ecaef1b7 100644
--- a/src/core/hle/service/lm/lm.cpp
+++ b/src/core/hle/service/lm/lm.cpp
@@ -17,7 +17,8 @@ namespace Service::LM {
 
 class ILogger final : public ServiceFramework<ILogger> {
 public:
-    ILogger(Manager& manager) : ServiceFramework("ILogger"), manager(manager) {
+    explicit ILogger(Manager& manager_, Memory::Memory& memory_)
+        : ServiceFramework("ILogger"), manager{manager_}, memory{memory_} {
         static const FunctionInfo functions[] = {
             {0, &ILogger::Log, "Log"},
             {1, &ILogger::SetDestination, "SetDestination"},
@@ -74,11 +75,13 @@ private:
     }
 
     Manager& manager;
+    Memory::Memory& memory;
 };
 
 class LM final : public ServiceFramework<LM> {
 public:
-    explicit LM(Manager& manager) : ServiceFramework{"lm"}, manager(manager) {
+    explicit LM(Manager& manager_, Memory::Memory& memory_)
+        : ServiceFramework{"lm"}, manager{manager_}, memory{memory_} {
         // clang-format off
         static const FunctionInfo functions[] = {
             {0, &LM::OpenLogger, "OpenLogger"},
@@ -94,14 +97,16 @@ private:
 
         IPC::ResponseBuilder rb{ctx, 2, 0, 1};
         rb.Push(RESULT_SUCCESS);
-        rb.PushIpcInterface<ILogger>(manager);
+        rb.PushIpcInterface<ILogger>(manager, memory);
     }
 
     Manager& manager;
+    Memory::Memory& memory;
 };
 
 void InstallInterfaces(Core::System& system) {
-    std::make_shared<LM>(system.GetLogManager())->InstallAsService(system.ServiceManager());
+    std::make_shared<LM>(system.GetLogManager(), system.Memory())
+        ->InstallAsService(system.ServiceManager());
 }
 
 } // namespace Service::LM
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp
index 6f4af77fd4..af0988d623 100644
--- a/src/core/reporter.cpp
+++ b/src/core/reporter.cpp
@@ -147,7 +147,7 @@ json GetFullDataAuto(const std::string& timestamp, u64 title_id, Core::System& s
 }
 
 template <bool read_value, typename DescriptorType>
-json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& buffer) {
+json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& buffer, Memory::Memory& memory) {
     auto buffer_out = json::array();
     for (const auto& desc : buffer) {
         auto entry = json{
@@ -167,7 +167,7 @@ json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& buffer) {
     return buffer_out;
 }
 
-json GetHLERequestContextData(Kernel::HLERequestContext& ctx) {
+json GetHLERequestContextData(Kernel::HLERequestContext& ctx, Memory::Memory& memory) {
     json out;
 
     auto cmd_buf = json::array();
@@ -177,10 +177,10 @@ json GetHLERequestContextData(Kernel::HLERequestContext& ctx) {
 
     out["command_buffer"] = std::move(cmd_buf);
 
-    out["buffer_descriptor_a"] = GetHLEBufferDescriptorData<true>(ctx.BufferDescriptorA());
-    out["buffer_descriptor_b"] = GetHLEBufferDescriptorData<false>(ctx.BufferDescriptorB());
-    out["buffer_descriptor_c"] = GetHLEBufferDescriptorData<false>(ctx.BufferDescriptorC());
-    out["buffer_descriptor_x"] = GetHLEBufferDescriptorData<true>(ctx.BufferDescriptorX());
+    out["buffer_descriptor_a"] = GetHLEBufferDescriptorData<true>(ctx.BufferDescriptorA(), memory);
+    out["buffer_descriptor_b"] = GetHLEBufferDescriptorData<false>(ctx.BufferDescriptorB(), memory);
+    out["buffer_descriptor_c"] = GetHLEBufferDescriptorData<false>(ctx.BufferDescriptorC(), memory);
+    out["buffer_descriptor_x"] = GetHLEBufferDescriptorData<true>(ctx.BufferDescriptorX(), memory);
 
     return out;
 }
@@ -259,7 +259,7 @@ void Reporter::SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u
     const auto title_id = system.CurrentProcess()->GetTitleID();
     auto out = GetFullDataAuto(timestamp, title_id, system);
 
-    auto function_out = GetHLERequestContextData(ctx);
+    auto function_out = GetHLERequestContextData(ctx, system.Memory());
     function_out["command_id"] = command_id;
     function_out["function_name"] = name;
     function_out["service_name"] = service_name;
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp
index 19b531ecb0..c7f42388f1 100644
--- a/src/core/tools/freezer.cpp
+++ b/src/core/tools/freezer.cpp
@@ -11,12 +11,11 @@
 #include "core/tools/freezer.h"
 
 namespace Tools {
-
 namespace {
 
 constexpr s64 MEMORY_FREEZER_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60);
 
-u64 MemoryReadWidth(u32 width, VAddr addr) {
+u64 MemoryReadWidth(Memory::Memory& memory, u32 width, VAddr addr) {
     switch (width) {
     case 1:
         return Memory::Read8(addr);
@@ -32,7 +31,7 @@ u64 MemoryReadWidth(u32 width, VAddr addr) {
     }
 }
 
-void MemoryWriteWidth(u32 width, VAddr addr, u64 value) {
+void MemoryWriteWidth(Memory::Memory& memory, u32 width, VAddr addr, u64 value) {
     switch (width) {
     case 1:
         Memory::Write8(addr, static_cast<u8>(value));
@@ -53,7 +52,8 @@ void MemoryWriteWidth(u32 width, VAddr addr, u64 value) {
 
 } // Anonymous namespace
 
-Freezer::Freezer(Core::Timing::CoreTiming& core_timing) : core_timing(core_timing) {
+Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Memory::Memory& memory_)
+    : core_timing{core_timing_}, memory{memory_} {
     event = Core::Timing::CreateEvent(
         "MemoryFreezer::FrameCallback",
         [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); });
@@ -89,7 +89,7 @@ void Freezer::Clear() {
 u64 Freezer::Freeze(VAddr address, u32 width) {
     std::lock_guard lock{entries_mutex};
 
-    const auto current_value = MemoryReadWidth(width, address);
+    const auto current_value = MemoryReadWidth(memory, width, address);
     entries.push_back({address, width, current_value});
 
     LOG_DEBUG(Common_Memory,
@@ -169,7 +169,7 @@ void Freezer::FrameCallback(u64 userdata, s64 cycles_late) {
         LOG_DEBUG(Common_Memory,
                   "Enforcing memory freeze at address={:016X}, value={:016X}, width={:02X}",
                   entry.address, entry.value, entry.width);
-        MemoryWriteWidth(entry.width, entry.address, entry.value);
+        MemoryWriteWidth(memory, entry.width, entry.address, entry.value);
     }
 
     core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS - cycles_late, event);
@@ -181,7 +181,7 @@ void Freezer::FillEntryReads() {
     LOG_DEBUG(Common_Memory, "Updating memory freeze entries to current values.");
 
     for (auto& entry : entries) {
-        entry.value = MemoryReadWidth(entry.width, entry.address);
+        entry.value = MemoryReadWidth(memory, entry.width, entry.address);
     }
 }
 
diff --git a/src/core/tools/freezer.h b/src/core/tools/freezer.h
index 90b1a885c1..916339c6c8 100644
--- a/src/core/tools/freezer.h
+++ b/src/core/tools/freezer.h
@@ -16,6 +16,10 @@ class CoreTiming;
 struct EventType;
 } // namespace Core::Timing
 
+namespace Memory {
+class Memory;
+}
+
 namespace Tools {
 
 /**
@@ -34,7 +38,7 @@ public:
         u64 value;
     };
 
-    explicit Freezer(Core::Timing::CoreTiming& core_timing);
+    explicit Freezer(Core::Timing::CoreTiming& core_timing_, Memory::Memory& memory_);
     ~Freezer();
 
     // Enables or disables the entire memory freezer.
@@ -78,6 +82,7 @@ private:
 
     std::shared_ptr<Core::Timing::EventType> event;
     Core::Timing::CoreTiming& core_timing;
+    Memory::Memory& memory;
 };
 
 } // namespace Tools
diff --git a/src/video_core/rasterizer_accelerated.cpp b/src/video_core/rasterizer_accelerated.cpp
index b230dcc182..f3dbadebcf 100644
--- a/src/video_core/rasterizer_accelerated.cpp
+++ b/src/video_core/rasterizer_accelerated.cpp
@@ -22,7 +22,8 @@ constexpr auto RangeFromInterval(Map& map, const Interval& interval) {
 
 } // Anonymous namespace
 
-RasterizerAccelerated::RasterizerAccelerated() = default;
+RasterizerAccelerated::RasterizerAccelerated(Memory::Memory& cpu_memory_)
+    : cpu_memory{cpu_memory_} {}
 
 RasterizerAccelerated::~RasterizerAccelerated() = default;
 
diff --git a/src/video_core/rasterizer_accelerated.h b/src/video_core/rasterizer_accelerated.h
index 8f7e3547e9..315798e7c8 100644
--- a/src/video_core/rasterizer_accelerated.h
+++ b/src/video_core/rasterizer_accelerated.h
@@ -11,12 +11,16 @@
 #include "common/common_types.h"
 #include "video_core/rasterizer_interface.h"
 
+namespace Memory {
+class Memory;
+}
+
 namespace VideoCore {
 
 /// Implements the shared part in GPU accelerated rasterizers in RasterizerInterface.
 class RasterizerAccelerated : public RasterizerInterface {
 public:
-    explicit RasterizerAccelerated();
+    explicit RasterizerAccelerated(Memory::Memory& cpu_memory_);
     ~RasterizerAccelerated() override;
 
     void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) override;
@@ -24,8 +28,9 @@ public:
 private:
     using CachedPageMap = boost::icl::interval_map<u64, int>;
     CachedPageMap cached_pages;
-
     std::mutex pages_mutex;
+
+    Memory::Memory& cpu_memory;
 };
 
 } // namespace VideoCore
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index f97ec06f0c..85f05544cd 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -86,8 +86,9 @@ std::size_t GetConstBufferSize(const Tegra::Engines::ConstBufferInfo& buffer,
 
 RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
                                    ScreenInfo& info)
-    : texture_cache{system, *this, device}, shader_cache{*this, system, emu_window, device},
-      system{system}, screen_info{info}, buffer_cache{*this, system, device, STREAM_BUFFER_SIZE} {
+    : RasterizerAccelerated{system.Memory()}, texture_cache{system, *this, device},
+      shader_cache{*this, system, emu_window, device}, system{system}, screen_info{info},
+      buffer_cache{*this, system, device, STREAM_BUFFER_SIZE} {
     shader_program_manager = std::make_unique<GLShader::ProgramManager>();
     state.draw.shader_program = 0;
     state.Apply();
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index d2e9f40315..9596387471 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -24,9 +24,11 @@ CachedBufferEntry::CachedBufferEntry(VAddr cpu_addr, std::size_t size, u64 offse
       alignment{alignment} {}
 
 VKBufferCache::VKBufferCache(Tegra::MemoryManager& tegra_memory_manager,
+                             Memory::Memory& cpu_memory_,
                              VideoCore::RasterizerInterface& rasterizer, const VKDevice& device,
                              VKMemoryManager& memory_manager, VKScheduler& scheduler, u64 size)
-    : RasterizerCache{rasterizer}, tegra_memory_manager{tegra_memory_manager} {
+    : RasterizerCache{rasterizer}, tegra_memory_manager{tegra_memory_manager}, cpu_memory{
+                                                                                   cpu_memory_} {
     const auto usage = vk::BufferUsageFlagBits::eVertexBuffer |
                        vk::BufferUsageFlagBits::eIndexBuffer |
                        vk::BufferUsageFlagBits::eUniformBuffer;
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index 49f13bcdcd..daa8ccf667 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -13,6 +13,10 @@
 #include "video_core/renderer_vulkan/declarations.h"
 #include "video_core/renderer_vulkan/vk_scheduler.h"
 
+namespace Memory {
+class Memory;
+}
+
 namespace Tegra {
 class MemoryManager;
 }
@@ -58,7 +62,7 @@ private:
 
 class VKBufferCache final : public RasterizerCache<std::shared_ptr<CachedBufferEntry>> {
 public:
-    explicit VKBufferCache(Tegra::MemoryManager& tegra_memory_manager,
+    explicit VKBufferCache(Tegra::MemoryManager& tegra_memory_manager, Memory::Memory& cpu_memory_,
                            VideoCore::RasterizerInterface& rasterizer, const VKDevice& device,
                            VKMemoryManager& memory_manager, VKScheduler& scheduler, u64 size);
     ~VKBufferCache();
@@ -92,6 +96,7 @@ private:
     void AlignBuffer(std::size_t alignment);
 
     Tegra::MemoryManager& tegra_memory_manager;
+    Memory::Memory& cpu_memory;
 
     std::unique_ptr<VKStreamBuffer> stream_buffer;
     vk::Buffer buffer_handle;

From 3f08e8d8d4ef16cf2468620fbfbdac46e43dcaef Mon Sep 17 00:00:00 2001
From: Lioncash <mathew1800@gmail.com>
Date: Tue, 26 Nov 2019 15:19:15 -0500
Subject: [PATCH 06/13] core/memory: Migrate over GetPointer()

With all of the interfaces ready for migration, it's trivial to migrate
over GetPointer().
---
 src/core/hle/kernel/server_session.cpp        |  3 +-
 src/core/memory.cpp                           | 38 +++++++++++--------
 src/core/memory.h                             | 22 ++++++++++-
 src/video_core/memory_manager.cpp             |  4 +-
 .../renderer_opengl/gl_rasterizer.cpp         |  3 +-
 .../renderer_opengl/renderer_opengl.cpp       |  2 +-
 .../renderer_vulkan/vk_buffer_cache.cpp       |  6 +--
 7 files changed, 53 insertions(+), 25 deletions(-)

diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index 57878514d2..1198c7a970 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -19,6 +19,7 @@
 #include "core/hle/kernel/server_session.h"
 #include "core/hle/kernel/session.h"
 #include "core/hle/kernel/thread.h"
+#include "core/memory.h"
 
 namespace Kernel {
 
@@ -133,7 +134,7 @@ ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread,
     // from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or
     // similar.
     Kernel::HLERequestContext context(SharedFrom(this), thread);
-    u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress());
+    u32* cmd_buf = (u32*)memory.GetPointer(thread->GetTLSAddress());
     context.PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
 
     ResultCode result = RESULT_SUCCESS;
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 0170336139..93cd67e39a 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -195,6 +195,21 @@ struct Memory::Impl {
         return IsValidVirtualAddress(*system.CurrentProcess(), vaddr);
     }
 
+    u8* GetPointer(const VAddr vaddr) {
+        u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
+        if (page_pointer != nullptr) {
+            return page_pointer + (vaddr & PAGE_MASK);
+        }
+
+        if (current_page_table->attributes[vaddr >> PAGE_BITS] ==
+            Common::PageType::RasterizerCachedMemory) {
+            return GetPointerFromVMA(vaddr);
+        }
+
+        LOG_ERROR(HW_Memory, "Unknown GetPointer @ 0x{:016X}", vaddr);
+        return nullptr;
+    }
+
     /**
      * Maps a region of pages as a specific type.
      *
@@ -276,6 +291,14 @@ bool Memory::IsValidVirtualAddress(const VAddr vaddr) const {
     return impl->IsValidVirtualAddress(vaddr);
 }
 
+u8* Memory::GetPointer(VAddr vaddr) {
+    return impl->GetPointer(vaddr);
+}
+
+const u8* Memory::GetPointer(VAddr vaddr) const {
+    return impl->GetPointer(vaddr);
+}
+
 void SetCurrentPageTable(Kernel::Process& process) {
     current_page_table = &process.VMManager().page_table;
 
@@ -292,21 +315,6 @@ bool IsKernelVirtualAddress(const VAddr vaddr) {
     return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END;
 }
 
-u8* GetPointer(const VAddr vaddr) {
-    u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
-    if (page_pointer) {
-        return page_pointer + (vaddr & PAGE_MASK);
-    }
-
-    if (current_page_table->attributes[vaddr >> PAGE_BITS] ==
-        Common::PageType::RasterizerCachedMemory) {
-        return GetPointerFromVMA(vaddr);
-    }
-
-    LOG_ERROR(HW_Memory, "Unknown GetPointer @ 0x{:016X}", vaddr);
-    return nullptr;
-}
-
 std::string ReadCString(VAddr vaddr, std::size_t max_length) {
     std::string string;
     string.reserve(max_length);
diff --git a/src/core/memory.h b/src/core/memory.h
index cacf4fb1a6..59b9ce2bbb 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -132,6 +132,26 @@ public:
      */
     bool IsValidVirtualAddress(VAddr vaddr) const;
 
+    /**
+     * Gets a pointer to the given address.
+     *
+     * @param vaddr Virtual address to retrieve a pointer to.
+     *
+     * @returns The pointer to the given address, if the address is valid.
+     *          If the address is not valid, nullptr will be returned.
+     */
+    u8* GetPointer(VAddr vaddr);
+
+    /**
+     * Gets a pointer to the given address.
+     *
+     * @param vaddr Virtual address to retrieve a pointer to.
+     *
+     * @returns The pointer to the given address, if the address is valid.
+     *          If the address is not valid, nullptr will be returned.
+     */
+    const u8* GetPointer(VAddr vaddr) const;
+
 private:
     struct Impl;
     std::unique_ptr<Impl> impl;
@@ -162,8 +182,6 @@ void WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size);
 void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size);
 void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size);
 
-u8* GetPointer(VAddr vaddr);
-
 std::string ReadCString(VAddr vaddr, std::size_t max_length);
 
 /**
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index bffae940c0..11848fbce1 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -52,7 +52,7 @@ GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, u64 size) {
     const u64 aligned_size{Common::AlignUp(size, page_size)};
     const GPUVAddr gpu_addr{FindFreeRegion(address_space_base, aligned_size)};
 
-    MapBackingMemory(gpu_addr, Memory::GetPointer(cpu_addr), aligned_size, cpu_addr);
+    MapBackingMemory(gpu_addr, system.Memory().GetPointer(cpu_addr), aligned_size, cpu_addr);
     ASSERT(system.CurrentProcess()
                ->VMManager()
                .SetMemoryAttribute(cpu_addr, size, Kernel::MemoryAttribute::DeviceMapped,
@@ -67,7 +67,7 @@ GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size)
 
     const u64 aligned_size{Common::AlignUp(size, page_size)};
 
-    MapBackingMemory(gpu_addr, Memory::GetPointer(cpu_addr), aligned_size, cpu_addr);
+    MapBackingMemory(gpu_addr, system.Memory().GetPointer(cpu_addr), aligned_size, cpu_addr);
     ASSERT(system.CurrentProcess()
                ->VMManager()
                .SetMemoryAttribute(cpu_addr, size, Kernel::MemoryAttribute::DeviceMapped,
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 85f05544cd..a568a4343e 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -19,6 +19,7 @@
 #include "common/scope_exit.h"
 #include "core/core.h"
 #include "core/hle/kernel/process.h"
+#include "core/memory.h"
 #include "core/settings.h"
 #include "video_core/engines/kepler_compute.h"
 #include "video_core/engines/maxwell_3d.h"
@@ -838,7 +839,7 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
     MICROPROFILE_SCOPE(OpenGL_CacheManagement);
 
     const auto surface{
-        texture_cache.TryFindFramebufferSurface(Memory::GetPointer(framebuffer_addr))};
+        texture_cache.TryFindFramebufferSurface(system.Memory().GetPointer(framebuffer_addr))};
     if (!surface) {
         return {};
     }
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 7646cbb0ec..a57a564f77 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -158,7 +158,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
         VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)};
     const u32 bytes_per_pixel{VideoCore::Surface::GetBytesPerPixel(pixel_format)};
     const u64 size_in_bytes{framebuffer.stride * framebuffer.height * bytes_per_pixel};
-    const auto host_ptr{Memory::GetPointer(framebuffer_addr)};
+    u8* const host_ptr{system.Memory().GetPointer(framebuffer_addr)};
     rasterizer->FlushRegion(ToCacheAddr(host_ptr), size_in_bytes);
 
     // TODO(Rodrigo): Read this from HLE
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 9596387471..46da81aaac 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -50,9 +50,9 @@ u64 VKBufferCache::UploadMemory(GPUVAddr gpu_addr, std::size_t size, u64 alignme
     // TODO: Figure out which size is the best for given games.
     cache &= size >= 2048;
 
-    const auto& host_ptr{Memory::GetPointer(*cpu_addr)};
+    u8* const host_ptr{cpu_memory.GetPointer(*cpu_addr)};
     if (cache) {
-        auto entry = TryGet(host_ptr);
+        const auto entry = TryGet(host_ptr);
         if (entry) {
             if (entry->GetSize() >= size && entry->GetAlignment() == alignment) {
                 return entry->GetOffset();
@@ -64,7 +64,7 @@ u64 VKBufferCache::UploadMemory(GPUVAddr gpu_addr, std::size_t size, u64 alignme
     AlignBuffer(alignment);
     const u64 uploaded_offset = buffer_offset;
 
-    if (!host_ptr) {
+    if (host_ptr == nullptr) {
         return uploaded_offset;
     }
 

From b2165c6b353be5e8117d1f9bc677bb198fa9d8cd Mon Sep 17 00:00:00 2001
From: Lioncash <mathew1800@gmail.com>
Date: Tue, 26 Nov 2019 15:48:19 -0500
Subject: [PATCH 07/13] core/memory: Migrate over ReadCString() to the Memory
 class

This only had one usage spot, so this is fairly straightforward to
convert over.
---
 src/core/hle/kernel/svc.cpp |  6 ++++--
 src/core/memory.cpp         | 33 +++++++++++++++++++--------------
 src/core/memory.h           | 19 +++++++++++++++++--
 3 files changed, 40 insertions(+), 18 deletions(-)

diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 68bff11ec2..738db528d6 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -332,7 +332,9 @@ static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_ad
 /// Connect to an OS service given the port name, returns the handle to the port to out
 static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle,
                                      VAddr port_name_address) {
-    if (!system.Memory().IsValidVirtualAddress(port_name_address)) {
+    auto& memory = system.Memory();
+
+    if (!memory.IsValidVirtualAddress(port_name_address)) {
         LOG_ERROR(Kernel_SVC,
                   "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}",
                   port_name_address);
@@ -341,7 +343,7 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle,
 
     static constexpr std::size_t PortNameMaxLength = 11;
     // Read 1 char beyond the max allowed port name to detect names that are too long.
-    std::string port_name = Memory::ReadCString(port_name_address, PortNameMaxLength + 1);
+    const std::string port_name = memory.ReadCString(port_name_address, PortNameMaxLength + 1);
     if (port_name.size() > PortNameMaxLength) {
         LOG_ERROR(Kernel_SVC, "Port name is too long, expected {} but got {}", PortNameMaxLength,
                   port_name.size());
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 93cd67e39a..fb824d7103 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -210,6 +210,21 @@ struct Memory::Impl {
         return nullptr;
     }
 
+    std::string ReadCString(VAddr vaddr, std::size_t max_length) {
+        std::string string;
+        string.reserve(max_length);
+        for (std::size_t i = 0; i < max_length; ++i) {
+            const char c = Read8(vaddr);
+            if (c == '\0') {
+                break;
+            }
+            string.push_back(c);
+            ++vaddr;
+        }
+        string.shrink_to_fit();
+        return string;
+    }
+
     /**
      * Maps a region of pages as a specific type.
      *
@@ -299,6 +314,10 @@ const u8* Memory::GetPointer(VAddr vaddr) const {
     return impl->GetPointer(vaddr);
 }
 
+std::string Memory::ReadCString(VAddr vaddr, std::size_t max_length) {
+    return impl->ReadCString(vaddr, max_length);
+}
+
 void SetCurrentPageTable(Kernel::Process& process) {
     current_page_table = &process.VMManager().page_table;
 
@@ -315,20 +334,6 @@ bool IsKernelVirtualAddress(const VAddr vaddr) {
     return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END;
 }
 
-std::string ReadCString(VAddr vaddr, std::size_t max_length) {
-    std::string string;
-    string.reserve(max_length);
-    for (std::size_t i = 0; i < max_length; ++i) {
-        char c = Read8(vaddr);
-        if (c == '\0')
-            break;
-        string.push_back(c);
-        ++vaddr;
-    }
-    string.shrink_to_fit();
-    return string;
-}
-
 void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
     if (vaddr == 0) {
         return;
diff --git a/src/core/memory.h b/src/core/memory.h
index 59b9ce2bbb..47765c8a06 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -152,6 +152,23 @@ public:
      */
     const u8* GetPointer(VAddr vaddr) const;
 
+    /**
+     * Reads a null-terminated string from the given virtual address.
+     * This function will continually read characters until either:
+     *
+     * - A null character ('\0') is reached.
+     * - max_length characters have been read.
+     *
+     * @note The final null-terminating character (if found) is not included
+     *       in the returned string.
+     *
+     * @param vaddr      The address to begin reading the string from.
+     * @param max_length The maximum length of the string to read in characters.
+     *
+     * @returns The read string.
+     */
+    std::string ReadCString(VAddr vaddr, std::size_t max_length);
+
 private:
     struct Impl;
     std::unique_ptr<Impl> impl;
@@ -182,8 +199,6 @@ void WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size);
 void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size);
 void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size);
 
-std::string ReadCString(VAddr vaddr, std::size_t max_length);
-
 /**
  * Mark each page touching the region as cached.
  */

From 849581075a230ad0f5419bb5d5e1f9e48e6cfd8a Mon Sep 17 00:00:00 2001
From: Lioncash <mathew1800@gmail.com>
Date: Tue, 26 Nov 2019 15:56:13 -0500
Subject: [PATCH 08/13] core/memory: Migrate over RasterizerMarkRegionCached()
 to the Memory class

This is only used within the accelerated rasterizer in two places, so
this is also a very trivial migration.
---
 src/core/memory.cpp                       | 130 +++++++++++-----------
 src/core/memory.h                         |  15 ++-
 src/video_core/rasterizer_accelerated.cpp |   4 +-
 3 files changed, 79 insertions(+), 70 deletions(-)

diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index fb824d7103..8c3489ed3e 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -225,6 +225,69 @@ struct Memory::Impl {
         return string;
     }
 
+    void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
+        if (vaddr == 0) {
+            return;
+        }
+
+        // Iterate over a contiguous CPU address space, which corresponds to the specified GPU
+        // address space, marking the region as un/cached. The region is marked un/cached at a
+        // granularity of CPU pages, hence why we iterate on a CPU page basis (note: GPU page size
+        // is different). This assumes the specified GPU address region is contiguous as well.
+
+        u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1;
+        for (unsigned i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) {
+            Common::PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS];
+
+            if (cached) {
+                // Switch page type to cached if now cached
+                switch (page_type) {
+                case Common::PageType::Unmapped:
+                    // It is not necessary for a process to have this region mapped into its address
+                    // space, for example, a system module need not have a VRAM mapping.
+                    break;
+                case Common::PageType::Memory:
+                    page_type = Common::PageType::RasterizerCachedMemory;
+                    current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr;
+                    break;
+                case Common::PageType::RasterizerCachedMemory:
+                    // There can be more than one GPU region mapped per CPU region, so it's common
+                    // that this area is already marked as cached.
+                    break;
+                default:
+                    UNREACHABLE();
+                }
+            } else {
+                // Switch page type to uncached if now uncached
+                switch (page_type) {
+                case Common::PageType::Unmapped:
+                    // It is not necessary for a process to have this region mapped into its address
+                    // space, for example, a system module need not have a VRAM mapping.
+                    break;
+                case Common::PageType::Memory:
+                    // There can be more than one GPU region mapped per CPU region, so it's common
+                    // that this area is already unmarked as cached.
+                    break;
+                case Common::PageType::RasterizerCachedMemory: {
+                    u8* pointer = GetPointerFromVMA(vaddr & ~PAGE_MASK);
+                    if (pointer == nullptr) {
+                        // It's possible that this function has been called while updating the
+                        // pagetable after unmapping a VMA. In that case the underlying VMA will no
+                        // longer exist, and we should just leave the pagetable entry blank.
+                        page_type = Common::PageType::Unmapped;
+                    } else {
+                        page_type = Common::PageType::Memory;
+                        current_page_table->pointers[vaddr >> PAGE_BITS] = pointer;
+                    }
+                    break;
+                }
+                default:
+                    UNREACHABLE();
+                }
+            }
+        }
+    }
+
     /**
      * Maps a region of pages as a specific type.
      *
@@ -318,6 +381,10 @@ std::string Memory::ReadCString(VAddr vaddr, std::size_t max_length) {
     return impl->ReadCString(vaddr, max_length);
 }
 
+void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
+    impl->RasterizerMarkRegionCached(vaddr, size, cached);
+}
+
 void SetCurrentPageTable(Kernel::Process& process) {
     current_page_table = &process.VMManager().page_table;
 
@@ -334,69 +401,6 @@ bool IsKernelVirtualAddress(const VAddr vaddr) {
     return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END;
 }
 
-void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
-    if (vaddr == 0) {
-        return;
-    }
-
-    // Iterate over a contiguous CPU address space, which corresponds to the specified GPU address
-    // space, marking the region as un/cached. The region is marked un/cached at a granularity of
-    // CPU pages, hence why we iterate on a CPU page basis (note: GPU page size is different). This
-    // assumes the specified GPU address region is contiguous as well.
-
-    u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1;
-    for (unsigned i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) {
-        Common::PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS];
-
-        if (cached) {
-            // Switch page type to cached if now cached
-            switch (page_type) {
-            case Common::PageType::Unmapped:
-                // It is not necessary for a process to have this region mapped into its address
-                // space, for example, a system module need not have a VRAM mapping.
-                break;
-            case Common::PageType::Memory:
-                page_type = Common::PageType::RasterizerCachedMemory;
-                current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr;
-                break;
-            case Common::PageType::RasterizerCachedMemory:
-                // There can be more than one GPU region mapped per CPU region, so it's common that
-                // this area is already marked as cached.
-                break;
-            default:
-                UNREACHABLE();
-            }
-        } else {
-            // Switch page type to uncached if now uncached
-            switch (page_type) {
-            case Common::PageType::Unmapped:
-                // It is not necessary for a process to have this region mapped into its address
-                // space, for example, a system module need not have a VRAM mapping.
-                break;
-            case Common::PageType::Memory:
-                // There can be more than one GPU region mapped per CPU region, so it's common that
-                // this area is already unmarked as cached.
-                break;
-            case Common::PageType::RasterizerCachedMemory: {
-                u8* pointer = GetPointerFromVMA(vaddr & ~PAGE_MASK);
-                if (pointer == nullptr) {
-                    // It's possible that this function has been called while updating the pagetable
-                    // after unmapping a VMA. In that case the underlying VMA will no longer exist,
-                    // and we should just leave the pagetable entry blank.
-                    page_type = Common::PageType::Unmapped;
-                } else {
-                    page_type = Common::PageType::Memory;
-                    current_page_table->pointers[vaddr >> PAGE_BITS] = pointer;
-                }
-                break;
-            }
-            default:
-                UNREACHABLE();
-            }
-        }
-    }
-}
-
 u8 Read8(const VAddr addr) {
     return Read<u8>(addr);
 }
diff --git a/src/core/memory.h b/src/core/memory.h
index 47765c8a06..7cd348b49a 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -169,6 +169,16 @@ public:
      */
     std::string ReadCString(VAddr vaddr, std::size_t max_length);
 
+    /**
+     * Marks each page within the specified address range as cached or uncached.
+     *
+     * @param vaddr  The virtual address indicating the start of the address range.
+     * @param size   The size of the address range in bytes.
+     * @param cached Whether or not any pages within the address range should be
+     *               marked as cached or uncached.
+     */
+    void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached);
+
 private:
     struct Impl;
     std::unique_ptr<Impl> impl;
@@ -199,9 +209,4 @@ void WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size);
 void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size);
 void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size);
 
-/**
- * Mark each page touching the region as cached.
- */
-void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached);
-
 } // namespace Memory
diff --git a/src/video_core/rasterizer_accelerated.cpp b/src/video_core/rasterizer_accelerated.cpp
index f3dbadebcf..fc6ecb899e 100644
--- a/src/video_core/rasterizer_accelerated.cpp
+++ b/src/video_core/rasterizer_accelerated.cpp
@@ -48,9 +48,9 @@ void RasterizerAccelerated::UpdatePagesCachedCount(VAddr addr, u64 size, int del
         const u64 interval_size = interval_end_addr - interval_start_addr;
 
         if (delta > 0 && count == delta) {
-            Memory::RasterizerMarkRegionCached(interval_start_addr, interval_size, true);
+            cpu_memory.RasterizerMarkRegionCached(interval_start_addr, interval_size, true);
         } else if (delta < 0 && count == -delta) {
-            Memory::RasterizerMarkRegionCached(interval_start_addr, interval_size, false);
+            cpu_memory.RasterizerMarkRegionCached(interval_start_addr, interval_size, false);
         } else {
             ASSERT(count >= 0);
         }

From 89ef3ef5752e42d0eb0bdfa23cc72d391db74216 Mon Sep 17 00:00:00 2001
From: Lioncash <mathew1800@gmail.com>
Date: Tue, 26 Nov 2019 16:06:49 -0500
Subject: [PATCH 09/13] core/memory: Migrate over ZeroBlock() and CopyBlock()
 to the Memory class

These currently aren't used anywhere in the codebase, so these are very
trivial to move over to the Memory class.
---
 src/core/memory.cpp | 199 ++++++++++++++++++++++++--------------------
 src/core/memory.h   |  53 +++++++++++-
 2 files changed, 161 insertions(+), 91 deletions(-)

diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 8c3489ed3e..c939e980d2 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -225,6 +225,99 @@ struct Memory::Impl {
         return string;
     }
 
+    void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std::size_t size) {
+        const auto& page_table = process.VMManager().page_table;
+        std::size_t remaining_size = size;
+        std::size_t page_index = dest_addr >> PAGE_BITS;
+        std::size_t page_offset = dest_addr & PAGE_MASK;
+
+        while (remaining_size > 0) {
+            const std::size_t copy_amount =
+                std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
+            const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
+
+            switch (page_table.attributes[page_index]) {
+            case Common::PageType::Unmapped: {
+                LOG_ERROR(HW_Memory,
+                          "Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
+                          current_vaddr, dest_addr, size);
+                break;
+            }
+            case Common::PageType::Memory: {
+                DEBUG_ASSERT(page_table.pointers[page_index]);
+
+                u8* dest_ptr = page_table.pointers[page_index] + page_offset;
+                std::memset(dest_ptr, 0, copy_amount);
+                break;
+            }
+            case Common::PageType::RasterizerCachedMemory: {
+                u8* const host_ptr = GetPointerFromVMA(process, current_vaddr);
+                system.GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount);
+                std::memset(host_ptr, 0, copy_amount);
+                break;
+            }
+            default:
+                UNREACHABLE();
+            }
+
+            page_index++;
+            page_offset = 0;
+            remaining_size -= copy_amount;
+        }
+    }
+
+    void ZeroBlock(const VAddr dest_addr, const std::size_t size) {
+        ZeroBlock(*system.CurrentProcess(), dest_addr, size);
+    }
+
+    void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr,
+                   const std::size_t size) {
+        const auto& page_table = process.VMManager().page_table;
+        std::size_t remaining_size = size;
+        std::size_t page_index = src_addr >> PAGE_BITS;
+        std::size_t page_offset = src_addr & PAGE_MASK;
+
+        while (remaining_size > 0) {
+            const std::size_t copy_amount =
+                std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
+            const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
+
+            switch (page_table.attributes[page_index]) {
+            case Common::PageType::Unmapped: {
+                LOG_ERROR(HW_Memory,
+                          "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
+                          current_vaddr, src_addr, size);
+                ZeroBlock(process, dest_addr, copy_amount);
+                break;
+            }
+            case Common::PageType::Memory: {
+                DEBUG_ASSERT(page_table.pointers[page_index]);
+                const u8* src_ptr = page_table.pointers[page_index] + page_offset;
+                WriteBlock(process, dest_addr, src_ptr, copy_amount);
+                break;
+            }
+            case Common::PageType::RasterizerCachedMemory: {
+                const u8* const host_ptr = GetPointerFromVMA(process, current_vaddr);
+                system.GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount);
+                WriteBlock(process, dest_addr, host_ptr, copy_amount);
+                break;
+            }
+            default:
+                UNREACHABLE();
+            }
+
+            page_index++;
+            page_offset = 0;
+            dest_addr += static_cast<VAddr>(copy_amount);
+            src_addr += static_cast<VAddr>(copy_amount);
+            remaining_size -= copy_amount;
+        }
+    }
+
+    void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size) {
+        return CopyBlock(*system.CurrentProcess(), dest_addr, src_addr, size);
+    }
+
     void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
         if (vaddr == 0) {
             return;
@@ -381,6 +474,23 @@ std::string Memory::ReadCString(VAddr vaddr, std::size_t max_length) {
     return impl->ReadCString(vaddr, max_length);
 }
 
+void Memory::ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size) {
+    impl->ZeroBlock(process, dest_addr, size);
+}
+
+void Memory::ZeroBlock(VAddr dest_addr, std::size_t size) {
+    impl->ZeroBlock(dest_addr, size);
+}
+
+void Memory::CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr,
+                       const std::size_t size) {
+    impl->CopyBlock(process, dest_addr, src_addr, size);
+}
+
+void Memory::CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size) {
+    impl->CopyBlock(dest_addr, src_addr, size);
+}
+
 void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
     impl->RasterizerMarkRegionCached(vaddr, size, cached);
 }
@@ -529,93 +639,4 @@ void WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t
     WriteBlock(*Core::System::GetInstance().CurrentProcess(), dest_addr, src_buffer, size);
 }
 
-void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std::size_t size) {
-    const auto& page_table = process.VMManager().page_table;
-    std::size_t remaining_size = size;
-    std::size_t page_index = dest_addr >> PAGE_BITS;
-    std::size_t page_offset = dest_addr & PAGE_MASK;
-
-    while (remaining_size > 0) {
-        const std::size_t copy_amount =
-            std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
-        const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
-
-        switch (page_table.attributes[page_index]) {
-        case Common::PageType::Unmapped: {
-            LOG_ERROR(HW_Memory,
-                      "Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
-                      current_vaddr, dest_addr, size);
-            break;
-        }
-        case Common::PageType::Memory: {
-            DEBUG_ASSERT(page_table.pointers[page_index]);
-
-            u8* dest_ptr = page_table.pointers[page_index] + page_offset;
-            std::memset(dest_ptr, 0, copy_amount);
-            break;
-        }
-        case Common::PageType::RasterizerCachedMemory: {
-            const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
-            Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount);
-            std::memset(host_ptr, 0, copy_amount);
-            break;
-        }
-        default:
-            UNREACHABLE();
-        }
-
-        page_index++;
-        page_offset = 0;
-        remaining_size -= copy_amount;
-    }
-}
-
-void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr,
-               const std::size_t size) {
-    const auto& page_table = process.VMManager().page_table;
-    std::size_t remaining_size = size;
-    std::size_t page_index = src_addr >> PAGE_BITS;
-    std::size_t page_offset = src_addr & PAGE_MASK;
-
-    while (remaining_size > 0) {
-        const std::size_t copy_amount =
-            std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
-        const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
-
-        switch (page_table.attributes[page_index]) {
-        case Common::PageType::Unmapped: {
-            LOG_ERROR(HW_Memory,
-                      "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
-                      current_vaddr, src_addr, size);
-            ZeroBlock(process, dest_addr, copy_amount);
-            break;
-        }
-        case Common::PageType::Memory: {
-            DEBUG_ASSERT(page_table.pointers[page_index]);
-            const u8* src_ptr = page_table.pointers[page_index] + page_offset;
-            WriteBlock(process, dest_addr, src_ptr, copy_amount);
-            break;
-        }
-        case Common::PageType::RasterizerCachedMemory: {
-            const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
-            Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount);
-            WriteBlock(process, dest_addr, host_ptr, copy_amount);
-            break;
-        }
-        default:
-            UNREACHABLE();
-        }
-
-        page_index++;
-        page_offset = 0;
-        dest_addr += static_cast<VAddr>(copy_amount);
-        src_addr += static_cast<VAddr>(copy_amount);
-        remaining_size -= copy_amount;
-    }
-}
-
-void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size) {
-    CopyBlock(*Core::System::GetInstance().CurrentProcess(), dest_addr, src_addr, size);
-}
-
 } // namespace Memory
diff --git a/src/core/memory.h b/src/core/memory.h
index 7cd348b49a..fc0013a965 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -169,6 +169,57 @@ public:
      */
     std::string ReadCString(VAddr vaddr, std::size_t max_length);
 
+    /**
+     * Fills the specified address range within a process' address space with zeroes.
+     *
+     * @param process   The process that will have a portion of its memory zeroed out.
+     * @param dest_addr The starting virtual address of the range to zero out.
+     * @param size      The size of the address range to zero out, in bytes.
+     *
+     * @post The range [dest_addr, size) within the process' address space is
+     *       filled with zeroes.
+     */
+    void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size);
+
+    /**
+     * Fills the specified address range within the current process' address space with zeroes.
+     *
+     * @param dest_addr The starting virtual address of the range to zero out.
+     * @param size      The size of the address range to zero out, in bytes.
+     *
+     * @post The range [dest_addr, size) within the current process' address space is
+     *       filled with zeroes.
+     */
+    void ZeroBlock(VAddr dest_addr, std::size_t size);
+
+    /**
+     * Copies data within a process' address space to another location within the
+     * same address space.
+     *
+     * @param process   The process that will have data copied within its address space.
+     * @param dest_addr The destination virtual address to begin copying the data into.
+     * @param src_addr  The source virtual address to begin copying the data from.
+     * @param size      The size of the data to copy, in bytes.
+     *
+     * @post The range [dest_addr, size) within the process' address space contains the
+     *       same data within the range [src_addr, size).
+     */
+    void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr,
+                   std::size_t size);
+
+    /**
+     * Copies data within the current process' address space to another location within the
+     * same address space.
+     *
+     * @param dest_addr The destination virtual address to begin copying the data into.
+     * @param src_addr  The source virtual address to begin copying the data from.
+     * @param size      The size of the data to copy, in bytes.
+     *
+     * @post The range [dest_addr, size) within the current process' address space
+     *       contains the same data within the range [src_addr, size).
+     */
+    void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size);
+
     /**
      * Marks each page within the specified address range as cached or uncached.
      *
@@ -206,7 +257,5 @@ void ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size);
 void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer,
                 std::size_t size);
 void WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size);
-void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size);
-void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size);
 
 } // namespace Memory

From b05bfc603689419dc515a656b9fc711d79994f13 Mon Sep 17 00:00:00 2001
From: Lioncash <mathew1800@gmail.com>
Date: Tue, 26 Nov 2019 16:29:34 -0500
Subject: [PATCH 10/13] core/memory: Migrate over Read{8, 16, 32, 64, Block} to
 the Memory class

With all of the trivial parts of the memory interface moved over, we can
get right into moving over the bits that are used.

Note that this does require the use of GetInstance from the global
system instance to be used within hle_ipc.cpp and the gdbstub. This is
fine for the time being, as they both already rely on the global system
instance in other functions. These will be removed in a change directed
at both of these respectively.

For now, it's sufficient, as it still accomplishes the goal of
de-globalizing the memory code.
---
 src/audio_core/audio_renderer.cpp             |  17 +-
 src/core/arm/arm_interface.cpp                |  24 +-
 src/core/arm/dynarmic/arm_dynarmic.cpp        |  12 +-
 src/core/gdbstub/gdbstub.cpp                  |   8 +-
 src/core/hle/kernel/address_arbiter.cpp       |  28 ++-
 src/core/hle/kernel/hle_ipc.cpp               |  12 +-
 src/core/hle/kernel/mutex.cpp                 |   2 +-
 src/core/hle/kernel/svc.cpp                   |  16 +-
 src/core/hle/service/audio/audout_u.cpp       |   6 +-
 src/core/hle/service/ldr/ldr.cpp              |   5 +-
 src/core/hle/service/lm/lm.cpp                |  10 +-
 .../hle/service/nvdrv/devices/nvhost_gpu.cpp  |   4 +-
 src/core/memory.cpp                           | 228 ++++++++++--------
 src/core/memory.h                             |  85 ++++++-
 src/core/memory/cheat_engine.cpp              |   5 +-
 src/core/memory/cheat_engine.h                |   4 +-
 src/core/reporter.cpp                         |   2 +-
 src/core/tools/freezer.cpp                    |   8 +-
 src/yuzu/debugger/wait_tree.cpp               |   7 +-
 19 files changed, 305 insertions(+), 178 deletions(-)

diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
index 12e2b79019..c187d8ac5d 100644
--- a/src/audio_core/audio_renderer.cpp
+++ b/src/audio_core/audio_renderer.cpp
@@ -259,9 +259,10 @@ void AudioRenderer::VoiceState::UpdateState() {
 }
 
 void AudioRenderer::VoiceState::RefreshBuffer(Memory::Memory& memory) {
-    std::vector<s16> new_samples(info.wave_buffer[wave_index].buffer_sz / sizeof(s16));
-    Memory::ReadBlock(info.wave_buffer[wave_index].buffer_addr, new_samples.data(),
-                      info.wave_buffer[wave_index].buffer_sz);
+    const auto wave_buffer_address = info.wave_buffer[wave_index].buffer_addr;
+    const auto wave_buffer_size = info.wave_buffer[wave_index].buffer_sz;
+    std::vector<s16> new_samples(wave_buffer_size / sizeof(s16));
+    memory.ReadBlock(wave_buffer_address, new_samples.data(), wave_buffer_size);
 
     switch (static_cast<Codec::PcmFormat>(info.sample_format)) {
     case Codec::PcmFormat::Int16: {
@@ -271,7 +272,7 @@ void AudioRenderer::VoiceState::RefreshBuffer(Memory::Memory& memory) {
     case Codec::PcmFormat::Adpcm: {
         // Decode ADPCM to PCM16
         Codec::ADPCM_Coeff coeffs;
-        Memory::ReadBlock(info.additional_params_addr, coeffs.data(), sizeof(Codec::ADPCM_Coeff));
+        memory.ReadBlock(info.additional_params_addr, coeffs.data(), sizeof(Codec::ADPCM_Coeff));
         new_samples = Codec::DecodeADPCM(reinterpret_cast<u8*>(new_samples.data()),
                                          new_samples.size() * sizeof(s16), coeffs, adpcm_state);
         break;
@@ -314,13 +315,13 @@ void AudioRenderer::EffectState::UpdateState(Memory::Memory& memory) {
         out_status.state = EffectStatus::New;
     } else {
         if (info.type == Effect::Aux) {
-            ASSERT_MSG(Memory::Read32(info.aux_info.return_buffer_info) == 0,
+            ASSERT_MSG(memory.Read32(info.aux_info.return_buffer_info) == 0,
                        "Aux buffers tried to update");
-            ASSERT_MSG(Memory::Read32(info.aux_info.send_buffer_info) == 0,
+            ASSERT_MSG(memory.Read32(info.aux_info.send_buffer_info) == 0,
                        "Aux buffers tried to update");
-            ASSERT_MSG(Memory::Read32(info.aux_info.return_buffer_base) == 0,
+            ASSERT_MSG(memory.Read32(info.aux_info.return_buffer_base) == 0,
                        "Aux buffers tried to update");
-            ASSERT_MSG(Memory::Read32(info.aux_info.send_buffer_base) == 0,
+            ASSERT_MSG(memory.Read32(info.aux_info.send_buffer_base) == 0,
                        "Aux buffers tried to update");
         }
     }
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index dea192869f..7e846ddd5c 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -60,15 +60,15 @@ static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size.");
 
 using Symbols = std::vector<std::pair<ELFSymbol, std::string>>;
 
-Symbols GetSymbols(VAddr text_offset) {
-    const auto mod_offset = text_offset + Memory::Read32(text_offset + 4);
+Symbols GetSymbols(VAddr text_offset, Memory::Memory& memory) {
+    const auto mod_offset = text_offset + memory.Read32(text_offset + 4);
 
     if (mod_offset < text_offset || (mod_offset & 0b11) != 0 ||
-        Memory::Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
+        memory.Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
         return {};
     }
 
-    const auto dynamic_offset = Memory::Read32(mod_offset + 0x4) + mod_offset;
+    const auto dynamic_offset = memory.Read32(mod_offset + 0x4) + mod_offset;
 
     VAddr string_table_offset{};
     VAddr symbol_table_offset{};
@@ -76,8 +76,8 @@ Symbols GetSymbols(VAddr text_offset) {
 
     VAddr dynamic_index = dynamic_offset;
     while (true) {
-        const auto tag = Memory::Read64(dynamic_index);
-        const auto value = Memory::Read64(dynamic_index + 0x8);
+        const u64 tag = memory.Read64(dynamic_index);
+        const u64 value = memory.Read64(dynamic_index + 0x8);
         dynamic_index += 0x10;
 
         if (tag == ELF_DYNAMIC_TAG_NULL) {
@@ -105,11 +105,11 @@ Symbols GetSymbols(VAddr text_offset) {
     VAddr symbol_index = symbol_table_address;
     while (symbol_index < string_table_address) {
         ELFSymbol symbol{};
-        Memory::ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol));
+        memory.ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol));
 
         VAddr string_offset = string_table_address + symbol.name_index;
         std::string name;
-        for (u8 c = Memory::Read8(string_offset); c != 0; c = Memory::Read8(++string_offset)) {
+        for (u8 c = memory.Read8(string_offset); c != 0; c = memory.Read8(++string_offset)) {
             name += static_cast<char>(c);
         }
 
@@ -141,17 +141,17 @@ constexpr u64 SEGMENT_BASE = 0x7100000000ull;
 
 std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
     std::vector<BacktraceEntry> out;
+    auto& memory = system.Memory();
 
     auto fp = GetReg(29);
     auto lr = GetReg(30);
-
     while (true) {
         out.push_back({"", 0, lr, 0});
         if (!fp) {
             break;
         }
-        lr = Memory::Read64(fp + 8) - 4;
-        fp = Memory::Read64(fp);
+        lr = memory.Read64(fp + 8) - 4;
+        fp = memory.Read64(fp);
     }
 
     std::map<VAddr, std::string> modules;
@@ -162,7 +162,7 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
 
     std::map<std::string, Symbols> symbols;
     for (const auto& module : modules) {
-        symbols.insert_or_assign(module.second, GetSymbols(module.first));
+        symbols.insert_or_assign(module.second, GetSymbols(module.first, memory));
     }
 
     for (auto& entry : out) {
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index 2b396f1d67..585fb55a90 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -28,20 +28,20 @@ public:
     explicit ARM_Dynarmic_Callbacks(ARM_Dynarmic& parent) : parent(parent) {}
 
     u8 MemoryRead8(u64 vaddr) override {
-        auto& s = parent.system;
-        return Memory::Read8(vaddr);
+        return parent.system.Memory().Read8(vaddr);
     }
     u16 MemoryRead16(u64 vaddr) override {
-        return Memory::Read16(vaddr);
+        return parent.system.Memory().Read16(vaddr);
     }
     u32 MemoryRead32(u64 vaddr) override {
-        return Memory::Read32(vaddr);
+        return parent.system.Memory().Read32(vaddr);
     }
     u64 MemoryRead64(u64 vaddr) override {
-        return Memory::Read64(vaddr);
+        return parent.system.Memory().Read64(vaddr);
     }
     Vector MemoryRead128(u64 vaddr) override {
-        return {Memory::Read64(vaddr), Memory::Read64(vaddr + 8)};
+        auto& memory = parent.system.Memory();
+        return {memory.Read64(vaddr), memory.Read64(vaddr + 8)};
     }
 
     void MemoryWrite8(u64 vaddr, u8 value) override {
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index 78e44f3bdb..1c74a44d82 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -969,13 +969,13 @@ static void ReadMemory() {
         SendReply("E01");
     }
 
-    const auto& memory = Core::System::GetInstance().Memory();
+    auto& memory = Core::System::GetInstance().Memory();
     if (!memory.IsValidVirtualAddress(addr)) {
         return SendReply("E00");
     }
 
     std::vector<u8> data(len);
-    Memory::ReadBlock(addr, data.data(), len);
+    memory.ReadBlock(addr, data.data(), len);
 
     MemToGdbHex(reply, data.data(), len);
     reply[len * 2] = '\0';
@@ -1057,7 +1057,9 @@ static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) {
     breakpoint.active = true;
     breakpoint.addr = addr;
     breakpoint.len = len;
-    Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size());
+
+    auto& memory = Core::System::GetInstance().Memory();
+    memory.ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size());
 
     static constexpr std::array<u8, 4> btrap{0x00, 0x7d, 0x20, 0xd4};
     if (type == BreakpointType::Execute) {
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index 7f9a559d2f..07f0dac67a 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -67,12 +67,14 @@ ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) {
 
 ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value,
                                                               s32 num_to_wake) {
+    auto& memory = system.Memory();
+
     // Ensure that we can write to the address.
-    if (!system.Memory().IsValidVirtualAddress(address)) {
+    if (!memory.IsValidVirtualAddress(address)) {
         return ERR_INVALID_ADDRESS_STATE;
     }
 
-    if (static_cast<s32>(Memory::Read32(address)) != value) {
+    if (static_cast<s32>(memory.Read32(address)) != value) {
         return ERR_INVALID_STATE;
     }
 
@@ -82,8 +84,10 @@ ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32
 
 ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
                                                                          s32 num_to_wake) {
+    auto& memory = system.Memory();
+
     // Ensure that we can write to the address.
-    if (!system.Memory().IsValidVirtualAddress(address)) {
+    if (!memory.IsValidVirtualAddress(address)) {
         return ERR_INVALID_ADDRESS_STATE;
     }
 
@@ -109,7 +113,7 @@ ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr a
         }
     }
 
-    if (static_cast<s32>(Memory::Read32(address)) != value) {
+    if (static_cast<s32>(memory.Read32(address)) != value) {
         return ERR_INVALID_STATE;
     }
 
@@ -134,12 +138,14 @@ ResultCode AddressArbiter::WaitForAddress(VAddr address, ArbitrationType type, s
 
 ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout,
                                                     bool should_decrement) {
+    auto& memory = system.Memory();
+
     // Ensure that we can read the address.
-    if (!system.Memory().IsValidVirtualAddress(address)) {
+    if (!memory.IsValidVirtualAddress(address)) {
         return ERR_INVALID_ADDRESS_STATE;
     }
 
-    const s32 cur_value = static_cast<s32>(Memory::Read32(address));
+    const s32 cur_value = static_cast<s32>(memory.Read32(address));
     if (cur_value >= value) {
         return ERR_INVALID_STATE;
     }
@@ -157,15 +163,19 @@ ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s6
 }
 
 ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
+    auto& memory = system.Memory();
+
     // Ensure that we can read the address.
-    if (!system.Memory().IsValidVirtualAddress(address)) {
+    if (!memory.IsValidVirtualAddress(address)) {
         return ERR_INVALID_ADDRESS_STATE;
     }
+
     // Only wait for the address if equal.
-    if (static_cast<s32>(Memory::Read32(address)) != value) {
+    if (static_cast<s32>(memory.Read32(address)) != value) {
         return ERR_INVALID_STATE;
     }
-    // Short-circuit without rescheduling, if timeout is zero.
+
+    // Short-circuit without rescheduling if timeout is zero.
     if (timeout == 0) {
         return RESULT_TIMEOUT;
     }
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index be24cef065..03745c4491 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -214,10 +214,11 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const HandleTabl
 ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
     auto& owner_process = *thread.GetOwnerProcess();
     auto& handle_table = owner_process.GetHandleTable();
+    auto& memory = Core::System::GetInstance().Memory();
 
     std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf;
-    Memory::ReadBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
-                      dst_cmdbuf.size() * sizeof(u32));
+    memory.ReadBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
+                     dst_cmdbuf.size() * sizeof(u32));
 
     // The header was already built in the internal command buffer. Attempt to parse it to verify
     // the integrity and then copy it over to the target command buffer.
@@ -282,15 +283,14 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
 std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const {
     std::vector<u8> buffer;
     const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()};
+    auto& memory = Core::System::GetInstance().Memory();
 
     if (is_buffer_a) {
         buffer.resize(BufferDescriptorA()[buffer_index].Size());
-        Memory::ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(),
-                          buffer.size());
+        memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size());
     } else {
         buffer.resize(BufferDescriptorX()[buffer_index].Size());
-        Memory::ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(),
-                          buffer.size());
+        memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size());
     }
 
     return buffer;
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index 8493d0f785..88eede4368 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -79,7 +79,7 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
     // thread.
     ASSERT(requesting_thread == current_thread);
 
-    const u32 addr_value = Memory::Read32(address);
+    const u32 addr_value = system.Memory().Read32(address);
 
     // If the mutex isn't being held, just return success.
     if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) {
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 738db528d6..a6c377cfcc 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -454,7 +454,8 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr
     LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}",
               handles_address, handle_count, nano_seconds);
 
-    if (!system.Memory().IsValidVirtualAddress(handles_address)) {
+    auto& memory = system.Memory();
+    if (!memory.IsValidVirtualAddress(handles_address)) {
         LOG_ERROR(Kernel_SVC,
                   "Handle address is not a valid virtual address, handle_address=0x{:016X}",
                   handles_address);
@@ -476,7 +477,7 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr
     const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
 
     for (u64 i = 0; i < handle_count; ++i) {
-        const Handle handle = Memory::Read32(handles_address + i * sizeof(Handle));
+        const Handle handle = memory.Read32(handles_address + i * sizeof(Handle));
         const auto object = handle_table.Get<WaitObject>(handle);
 
         if (object == nullptr) {
@@ -618,13 +619,15 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
             return;
         }
 
+        auto& memory = system.Memory();
+
         // This typically is an error code so we're going to assume this is the case
         if (sz == sizeof(u32)) {
-            LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", Memory::Read32(addr));
+            LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", memory.Read32(addr));
         } else {
             // We don't know what's in here so we'll hexdump it
             debug_buffer.resize(sz);
-            Memory::ReadBlock(addr, debug_buffer.data(), sz);
+            memory.ReadBlock(addr, debug_buffer.data(), sz);
             std::string hexdump;
             for (std::size_t i = 0; i < debug_buffer.size(); i++) {
                 hexdump += fmt::format("{:02X} ", debug_buffer[i]);
@@ -714,7 +717,7 @@ static void OutputDebugString([[maybe_unused]] Core::System& system, VAddr addre
     }
 
     std::string str(len, '\0');
-    Memory::ReadBlock(address, str.data(), str.size());
+    system.Memory().ReadBlock(address, str.data(), str.size());
     LOG_DEBUG(Debug_Emulated, "{}", str);
 }
 
@@ -1674,6 +1677,7 @@ static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_var
 
         const std::size_t current_core = system.CurrentCoreIndex();
         auto& monitor = system.Monitor();
+        auto& memory = system.Memory();
 
         // Atomically read the value of the mutex.
         u32 mutex_val = 0;
@@ -1683,7 +1687,7 @@ static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_var
             monitor.SetExclusive(current_core, mutex_address);
 
             // If the mutex is not yet acquired, acquire it.
-            mutex_val = Memory::Read32(mutex_address);
+            mutex_val = memory.Read32(mutex_address);
 
             if (mutex_val != 0) {
                 update_val = mutex_val | Mutex::MutexHasWaitersFlag;
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 6a29377e31..4fb2cbc4b0 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -43,7 +43,8 @@ public:
     IAudioOut(Core::System& system, AudoutParams audio_params, AudioCore::AudioOut& audio_core,
               std::string&& device_name, std::string&& unique_name)
         : ServiceFramework("IAudioOut"), audio_core(audio_core),
-          device_name(std::move(device_name)), audio_params(audio_params) {
+          device_name(std::move(device_name)),
+          audio_params(audio_params), main_memory{system.Memory()} {
         // clang-format off
         static const FunctionInfo functions[] = {
             {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
@@ -137,7 +138,7 @@ private:
         const u64 tag{rp.Pop<u64>()};
 
         std::vector<s16> samples(audio_buffer.buffer_size / sizeof(s16));
-        Memory::ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size);
+        main_memory.ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size);
 
         if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) {
             IPC::ResponseBuilder rb{ctx, 2};
@@ -209,6 +210,7 @@ private:
 
     /// This is the event handle used to check if the audio buffer was released
     Kernel::EventPair buffer_event;
+    Memory::Memory& main_memory;
 };
 
 AudOutU::AudOutU(Core::System& system_) : ServiceFramework("audout:u"), system{system_} {
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 88f903bfd8..157aeec88e 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -140,9 +140,10 @@ public:
             rb.Push(ERROR_INVALID_SIZE);
             return;
         }
+
         // Read NRR data from memory
         std::vector<u8> nrr_data(nrr_size);
-        Memory::ReadBlock(nrr_address, nrr_data.data(), nrr_size);
+        system.Memory().ReadBlock(nrr_address, nrr_data.data(), nrr_size);
         NRRHeader header;
         std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader));
 
@@ -291,7 +292,7 @@ public:
 
         // Read NRO data from memory
         std::vector<u8> nro_data(nro_size);
-        Memory::ReadBlock(nro_address, nro_data.data(), nro_size);
+        system.Memory().ReadBlock(nro_address, nro_data.data(), nro_size);
 
         SHA256Hash hash{};
         mbedtls_sha256_ret(nro_data.data(), nro_data.size(), hash.data(), 0);
diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp
index 74ecaef1b7..346c8f8992 100644
--- a/src/core/hle/service/lm/lm.cpp
+++ b/src/core/hle/service/lm/lm.cpp
@@ -36,15 +36,15 @@ private:
         MessageHeader header{};
         VAddr addr{ctx.BufferDescriptorX()[0].Address()};
         const VAddr end_addr{addr + ctx.BufferDescriptorX()[0].size};
-        Memory::ReadBlock(addr, &header, sizeof(MessageHeader));
+        memory.ReadBlock(addr, &header, sizeof(MessageHeader));
         addr += sizeof(MessageHeader);
 
         FieldMap fields;
         while (addr < end_addr) {
-            const auto field = static_cast<Field>(Memory::Read8(addr++));
-            const auto length = Memory::Read8(addr++);
+            const auto field = static_cast<Field>(memory.Read8(addr++));
+            const auto length = memory.Read8(addr++);
 
-            if (static_cast<Field>(Memory::Read8(addr)) == Field::Skip) {
+            if (static_cast<Field>(memory.Read8(addr)) == Field::Skip) {
                 ++addr;
             }
 
@@ -55,7 +55,7 @@ private:
             }
 
             std::vector<u8> data(length);
-            Memory::ReadBlock(addr, data.data(), length);
+            memory.ReadBlock(addr, data.data(), length);
             fields.emplace(field, std::move(data));
         }
 
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 9de0ace224..6d8bca8bba 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -191,8 +191,8 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output,
         std::memcpy(entries.data(), input2.data(),
                     params.num_entries * sizeof(Tegra::CommandListHeader));
     } else {
-        Memory::ReadBlock(params.address, entries.data(),
-                          params.num_entries * sizeof(Tegra::CommandListHeader));
+        system.Memory().ReadBlock(params.address, entries.data(),
+                                  params.num_entries * sizeof(Tegra::CommandListHeader));
     }
     UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0);
     UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0);
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index c939e980d2..699c481071 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -59,37 +59,6 @@ u8* GetPointerFromVMA(VAddr vaddr) {
     return ::Memory::GetPointerFromVMA(*Core::System::GetInstance().CurrentProcess(), vaddr);
 }
 
-template <typename T>
-T Read(const VAddr vaddr) {
-    const u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
-    if (page_pointer != nullptr) {
-        // NOTE: Avoid adding any extra logic to this fast-path block
-        T value;
-        std::memcpy(&value, &page_pointer[vaddr & PAGE_MASK], sizeof(T));
-        return value;
-    }
-
-    const Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
-    switch (type) {
-    case Common::PageType::Unmapped:
-        LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr);
-        return 0;
-    case Common::PageType::Memory:
-        ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
-        break;
-    case Common::PageType::RasterizerCachedMemory: {
-        const u8* const host_ptr{GetPointerFromVMA(vaddr)};
-        Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), sizeof(T));
-        T value;
-        std::memcpy(&value, host_ptr, sizeof(T));
-        return value;
-    }
-    default:
-        UNREACHABLE();
-    }
-    return {};
-}
-
 template <typename T>
 void Write(const VAddr vaddr, const T data) {
     u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
@@ -210,6 +179,22 @@ struct Memory::Impl {
         return nullptr;
     }
 
+    u8 Read8(const VAddr addr) {
+        return Read<u8>(addr);
+    }
+
+    u16 Read16(const VAddr addr) {
+        return Read<u16_le>(addr);
+    }
+
+    u32 Read32(const VAddr addr) {
+        return Read<u32_le>(addr);
+    }
+
+    u64 Read64(const VAddr addr) {
+        return Read<u64_le>(addr);
+    }
+
     std::string ReadCString(VAddr vaddr, std::size_t max_length) {
         std::string string;
         string.reserve(max_length);
@@ -225,6 +210,55 @@ struct Memory::Impl {
         return string;
     }
 
+    void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer,
+                   const std::size_t size) {
+        const auto& page_table = process.VMManager().page_table;
+
+        std::size_t remaining_size = size;
+        std::size_t page_index = src_addr >> PAGE_BITS;
+        std::size_t page_offset = src_addr & PAGE_MASK;
+
+        while (remaining_size > 0) {
+            const std::size_t copy_amount =
+                std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
+            const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
+
+            switch (page_table.attributes[page_index]) {
+            case Common::PageType::Unmapped: {
+                LOG_ERROR(HW_Memory,
+                          "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
+                          current_vaddr, src_addr, size);
+                std::memset(dest_buffer, 0, copy_amount);
+                break;
+            }
+            case Common::PageType::Memory: {
+                DEBUG_ASSERT(page_table.pointers[page_index]);
+
+                const u8* const src_ptr = page_table.pointers[page_index] + page_offset;
+                std::memcpy(dest_buffer, src_ptr, copy_amount);
+                break;
+            }
+            case Common::PageType::RasterizerCachedMemory: {
+                const u8* const host_ptr = GetPointerFromVMA(process, current_vaddr);
+                system.GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount);
+                std::memcpy(dest_buffer, host_ptr, copy_amount);
+                break;
+            }
+            default:
+                UNREACHABLE();
+            }
+
+            page_index++;
+            page_offset = 0;
+            dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
+            remaining_size -= copy_amount;
+        }
+    }
+
+    void ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) {
+        ReadBlock(*system.CurrentProcess(), src_addr, dest_buffer, size);
+    }
+
     void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std::size_t size) {
         const auto& page_table = process.VMManager().page_table;
         std::size_t remaining_size = size;
@@ -425,6 +459,48 @@ struct Memory::Impl {
         }
     }
 
+    /**
+     * Reads a particular data type out of memory at the given virtual address.
+     *
+     * @param vaddr The virtual address to read the data type from.
+     *
+     * @tparam T The data type to read out of memory. This type *must* be
+     *           trivially copyable, otherwise the behavior of this function
+     *           is undefined.
+     *
+     * @returns The instance of T read from the specified virtual address.
+     */
+    template <typename T>
+    T Read(const VAddr vaddr) {
+        const u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
+        if (page_pointer != nullptr) {
+            // NOTE: Avoid adding any extra logic to this fast-path block
+            T value;
+            std::memcpy(&value, &page_pointer[vaddr & PAGE_MASK], sizeof(T));
+            return value;
+        }
+
+        const Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
+        switch (type) {
+        case Common::PageType::Unmapped:
+            LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr);
+            return 0;
+        case Common::PageType::Memory:
+            ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
+            break;
+        case Common::PageType::RasterizerCachedMemory: {
+            const u8* const host_ptr = GetPointerFromVMA(vaddr);
+            system.GPU().FlushRegion(ToCacheAddr(host_ptr), sizeof(T));
+            T value;
+            std::memcpy(&value, host_ptr, sizeof(T));
+            return value;
+        }
+        default:
+            UNREACHABLE();
+        }
+        return {};
+    }
+
     Core::System& system;
 };
 
@@ -470,10 +546,35 @@ const u8* Memory::GetPointer(VAddr vaddr) const {
     return impl->GetPointer(vaddr);
 }
 
+u8 Memory::Read8(const VAddr addr) {
+    return impl->Read8(addr);
+}
+
+u16 Memory::Read16(const VAddr addr) {
+    return impl->Read16(addr);
+}
+
+u32 Memory::Read32(const VAddr addr) {
+    return impl->Read32(addr);
+}
+
+u64 Memory::Read64(const VAddr addr) {
+    return impl->Read64(addr);
+}
+
 std::string Memory::ReadCString(VAddr vaddr, std::size_t max_length) {
     return impl->ReadCString(vaddr, max_length);
 }
 
+void Memory::ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer,
+                       const std::size_t size) {
+    impl->ReadBlock(process, src_addr, dest_buffer, size);
+}
+
+void Memory::ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) {
+    impl->ReadBlock(src_addr, dest_buffer, size);
+}
+
 void Memory::ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size) {
     impl->ZeroBlock(process, dest_addr, size);
 }
@@ -511,71 +612,6 @@ bool IsKernelVirtualAddress(const VAddr vaddr) {
     return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END;
 }
 
-u8 Read8(const VAddr addr) {
-    return Read<u8>(addr);
-}
-
-u16 Read16(const VAddr addr) {
-    return Read<u16_le>(addr);
-}
-
-u32 Read32(const VAddr addr) {
-    return Read<u32_le>(addr);
-}
-
-u64 Read64(const VAddr addr) {
-    return Read<u64_le>(addr);
-}
-
-void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer,
-               const std::size_t size) {
-    const auto& page_table = process.VMManager().page_table;
-
-    std::size_t remaining_size = size;
-    std::size_t page_index = src_addr >> PAGE_BITS;
-    std::size_t page_offset = src_addr & PAGE_MASK;
-
-    while (remaining_size > 0) {
-        const std::size_t copy_amount =
-            std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
-        const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
-
-        switch (page_table.attributes[page_index]) {
-        case Common::PageType::Unmapped: {
-            LOG_ERROR(HW_Memory,
-                      "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
-                      current_vaddr, src_addr, size);
-            std::memset(dest_buffer, 0, copy_amount);
-            break;
-        }
-        case Common::PageType::Memory: {
-            DEBUG_ASSERT(page_table.pointers[page_index]);
-
-            const u8* src_ptr = page_table.pointers[page_index] + page_offset;
-            std::memcpy(dest_buffer, src_ptr, copy_amount);
-            break;
-        }
-        case Common::PageType::RasterizerCachedMemory: {
-            const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
-            Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount);
-            std::memcpy(dest_buffer, host_ptr, copy_amount);
-            break;
-        }
-        default:
-            UNREACHABLE();
-        }
-
-        page_index++;
-        page_offset = 0;
-        dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
-        remaining_size -= copy_amount;
-    }
-}
-
-void ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) {
-    ReadBlock(*Core::System::GetInstance().CurrentProcess(), src_addr, dest_buffer, size);
-}
-
 void Write8(const VAddr addr, const u8 data) {
     Write<u8>(addr, data);
 }
diff --git a/src/core/memory.h b/src/core/memory.h
index fc0013a965..cc6ab920ea 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -152,6 +152,46 @@ public:
      */
     const u8* GetPointer(VAddr vaddr) const;
 
+    /**
+     * Reads an 8-bit unsigned value from the current process' address space
+     * at the given virtual address.
+     *
+     * @param addr The virtual address to read the 8-bit value from.
+     *
+     * @returns the read 8-bit unsigned value.
+     */
+    u8 Read8(VAddr addr);
+
+    /**
+     * Reads a 16-bit unsigned value from the current process' address space
+     * at the given virtual address.
+     *
+     * @param addr The virtual address to read the 16-bit value from.
+     *
+     * @returns the read 16-bit unsigned value.
+     */
+    u16 Read16(VAddr addr);
+
+    /**
+     * Reads a 32-bit unsigned value from the current process' address space
+     * at the given virtual address.
+     *
+     * @param addr The virtual address to read the 32-bit value from.
+     *
+     * @returns the read 32-bit unsigned value.
+     */
+    u32 Read32(VAddr addr);
+
+    /**
+     * Reads a 64-bit unsigned value from the current process' address space
+     * at the given virtual address.
+     *
+     * @param addr The virtual address to read the 64-bit value from.
+     *
+     * @returns the read 64-bit value.
+     */
+    u64 Read64(VAddr addr);
+
     /**
      * Reads a null-terminated string from the given virtual address.
      * This function will continually read characters until either:
@@ -169,6 +209,44 @@ public:
      */
     std::string ReadCString(VAddr vaddr, std::size_t max_length);
 
+    /**
+     * Reads a contiguous block of bytes from a specified process' address space.
+     *
+     * @param process     The process to read the data from.
+     * @param src_addr    The virtual address to begin reading from.
+     * @param dest_buffer The buffer to place the read bytes into.
+     * @param size        The amount of data to read, in bytes.
+     *
+     * @note If a size of 0 is specified, then this function reads nothing and
+     *       no attempts to access memory are made at all.
+     *
+     * @pre dest_buffer must be at least size bytes in length, otherwise a
+     *      buffer overrun will occur.
+     *
+     * @post The range [dest_buffer, size) contains the read bytes from the
+     *       process' address space.
+     */
+    void ReadBlock(const Kernel::Process& process, VAddr src_addr, void* dest_buffer,
+                   std::size_t size);
+
+    /**
+     * Reads a contiguous block of bytes from the current process' address space.
+     *
+     * @param src_addr    The virtual address to begin reading from.
+     * @param dest_buffer The buffer to place the read bytes into.
+     * @param size        The amount of data to read, in bytes.
+     *
+     * @note If a size of 0 is specified, then this function reads nothing and
+     *       no attempts to access memory are made at all.
+     *
+     * @pre dest_buffer must be at least size bytes in length, otherwise a
+     *      buffer overrun will occur.
+     *
+     * @post The range [dest_buffer, size) contains the read bytes from the
+     *       current process' address space.
+     */
+    void ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size);
+
     /**
      * Fills the specified address range within a process' address space with zeroes.
      *
@@ -242,18 +320,11 @@ void SetCurrentPageTable(Kernel::Process& process);
 /// Determines if the given VAddr is a kernel address
 bool IsKernelVirtualAddress(VAddr vaddr);
 
-u8 Read8(VAddr addr);
-u16 Read16(VAddr addr);
-u32 Read32(VAddr addr);
-u64 Read64(VAddr addr);
-
 void Write8(VAddr addr, u8 data);
 void Write16(VAddr addr, u16 data);
 void Write32(VAddr addr, u32 data);
 void Write64(VAddr addr, u64 data);
 
-void ReadBlock(const Kernel::Process& process, VAddr src_addr, void* dest_buffer, std::size_t size);
-void ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size);
 void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer,
                 std::size_t size);
 void WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size);
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index b73cc9fbd9..d6745af8bb 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -20,14 +20,13 @@ namespace Memory {
 constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 12);
 constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
 
-StandardVmCallbacks::StandardVmCallbacks(const Core::System& system,
-                                         const CheatProcessMetadata& metadata)
+StandardVmCallbacks::StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata)
     : metadata(metadata), system(system) {}
 
 StandardVmCallbacks::~StandardVmCallbacks() = default;
 
 void StandardVmCallbacks::MemoryRead(VAddr address, void* data, u64 size) {
-    ReadBlock(SanitizeAddress(address), data, size);
+    system.Memory().ReadBlock(SanitizeAddress(address), data, size);
 }
 
 void StandardVmCallbacks::MemoryWrite(VAddr address, const void* data, u64 size) {
diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h
index e3db90dacc..3d6b2298ad 100644
--- a/src/core/memory/cheat_engine.h
+++ b/src/core/memory/cheat_engine.h
@@ -24,7 +24,7 @@ namespace Memory {
 
 class StandardVmCallbacks : public DmntCheatVm::Callbacks {
 public:
-    StandardVmCallbacks(const Core::System& system, const CheatProcessMetadata& metadata);
+    StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata);
     ~StandardVmCallbacks() override;
 
     void MemoryRead(VAddr address, void* data, u64 size) override;
@@ -37,7 +37,7 @@ private:
     VAddr SanitizeAddress(VAddr address) const;
 
     const CheatProcessMetadata& metadata;
-    const Core::System& system;
+    Core::System& system;
 };
 
 // Intermediary class that parses a text file or other disk format for storing cheats into a
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp
index af0988d623..f95eee3b1f 100644
--- a/src/core/reporter.cpp
+++ b/src/core/reporter.cpp
@@ -157,7 +157,7 @@ json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& buffer, Memor
 
         if constexpr (read_value) {
             std::vector<u8> data(desc.Size());
-            Memory::ReadBlock(desc.Address(), data.data(), desc.Size());
+            memory.ReadBlock(desc.Address(), data.data(), desc.Size());
             entry["data"] = Common::HexToString(data);
         }
 
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp
index c7f42388f1..ab66f35f99 100644
--- a/src/core/tools/freezer.cpp
+++ b/src/core/tools/freezer.cpp
@@ -18,13 +18,13 @@ constexpr s64 MEMORY_FREEZER_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_R
 u64 MemoryReadWidth(Memory::Memory& memory, u32 width, VAddr addr) {
     switch (width) {
     case 1:
-        return Memory::Read8(addr);
+        return memory.Read8(addr);
     case 2:
-        return Memory::Read16(addr);
+        return memory.Read16(addr);
     case 4:
-        return Memory::Read32(addr);
+        return memory.Read32(addr);
     case 8:
-        return Memory::Read64(addr);
+        return memory.Read64(addr);
     default:
         UNREACHABLE();
         return 0;
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index 535b3ce903..727bd8a943 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -80,7 +80,7 @@ QString WaitTreeText::GetText() const {
 
 WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address, const Kernel::HandleTable& handle_table)
     : mutex_address(mutex_address) {
-    mutex_value = Memory::Read32(mutex_address);
+    mutex_value = Core::System::GetInstance().Memory().Read32(mutex_address);
     owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Mutex::MutexOwnerMask);
     owner = handle_table.Get<Kernel::Thread>(owner_handle);
 }
@@ -115,10 +115,11 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() cons
     std::vector<std::unique_ptr<WaitTreeItem>> list;
 
     constexpr std::size_t BaseRegister = 29;
+    auto& memory = Core::System::GetInstance().Memory();
     u64 base_pointer = thread.GetContext().cpu_registers[BaseRegister];
 
     while (base_pointer != 0) {
-        const u64 lr = Memory::Read64(base_pointer + sizeof(u64));
+        const u64 lr = memory.Read64(base_pointer + sizeof(u64));
         if (lr == 0) {
             break;
         }
@@ -126,7 +127,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() cons
         list.push_back(std::make_unique<WaitTreeText>(
             tr("0x%1").arg(lr - sizeof(u32), 16, 16, QLatin1Char{'0'})));
 
-        base_pointer = Memory::Read64(base_pointer);
+        base_pointer = memory.Read64(base_pointer);
     }
 
     return list;

From e4c381b8850db96f162cfcf2cbe28b0e7c1f76f1 Mon Sep 17 00:00:00 2001
From: Lioncash <mathew1800@gmail.com>
Date: Tue, 26 Nov 2019 17:39:57 -0500
Subject: [PATCH 11/13] core/memory: Migrate over Write{8, 16, 32, 64, Block}
 to the Memory class

The Write functions are used slightly less than the Read functions,
which make these a bit nicer to move over.

The only adjustments we really need to make here are to Dynarmic's
exclusive monitor instance. We need to keep a reference to the currently
active memory instance to perform exclusive read/write operations.
---
 src/core/arm/dynarmic/arm_dynarmic.cpp  |  30 ++--
 src/core/arm/dynarmic/arm_dynarmic.h    |   7 +-
 src/core/core_cpu.cpp                   |   5 +-
 src/core/core_cpu.h                     |  18 +-
 src/core/cpu_core_manager.cpp           |   2 +-
 src/core/gdbstub/gdbstub.cpp            |  16 +-
 src/core/hle/kernel/address_arbiter.cpp |   6 +-
 src/core/hle/kernel/hle_ipc.cpp         |   9 +-
 src/core/hle/kernel/mutex.cpp           |   4 +-
 src/core/hle/kernel/svc.cpp             |  27 +--
 src/core/memory.cpp                     | 220 ++++++++++++++----------
 src/core/memory.h                       |  97 ++++++++++-
 src/core/memory/cheat_engine.cpp        |   2 +-
 src/core/tools/freezer.cpp              |   8 +-
 14 files changed, 298 insertions(+), 153 deletions(-)

diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index 585fb55a90..f8c7f0efd6 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -45,20 +45,21 @@ public:
     }
 
     void MemoryWrite8(u64 vaddr, u8 value) override {
-        Memory::Write8(vaddr, value);
+        parent.system.Memory().Write8(vaddr, value);
     }
     void MemoryWrite16(u64 vaddr, u16 value) override {
-        Memory::Write16(vaddr, value);
+        parent.system.Memory().Write16(vaddr, value);
     }
     void MemoryWrite32(u64 vaddr, u32 value) override {
-        Memory::Write32(vaddr, value);
+        parent.system.Memory().Write32(vaddr, value);
     }
     void MemoryWrite64(u64 vaddr, u64 value) override {
-        Memory::Write64(vaddr, value);
+        parent.system.Memory().Write64(vaddr, value);
     }
     void MemoryWrite128(u64 vaddr, Vector value) override {
-        Memory::Write64(vaddr, value[0]);
-        Memory::Write64(vaddr + 8, value[1]);
+        auto& memory = parent.system.Memory();
+        memory.Write64(vaddr, value[0]);
+        memory.Write64(vaddr + 8, value[1]);
     }
 
     void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
@@ -266,7 +267,9 @@ void ARM_Dynarmic::PageTableChanged(Common::PageTable& page_table,
     jit = MakeJit(page_table, new_address_space_size_in_bits);
 }
 
-DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(std::size_t core_count) : monitor(core_count) {}
+DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(Memory::Memory& memory_, std::size_t core_count)
+    : monitor(core_count), memory{memory_} {}
+
 DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default;
 
 void DynarmicExclusiveMonitor::SetExclusive(std::size_t core_index, VAddr addr) {
@@ -279,29 +282,28 @@ void DynarmicExclusiveMonitor::ClearExclusive() {
 }
 
 bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) {
-    return monitor.DoExclusiveOperation(core_index, vaddr, 1,
-                                        [&] { Memory::Write8(vaddr, value); });
+    return monitor.DoExclusiveOperation(core_index, vaddr, 1, [&] { memory.Write8(vaddr, value); });
 }
 
 bool DynarmicExclusiveMonitor::ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) {
     return monitor.DoExclusiveOperation(core_index, vaddr, 2,
-                                        [&] { Memory::Write16(vaddr, value); });
+                                        [&] { memory.Write16(vaddr, value); });
 }
 
 bool DynarmicExclusiveMonitor::ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) {
     return monitor.DoExclusiveOperation(core_index, vaddr, 4,
-                                        [&] { Memory::Write32(vaddr, value); });
+                                        [&] { memory.Write32(vaddr, value); });
 }
 
 bool DynarmicExclusiveMonitor::ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) {
     return monitor.DoExclusiveOperation(core_index, vaddr, 8,
-                                        [&] { Memory::Write64(vaddr, value); });
+                                        [&] { memory.Write64(vaddr, value); });
 }
 
 bool DynarmicExclusiveMonitor::ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) {
     return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&] {
-        Memory::Write64(vaddr + 0, value[0]);
-        Memory::Write64(vaddr + 8, value[1]);
+        memory.Write64(vaddr + 0, value[0]);
+        memory.Write64(vaddr + 8, value[1]);
     });
 }
 
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index d08de475f2..9cd475cfb6 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -12,6 +12,10 @@
 #include "core/arm/exclusive_monitor.h"
 #include "core/arm/unicorn/arm_unicorn.h"
 
+namespace Memory {
+class Memory;
+}
+
 namespace Core {
 
 class ARM_Dynarmic_Callbacks;
@@ -63,7 +67,7 @@ private:
 
 class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
 public:
-    explicit DynarmicExclusiveMonitor(std::size_t core_count);
+    explicit DynarmicExclusiveMonitor(Memory::Memory& memory_, std::size_t core_count);
     ~DynarmicExclusiveMonitor() override;
 
     void SetExclusive(std::size_t core_index, VAddr addr) override;
@@ -78,6 +82,7 @@ public:
 private:
     friend class ARM_Dynarmic;
     Dynarmic::A64::ExclusiveMonitor monitor;
+    Memory::Memory& memory;
 };
 
 } // namespace Core
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp
index 233ea572ca..cf3fe0b0b0 100644
--- a/src/core/core_cpu.cpp
+++ b/src/core/core_cpu.cpp
@@ -66,9 +66,10 @@ Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_ba
 
 Cpu::~Cpu() = default;
 
-std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) {
+std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(
+    [[maybe_unused]] Memory::Memory& memory, [[maybe_unused]] std::size_t num_cores) {
 #ifdef ARCHITECTURE_x86_64
-    return std::make_unique<DynarmicExclusiveMonitor>(num_cores);
+    return std::make_unique<DynarmicExclusiveMonitor>(memory, num_cores);
 #else
     // TODO(merry): Passthrough exclusive monitor
     return nullptr;
diff --git a/src/core/core_cpu.h b/src/core/core_cpu.h
index cafca8df7f..78f5021a22 100644
--- a/src/core/core_cpu.h
+++ b/src/core/core_cpu.h
@@ -24,6 +24,10 @@ namespace Core::Timing {
 class CoreTiming;
 }
 
+namespace Memory {
+class Memory;
+}
+
 namespace Core {
 
 class ARM_Interface;
@@ -86,7 +90,19 @@ public:
 
     void Shutdown();
 
-    static std::unique_ptr<ExclusiveMonitor> MakeExclusiveMonitor(std::size_t num_cores);
+    /**
+     * Creates an exclusive monitor to handle exclusive reads/writes.
+     *
+     * @param memory The current memory subsystem that the monitor may wish
+     *               to keep track of.
+     *
+     * @param num_cores The number of cores to assume about the CPU.
+     *
+     * @returns The constructed exclusive monitor instance, or nullptr if the current
+     *          CPU backend is unable to use an exclusive monitor.
+     */
+    static std::unique_ptr<ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory,
+                                                                  std::size_t num_cores);
 
 private:
     void Reschedule();
diff --git a/src/core/cpu_core_manager.cpp b/src/core/cpu_core_manager.cpp
index 8efd410bba..f04a341336 100644
--- a/src/core/cpu_core_manager.cpp
+++ b/src/core/cpu_core_manager.cpp
@@ -25,7 +25,7 @@ CpuCoreManager::~CpuCoreManager() = default;
 
 void CpuCoreManager::Initialize() {
     barrier = std::make_unique<CpuBarrier>();
-    exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size());
+    exclusive_monitor = Cpu::MakeExclusiveMonitor(system.Memory(), cores.size());
 
     for (std::size_t index = 0; index < cores.size(); ++index) {
         cores[index] = std::make_unique<Cpu>(system, *exclusive_monitor, *barrier, index);
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index 1c74a44d82..37cb288483 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -508,8 +508,9 @@ static void RemoveBreakpoint(BreakpointType type, VAddr addr) {
               bp->second.len, bp->second.addr, static_cast<int>(type));
 
     if (type == BreakpointType::Execute) {
-        Memory::WriteBlock(bp->second.addr, bp->second.inst.data(), bp->second.inst.size());
-        Core::System::GetInstance().InvalidateCpuInstructionCaches();
+        auto& system = Core::System::GetInstance();
+        system.Memory().WriteBlock(bp->second.addr, bp->second.inst.data(), bp->second.inst.size());
+        system.InvalidateCpuInstructionCaches();
     }
     p.erase(addr);
 }
@@ -993,14 +994,14 @@ static void WriteMemory() {
     const u64 len = HexToLong(start_offset, static_cast<u64>(len_pos - start_offset));
 
     auto& system = Core::System::GetInstance();
-    const auto& memory = system.Memory();
+    auto& memory = system.Memory();
     if (!memory.IsValidVirtualAddress(addr)) {
         return SendReply("E00");
     }
 
     std::vector<u8> data(len);
     GdbHexToMem(data.data(), len_pos + 1, len);
-    Memory::WriteBlock(addr, data.data(), len);
+    memory.WriteBlock(addr, data.data(), len);
     system.InvalidateCpuInstructionCaches();
     SendReply("OK");
 }
@@ -1058,13 +1059,14 @@ static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) {
     breakpoint.addr = addr;
     breakpoint.len = len;
 
-    auto& memory = Core::System::GetInstance().Memory();
+    auto& system = Core::System::GetInstance();
+    auto& memory = system.Memory();
     memory.ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size());
 
     static constexpr std::array<u8, 4> btrap{0x00, 0x7d, 0x20, 0xd4};
     if (type == BreakpointType::Execute) {
-        Memory::WriteBlock(addr, btrap.data(), btrap.size());
-        Core::System::GetInstance().InvalidateCpuInstructionCaches();
+        memory.WriteBlock(addr, btrap.data(), btrap.size());
+        system.InvalidateCpuInstructionCaches();
     }
     p.insert({addr, breakpoint});
 
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index 07f0dac67a..98d07fa5b1 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -78,7 +78,7 @@ ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32
         return ERR_INVALID_STATE;
     }
 
-    Memory::Write32(address, static_cast<u32>(value + 1));
+    memory.Write32(address, static_cast<u32>(value + 1));
     return SignalToAddressOnly(address, num_to_wake);
 }
 
@@ -117,7 +117,7 @@ ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr a
         return ERR_INVALID_STATE;
     }
 
-    Memory::Write32(address, static_cast<u32>(updated_value));
+    memory.Write32(address, static_cast<u32>(updated_value));
     WakeThreads(waiting_threads, num_to_wake);
     return RESULT_SUCCESS;
 }
@@ -151,7 +151,7 @@ ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s6
     }
 
     if (should_decrement) {
-        Memory::Write32(address, static_cast<u32>(cur_value - 1));
+        memory.Write32(address, static_cast<u32>(cur_value - 1));
     }
 
     // Short-circuit without rescheduling, if timeout is zero.
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 03745c4491..8b01567a84 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -274,8 +274,8 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
     }
 
     // Copy the translated command buffer back into the thread's command buffer area.
-    Memory::WriteBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
-                       dst_cmdbuf.size() * sizeof(u32));
+    memory.WriteBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
+                      dst_cmdbuf.size() * sizeof(u32));
 
     return RESULT_SUCCESS;
 }
@@ -311,10 +311,11 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
         size = buffer_size; // TODO(bunnei): This needs to be HW tested
     }
 
+    auto& memory = Core::System::GetInstance().Memory();
     if (is_buffer_b) {
-        Memory::WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
+        memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
     } else {
-        Memory::WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);
+        memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);
     }
 
     return size;
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index 88eede4368..061e9bcb02 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -117,7 +117,7 @@ ResultCode Mutex::Release(VAddr address) {
 
     // There are no more threads waiting for the mutex, release it completely.
     if (thread == nullptr) {
-        Memory::Write32(address, 0);
+        system.Memory().Write32(address, 0);
         return RESULT_SUCCESS;
     }
 
@@ -132,7 +132,7 @@ ResultCode Mutex::Release(VAddr address) {
     }
 
     // Grant the mutex to the next waiting thread and resume it.
-    Memory::Write32(address, mutex_value);
+    system.Memory().Write32(address, mutex_value);
 
     ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
     thread->ResumeFromWait();
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index a6c377cfcc..db3ae3eb88 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -1120,7 +1120,7 @@ static ResultCode GetThreadContext(Core::System& system, VAddr thread_context, H
         std::fill(ctx.vector_registers.begin() + 16, ctx.vector_registers.end(), u128{});
     }
 
-    Memory::WriteBlock(thread_context, &ctx, sizeof(ctx));
+    system.Memory().WriteBlock(thread_context, &ctx, sizeof(ctx));
     return RESULT_SUCCESS;
 }
 
@@ -1280,20 +1280,21 @@ static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_add
         return ERR_INVALID_HANDLE;
     }
 
+    auto& memory = system.Memory();
     const auto& vm_manager = process->VMManager();
     const MemoryInfo memory_info = vm_manager.QueryMemory(address);
 
-    Memory::Write64(memory_info_address, memory_info.base_address);
-    Memory::Write64(memory_info_address + 8, memory_info.size);
-    Memory::Write32(memory_info_address + 16, memory_info.state);
-    Memory::Write32(memory_info_address + 20, memory_info.attributes);
-    Memory::Write32(memory_info_address + 24, memory_info.permission);
-    Memory::Write32(memory_info_address + 32, memory_info.ipc_ref_count);
-    Memory::Write32(memory_info_address + 28, memory_info.device_ref_count);
-    Memory::Write32(memory_info_address + 36, 0);
+    memory.Write64(memory_info_address, memory_info.base_address);
+    memory.Write64(memory_info_address + 8, memory_info.size);
+    memory.Write32(memory_info_address + 16, memory_info.state);
+    memory.Write32(memory_info_address + 20, memory_info.attributes);
+    memory.Write32(memory_info_address + 24, memory_info.permission);
+    memory.Write32(memory_info_address + 32, memory_info.ipc_ref_count);
+    memory.Write32(memory_info_address + 28, memory_info.device_ref_count);
+    memory.Write32(memory_info_address + 36, 0);
 
     // Page info appears to be currently unused by the kernel and is always set to zero.
-    Memory::Write32(page_info_address, 0);
+    memory.Write32(page_info_address, 0);
 
     return RESULT_SUCCESS;
 }
@@ -2290,12 +2291,13 @@ static ResultCode GetProcessList(Core::System& system, u32* out_num_processes,
         return ERR_INVALID_ADDRESS_STATE;
     }
 
+    auto& memory = system.Memory();
     const auto& process_list = kernel.GetProcessList();
     const auto num_processes = process_list.size();
     const auto copy_amount = std::min(std::size_t{out_process_ids_size}, num_processes);
 
     for (std::size_t i = 0; i < copy_amount; ++i) {
-        Memory::Write64(out_process_ids, process_list[i]->GetProcessID());
+        memory.Write64(out_process_ids, process_list[i]->GetProcessID());
         out_process_ids += sizeof(u64);
     }
 
@@ -2329,13 +2331,14 @@ static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAdd
         return ERR_INVALID_ADDRESS_STATE;
     }
 
+    auto& memory = system.Memory();
     const auto& thread_list = current_process->GetThreadList();
     const auto num_threads = thread_list.size();
     const auto copy_amount = std::min(std::size_t{out_thread_ids_size}, num_threads);
 
     auto list_iter = thread_list.cbegin();
     for (std::size_t i = 0; i < copy_amount; ++i, ++list_iter) {
-        Memory::Write64(out_thread_ids, (*list_iter)->GetThreadID());
+        memory.Write64(out_thread_ids, (*list_iter)->GetThreadID());
         out_thread_ids += sizeof(u64);
     }
 
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 699c481071..5c940a82e8 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -58,35 +58,6 @@ u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) {
 u8* GetPointerFromVMA(VAddr vaddr) {
     return ::Memory::GetPointerFromVMA(*Core::System::GetInstance().CurrentProcess(), vaddr);
 }
-
-template <typename T>
-void Write(const VAddr vaddr, const T data) {
-    u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
-    if (page_pointer != nullptr) {
-        // NOTE: Avoid adding any extra logic to this fast-path block
-        std::memcpy(&page_pointer[vaddr & PAGE_MASK], &data, sizeof(T));
-        return;
-    }
-
-    Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
-    switch (type) {
-    case Common::PageType::Unmapped:
-        LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
-                  static_cast<u32>(data), vaddr);
-        return;
-    case Common::PageType::Memory:
-        ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
-        break;
-    case Common::PageType::RasterizerCachedMemory: {
-        u8* const host_ptr{GetPointerFromVMA(vaddr)};
-        Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), sizeof(T));
-        std::memcpy(host_ptr, &data, sizeof(T));
-        break;
-    }
-    default:
-        UNREACHABLE();
-    }
-}
 } // Anonymous namespace
 
 // Implementation class used to keep the specifics of the memory subsystem hidden
@@ -195,6 +166,22 @@ struct Memory::Impl {
         return Read<u64_le>(addr);
     }
 
+    void Write8(const VAddr addr, const u8 data) {
+        Write<u8>(addr, data);
+    }
+
+    void Write16(const VAddr addr, const u16 data) {
+        Write<u16_le>(addr, data);
+    }
+
+    void Write32(const VAddr addr, const u32 data) {
+        Write<u32_le>(addr, data);
+    }
+
+    void Write64(const VAddr addr, const u64 data) {
+        Write<u64_le>(addr, data);
+    }
+
     std::string ReadCString(VAddr vaddr, std::size_t max_length) {
         std::string string;
         string.reserve(max_length);
@@ -259,6 +246,53 @@ struct Memory::Impl {
         ReadBlock(*system.CurrentProcess(), src_addr, dest_buffer, size);
     }
 
+    void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer,
+                    const std::size_t size) {
+        const auto& page_table = process.VMManager().page_table;
+        std::size_t remaining_size = size;
+        std::size_t page_index = dest_addr >> PAGE_BITS;
+        std::size_t page_offset = dest_addr & PAGE_MASK;
+
+        while (remaining_size > 0) {
+            const std::size_t copy_amount =
+                std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
+            const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
+
+            switch (page_table.attributes[page_index]) {
+            case Common::PageType::Unmapped: {
+                LOG_ERROR(HW_Memory,
+                          "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
+                          current_vaddr, dest_addr, size);
+                break;
+            }
+            case Common::PageType::Memory: {
+                DEBUG_ASSERT(page_table.pointers[page_index]);
+
+                u8* const dest_ptr = page_table.pointers[page_index] + page_offset;
+                std::memcpy(dest_ptr, src_buffer, copy_amount);
+                break;
+            }
+            case Common::PageType::RasterizerCachedMemory: {
+                u8* const host_ptr = GetPointerFromVMA(process, current_vaddr);
+                system.GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount);
+                std::memcpy(host_ptr, src_buffer, copy_amount);
+                break;
+            }
+            default:
+                UNREACHABLE();
+            }
+
+            page_index++;
+            page_offset = 0;
+            src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
+            remaining_size -= copy_amount;
+        }
+    }
+
+    void WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) {
+        WriteBlock(*system.CurrentProcess(), dest_addr, src_buffer, size);
+    }
+
     void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std::size_t size) {
         const auto& page_table = process.VMManager().page_table;
         std::size_t remaining_size = size;
@@ -501,6 +535,46 @@ struct Memory::Impl {
         return {};
     }
 
+    /**
+     * Writes a particular data type to memory at the given virtual address.
+     *
+     * @param vaddr The virtual address to write the data type to.
+     *
+     * @tparam T The data type to write to memory. This type *must* be
+     *           trivially copyable, otherwise the behavior of this function
+     *           is undefined.
+     *
+     * @returns The instance of T write to the specified virtual address.
+     */
+    template <typename T>
+    void Write(const VAddr vaddr, const T data) {
+        u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
+        if (page_pointer != nullptr) {
+            // NOTE: Avoid adding any extra logic to this fast-path block
+            std::memcpy(&page_pointer[vaddr & PAGE_MASK], &data, sizeof(T));
+            return;
+        }
+
+        const Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
+        switch (type) {
+        case Common::PageType::Unmapped:
+            LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
+                      static_cast<u32>(data), vaddr);
+            return;
+        case Common::PageType::Memory:
+            ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
+            break;
+        case Common::PageType::RasterizerCachedMemory: {
+            u8* const host_ptr{GetPointerFromVMA(vaddr)};
+            system.GPU().InvalidateRegion(ToCacheAddr(host_ptr), sizeof(T));
+            std::memcpy(host_ptr, &data, sizeof(T));
+            break;
+        }
+        default:
+            UNREACHABLE();
+        }
+    }
+
     Core::System& system;
 };
 
@@ -562,6 +636,22 @@ u64 Memory::Read64(const VAddr addr) {
     return impl->Read64(addr);
 }
 
+void Memory::Write8(VAddr addr, u8 data) {
+    impl->Write8(addr, data);
+}
+
+void Memory::Write16(VAddr addr, u16 data) {
+    impl->Write16(addr, data);
+}
+
+void Memory::Write32(VAddr addr, u32 data) {
+    impl->Write32(addr, data);
+}
+
+void Memory::Write64(VAddr addr, u64 data) {
+    impl->Write64(addr, data);
+}
+
 std::string Memory::ReadCString(VAddr vaddr, std::size_t max_length) {
     return impl->ReadCString(vaddr, max_length);
 }
@@ -575,6 +665,15 @@ void Memory::ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_
     impl->ReadBlock(src_addr, dest_buffer, size);
 }
 
+void Memory::WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer,
+                        std::size_t size) {
+    impl->WriteBlock(process, dest_addr, src_buffer, size);
+}
+
+void Memory::WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) {
+    impl->WriteBlock(dest_addr, src_buffer, size);
+}
+
 void Memory::ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size) {
     impl->ZeroBlock(process, dest_addr, size);
 }
@@ -612,67 +711,4 @@ bool IsKernelVirtualAddress(const VAddr vaddr) {
     return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END;
 }
 
-void Write8(const VAddr addr, const u8 data) {
-    Write<u8>(addr, data);
-}
-
-void Write16(const VAddr addr, const u16 data) {
-    Write<u16_le>(addr, data);
-}
-
-void Write32(const VAddr addr, const u32 data) {
-    Write<u32_le>(addr, data);
-}
-
-void Write64(const VAddr addr, const u64 data) {
-    Write<u64_le>(addr, data);
-}
-
-void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer,
-                const std::size_t size) {
-    const auto& page_table = process.VMManager().page_table;
-    std::size_t remaining_size = size;
-    std::size_t page_index = dest_addr >> PAGE_BITS;
-    std::size_t page_offset = dest_addr & PAGE_MASK;
-
-    while (remaining_size > 0) {
-        const std::size_t copy_amount =
-            std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
-        const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
-
-        switch (page_table.attributes[page_index]) {
-        case Common::PageType::Unmapped: {
-            LOG_ERROR(HW_Memory,
-                      "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
-                      current_vaddr, dest_addr, size);
-            break;
-        }
-        case Common::PageType::Memory: {
-            DEBUG_ASSERT(page_table.pointers[page_index]);
-
-            u8* dest_ptr = page_table.pointers[page_index] + page_offset;
-            std::memcpy(dest_ptr, src_buffer, copy_amount);
-            break;
-        }
-        case Common::PageType::RasterizerCachedMemory: {
-            const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
-            Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount);
-            std::memcpy(host_ptr, src_buffer, copy_amount);
-            break;
-        }
-        default:
-            UNREACHABLE();
-        }
-
-        page_index++;
-        page_offset = 0;
-        src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
-        remaining_size -= copy_amount;
-    }
-}
-
-void WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) {
-    WriteBlock(*Core::System::GetInstance().CurrentProcess(), dest_addr, src_buffer, size);
-}
-
 } // namespace Memory
diff --git a/src/core/memory.h b/src/core/memory.h
index cc6ab920ea..7878f3fb18 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -192,6 +192,50 @@ public:
      */
     u64 Read64(VAddr addr);
 
+    /**
+     * Writes an 8-bit unsigned integer to the given virtual address in
+     * the current process' address space.
+     *
+     * @param addr The virtual address to write the 8-bit unsigned integer to.
+     * @param data The 8-bit unsigned integer to write to the given virtual address.
+     *
+     * @post The memory at the given virtual address contains the specified data value.
+     */
+    void Write8(VAddr addr, u8 data);
+
+    /**
+     * Writes a 16-bit unsigned integer to the given virtual address in
+     * the current process' address space.
+     *
+     * @param addr The virtual address to write the 16-bit unsigned integer to.
+     * @param data The 16-bit unsigned integer to write to the given virtual address.
+     *
+     * @post The memory range [addr, sizeof(data)) contains the given data value.
+     */
+    void Write16(VAddr addr, u16 data);
+
+    /**
+     * Writes a 32-bit unsigned integer to the given virtual address in
+     * the current process' address space.
+     *
+     * @param addr The virtual address to write the 32-bit unsigned integer to.
+     * @param data The 32-bit unsigned integer to write to the given virtual address.
+     *
+     * @post The memory range [addr, sizeof(data)) contains the given data value.
+     */
+    void Write32(VAddr addr, u32 data);
+
+    /**
+     * Writes a 64-bit unsigned integer to the given virtual address in
+     * the current process' address space.
+     *
+     * @param addr The virtual address to write the 64-bit unsigned integer to.
+     * @param data The 64-bit unsigned integer to write to the given virtual address.
+     *
+     * @post The memory range [addr, sizeof(data)) contains the given data value.
+     */
+    void Write64(VAddr addr, u64 data);
+
     /**
      * Reads a null-terminated string from the given virtual address.
      * This function will continually read characters until either:
@@ -247,6 +291,50 @@ public:
      */
     void ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size);
 
+    /**
+     * Writes a range of bytes into a given process' address space at the specified
+     * virtual address.
+     *
+     * @param process    The process to write data into the address space of.
+     * @param dest_addr  The destination virtual address to begin writing the data at.
+     * @param src_buffer The data to write into the process' address space.
+     * @param size       The size of the data to write, in bytes.
+     *
+     * @post The address range [dest_addr, size) in the process' address space
+     *       contains the data that was within src_buffer.
+     *
+     * @post If an attempt is made to write into an unmapped region of memory, the writes
+     *       will be ignored and an error will be logged.
+     *
+     * @post If a write is performed into a region of memory that is considered cached
+     *       rasterizer memory, will cause the currently active rasterizer to be notified
+     *       and will mark that region as invalidated to caches that the active
+     *       graphics backend may be maintaining over the course of execution.
+     */
+    void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer,
+                    std::size_t size);
+
+    /**
+     * Writes a range of bytes into the current process' address space at the specified
+     * virtual address.
+     *
+     * @param dest_addr  The destination virtual address to begin writing the data at.
+     * @param src_buffer The data to write into the current process' address space.
+     * @param size       The size of the data to write, in bytes.
+     *
+     * @post The address range [dest_addr, size) in the current process' address space
+     *       contains the data that was within src_buffer.
+     *
+     * @post If an attempt is made to write into an unmapped region of memory, the writes
+     *       will be ignored and an error will be logged.
+     *
+     * @post If a write is performed into a region of memory that is considered cached
+     *       rasterizer memory, will cause the currently active rasterizer to be notified
+     *       and will mark that region as invalidated to caches that the active
+     *       graphics backend may be maintaining over the course of execution.
+     */
+    void WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size);
+
     /**
      * Fills the specified address range within a process' address space with zeroes.
      *
@@ -320,13 +408,4 @@ void SetCurrentPageTable(Kernel::Process& process);
 /// Determines if the given VAddr is a kernel address
 bool IsKernelVirtualAddress(VAddr vaddr);
 
-void Write8(VAddr addr, u8 data);
-void Write16(VAddr addr, u16 data);
-void Write32(VAddr addr, u32 data);
-void Write64(VAddr addr, u64 data);
-
-void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer,
-                std::size_t size);
-void WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size);
-
 } // namespace Memory
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index d6745af8bb..d1e6bed93f 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -30,7 +30,7 @@ void StandardVmCallbacks::MemoryRead(VAddr address, void* data, u64 size) {
 }
 
 void StandardVmCallbacks::MemoryWrite(VAddr address, const void* data, u64 size) {
-    WriteBlock(SanitizeAddress(address), data, size);
+    system.Memory().WriteBlock(SanitizeAddress(address), data, size);
 }
 
 u64 StandardVmCallbacks::HidKeysDown() {
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp
index ab66f35f99..55e0dbc49b 100644
--- a/src/core/tools/freezer.cpp
+++ b/src/core/tools/freezer.cpp
@@ -34,16 +34,16 @@ u64 MemoryReadWidth(Memory::Memory& memory, u32 width, VAddr addr) {
 void MemoryWriteWidth(Memory::Memory& memory, u32 width, VAddr addr, u64 value) {
     switch (width) {
     case 1:
-        Memory::Write8(addr, static_cast<u8>(value));
+        memory.Write8(addr, static_cast<u8>(value));
         break;
     case 2:
-        Memory::Write16(addr, static_cast<u16>(value));
+        memory.Write16(addr, static_cast<u16>(value));
         break;
     case 4:
-        Memory::Write32(addr, static_cast<u32>(value));
+        memory.Write32(addr, static_cast<u32>(value));
         break;
     case 8:
-        Memory::Write64(addr, value);
+        memory.Write64(addr, value);
         break;
     default:
         UNREACHABLE();

From 50a518be69ef871a674afd91caebdf31cbda4485 Mon Sep 17 00:00:00 2001
From: Lioncash <mathew1800@gmail.com>
Date: Tue, 26 Nov 2019 18:28:44 -0500
Subject: [PATCH 12/13] core/memory: Migrate over GetPointerFromVMA() to the
 Memory class

Now that everything else is migrated over, this is essentially just code
relocation and conversion of a global accessor to the class member
variable.

All that remains is to migrate over the page table.
---
 src/core/memory.cpp | 72 ++++++++++++++++++++++-----------------------
 1 file changed, 36 insertions(+), 36 deletions(-)

diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 5c940a82e8..a49e971aa4 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -22,42 +22,6 @@
 namespace Memory {
 namespace {
 Common::PageTable* current_page_table = nullptr;
-
-/**
- * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned)
- * using a VMA from the current process
- */
-u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) {
-    const auto& vm_manager = process.VMManager();
-
-    const auto it = vm_manager.FindVMA(vaddr);
-    DEBUG_ASSERT(vm_manager.IsValidHandle(it));
-
-    u8* direct_pointer = nullptr;
-    const auto& vma = it->second;
-    switch (vma.type) {
-    case Kernel::VMAType::AllocatedMemoryBlock:
-        direct_pointer = vma.backing_block->data() + vma.offset;
-        break;
-    case Kernel::VMAType::BackingMemory:
-        direct_pointer = vma.backing_memory;
-        break;
-    case Kernel::VMAType::Free:
-        return nullptr;
-    default:
-        UNREACHABLE();
-    }
-
-    return direct_pointer + (vaddr - vma.base);
-}
-
-/**
- * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned)
- * using a VMA from the current process.
- */
-u8* GetPointerFromVMA(VAddr vaddr) {
-    return ::Memory::GetPointerFromVMA(*Core::System::GetInstance().CurrentProcess(), vaddr);
-}
 } // Anonymous namespace
 
 // Implementation class used to keep the specifics of the memory subsystem hidden
@@ -135,6 +99,42 @@ struct Memory::Impl {
         return IsValidVirtualAddress(*system.CurrentProcess(), vaddr);
     }
 
+    /**
+     * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned)
+     * using a VMA from the current process
+     */
+    u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) {
+        const auto& vm_manager = process.VMManager();
+
+        const auto it = vm_manager.FindVMA(vaddr);
+        DEBUG_ASSERT(vm_manager.IsValidHandle(it));
+
+        u8* direct_pointer = nullptr;
+        const auto& vma = it->second;
+        switch (vma.type) {
+        case Kernel::VMAType::AllocatedMemoryBlock:
+            direct_pointer = vma.backing_block->data() + vma.offset;
+            break;
+        case Kernel::VMAType::BackingMemory:
+            direct_pointer = vma.backing_memory;
+            break;
+        case Kernel::VMAType::Free:
+            return nullptr;
+        default:
+            UNREACHABLE();
+        }
+
+        return direct_pointer + (vaddr - vma.base);
+    }
+
+    /**
+     * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned)
+     * using a VMA from the current process.
+     */
+    u8* GetPointerFromVMA(VAddr vaddr) {
+        return GetPointerFromVMA(*system.CurrentProcess(), vaddr);
+    }
+
     u8* GetPointer(const VAddr vaddr) {
         u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
         if (page_pointer != nullptr) {

From e7e939104bb167babec7b5f7d5d8390c85f3cbf4 Mon Sep 17 00:00:00 2001
From: Lioncash <mathew1800@gmail.com>
Date: Tue, 26 Nov 2019 18:34:30 -0500
Subject: [PATCH 13/13] core/memory; Migrate over SetCurrentPageTable() to the
 Memory class

Now that literally every other API function is converted over to the
Memory class, we can just move the file-local page table into the Memory
implementation class, finally getting rid of global state within the
memory code.
---
 src/core/hle/kernel/kernel.cpp | 18 +++++++++++-------
 src/core/memory.cpp            | 31 ++++++++++++++++---------------
 src/core/memory.h              | 11 +++++++----
 3 files changed, 34 insertions(+), 26 deletions(-)

diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index a9851113a9..1c90546a4f 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -154,6 +154,16 @@ struct KernelCore::Impl {
         system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
     }
 
+    void MakeCurrentProcess(Process* process) {
+        current_process = process;
+
+        if (process == nullptr) {
+            return;
+        }
+
+        system.Memory().SetCurrentPageTable(*process);
+    }
+
     std::atomic<u32> next_object_id{0};
     std::atomic<u64> next_kernel_process_id{Process::InitialKIPIDMin};
     std::atomic<u64> next_user_process_id{Process::ProcessIDMin};
@@ -208,13 +218,7 @@ void KernelCore::AppendNewProcess(std::shared_ptr<Process> process) {
 }
 
 void KernelCore::MakeCurrentProcess(Process* process) {
-    impl->current_process = process;
-
-    if (process == nullptr) {
-        return;
-    }
-
-    Memory::SetCurrentPageTable(*process);
+    impl->MakeCurrentProcess(process);
 }
 
 Process* KernelCore::CurrentProcess() {
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index a49e971aa4..91bf07a923 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -20,9 +20,6 @@
 #include "video_core/gpu.h"
 
 namespace Memory {
-namespace {
-Common::PageTable* current_page_table = nullptr;
-} // Anonymous namespace
 
 // Implementation class used to keep the specifics of the memory subsystem hidden
 // from outside classes. This also allows modification to the internals of the memory
@@ -30,6 +27,17 @@ Common::PageTable* current_page_table = nullptr;
 struct Memory::Impl {
     explicit Impl(Core::System& system_) : system{system_} {}
 
+    void SetCurrentPageTable(Kernel::Process& process) {
+        current_page_table = &process.VMManager().page_table;
+
+        const std::size_t address_space_width = process.VMManager().GetAddressSpaceWidth();
+
+        system.ArmInterface(0).PageTableChanged(*current_page_table, address_space_width);
+        system.ArmInterface(1).PageTableChanged(*current_page_table, address_space_width);
+        system.ArmInterface(2).PageTableChanged(*current_page_table, address_space_width);
+        system.ArmInterface(3).PageTableChanged(*current_page_table, address_space_width);
+    }
+
     void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target) {
         ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
         ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
@@ -575,12 +583,17 @@ struct Memory::Impl {
         }
     }
 
+    Common::PageTable* current_page_table = nullptr;
     Core::System& system;
 };
 
 Memory::Memory(Core::System& system) : impl{std::make_unique<Impl>(system)} {}
 Memory::~Memory() = default;
 
+void Memory::SetCurrentPageTable(Kernel::Process& process) {
+    impl->SetCurrentPageTable(process);
+}
+
 void Memory::MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target) {
     impl->MapMemoryRegion(page_table, base, size, target);
 }
@@ -695,18 +708,6 @@ void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
     impl->RasterizerMarkRegionCached(vaddr, size, cached);
 }
 
-void SetCurrentPageTable(Kernel::Process& process) {
-    current_page_table = &process.VMManager().page_table;
-
-    const std::size_t address_space_width = process.VMManager().GetAddressSpaceWidth();
-
-    auto& system = Core::System::GetInstance();
-    system.ArmInterface(0).PageTableChanged(*current_page_table, address_space_width);
-    system.ArmInterface(1).PageTableChanged(*current_page_table, address_space_width);
-    system.ArmInterface(2).PageTableChanged(*current_page_table, address_space_width);
-    system.ArmInterface(3).PageTableChanged(*current_page_table, address_space_width);
-}
-
 bool IsKernelVirtualAddress(const VAddr vaddr) {
     return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END;
 }
diff --git a/src/core/memory.h b/src/core/memory.h
index 7878f3fb18..1428a6d609 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -58,6 +58,13 @@ public:
     Memory(Memory&&) = default;
     Memory& operator=(Memory&&) = default;
 
+    /**
+     * Changes the currently active page table to that of the given process instance.
+     *
+     * @param process The process to use the page table of.
+     */
+    void SetCurrentPageTable(Kernel::Process& process);
+
     /**
      * Maps an allocated buffer onto a region of the emulated process address space.
      *
@@ -401,10 +408,6 @@ private:
     std::unique_ptr<Impl> impl;
 };
 
-/// Changes the currently active page table to that of
-/// the given process instance.
-void SetCurrentPageTable(Kernel::Process& process);
-
 /// Determines if the given VAddr is a kernel address
 bool IsKernelVirtualAddress(VAddr vaddr);