From e4c381b8850db96f162cfcf2cbe28b0e7c1f76f1 Mon Sep 17 00:00:00 2001
From: Lioncash <mathew1800@gmail.com>
Date: Tue, 26 Nov 2019 17:39:57 -0500
Subject: 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(-)

(limited to 'src')

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();
-- 
cgit v1.2.3-70-g09d2