diff --git a/include/cpu_dynarmic.hpp b/include/cpu_dynarmic.hpp
index f5750cb5..6cd53101 100644
--- a/include/cpu_dynarmic.hpp
+++ b/include/cpu_dynarmic.hpp
@@ -8,21 +8,18 @@
 class MyEnvironment final : public Dynarmic::A32::UserCallbacks {
 public:
     u64 ticks_left = 0;
-    std::array<u8, 2048> memory{};
+    Memory& mem;
 
     u8 MemoryRead8(u32 vaddr) override {
-        if (vaddr >= memory.size()) {
-            return 0;
-        }
-        return memory[vaddr];
+        return mem.read8(vaddr);
     }
 
     u16 MemoryRead16(u32 vaddr) override {
-        return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8;
+        return mem.read16(vaddr);
     }
 
     u32 MemoryRead32(u32 vaddr) override {
-        return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16;
+        return mem.read32(vaddr);
     }
 
     u64 MemoryRead64(u32 vaddr) override {
@@ -30,20 +27,15 @@ public:
     }
 
     void MemoryWrite8(u32 vaddr, u8 value) override {
-        if (vaddr >= memory.size()) {
-            return;
-        }
-        memory[vaddr] = value;
+        mem.write8(vaddr, value);
     }
 
     void MemoryWrite16(u32 vaddr, u16 value) override {
-        MemoryWrite8(vaddr, u8(value));
-        MemoryWrite8(vaddr + 1, u8(value >> 8));
+        mem.write16(vaddr, value);
     }
 
     void MemoryWrite32(u32 vaddr, u32 value) override {
-        MemoryWrite16(vaddr, u16(value));
-        MemoryWrite16(vaddr + 2, u16(value >> 16));
+        mem.write32(vaddr, value);
     }
 
     void MemoryWrite64(u32 vaddr, u64 value) override {
@@ -75,6 +67,8 @@ public:
     u64 GetTicksRemaining() override {
         return ticks_left;
     }
+
+    MyEnvironment(Memory& mem) : mem(mem) {}
 };
 
 class CPU {
@@ -105,4 +99,10 @@ public:
     u32 getCPSR() {
         return jit.Cpsr();
     }
+
+    void runFrame() {
+        env.ticks_left = 268111856 / 60;
+        const auto exitReason = jit.Run();
+        Helpers::panic("Exit reason: %d", (u32)exitReason);
+    }
 };
\ No newline at end of file
diff --git a/include/memory.hpp b/include/memory.hpp
index 8dbdf498..8c11e2e8 100644
--- a/include/memory.hpp
+++ b/include/memory.hpp
@@ -4,6 +4,18 @@
 #include <vector>
 #include "helpers.hpp"
 
+namespace VirtualAddrs {
+	enum : u32 {
+		ExecutableStart = 0x00100000,
+		MaxExeSize = 0x03F00000,
+		ExecutableEnd = 0x00100000 + 0x03F00000,
+
+		// Stack for main ARM11 thread.
+		// Typically 0x4000 bytes
+		StackTop = 0x10000000
+	};
+}
+
 class Memory {
 	u8* fcram;
 
@@ -12,11 +24,19 @@ class Memory {
 	static constexpr u32 pageShift = 12;
 	static constexpr u32 pageSize = 1 << pageShift;
 	static constexpr u32 pageMask = pageSize - 1;
+	static constexpr u32 totalPageCount = 1 << (32 - pageShift);
 
 public:
 	Memory();
 	void* getReadPointer(u32 address);
 	void* getWritePointer(u32 address);
-	
 	std::optional<u32> loadELF(std::filesystem::path& path);
+
+	u8 read8(u32 vaddr);
+	u16 read16(u32 vaddr);
+	u32 read32(u32 vaddr);
+
+	void write8(u32 vaddr, u8 value);
+	void write16(u32 vaddr, u16 value);
+	void write32(u32 vaddr, u32 value);
 };
\ No newline at end of file
diff --git a/src/core/CPU/cpu_dynarmic.cpp b/src/core/CPU/cpu_dynarmic.cpp
index b4d3e8e4..499a4219 100644
--- a/src/core/CPU/cpu_dynarmic.cpp
+++ b/src/core/CPU/cpu_dynarmic.cpp
@@ -1,35 +1,15 @@
 #ifdef CPU_DYNARMIC
 #include "cpu_dynarmic.hpp"
 
-CPU::CPU(Memory& mem) : mem(mem) {
-    // Execute at least 1 instruction.
-    // (Note: More than one instruction may be executed.)
-    env.ticks_left = 10;
-
-    // Write some code to memory.
-    env.MemoryWrite16(0, 0x0088); // lsls r0, r1, #2
-    env.MemoryWrite16(2, 0x3045); // adds r0, #69
-    env.MemoryWrite16(4, 0x3845); // subs r0, #69
-    env.MemoryWrite16(6, 0xE7FE); // b +#0 (infinite loop)
-
-    // Setup registers.
-    auto& regs = jit.Regs();
-    regs[0] = 1;
-    regs[1] = 2;
-    regs[15] = 0; // PC = 0
-    setCPSR(0x00000030); // Thumb mode
-
-    // Execute!
-    jit.Run();
-
-    // Here we would expect cpu.Regs()[0] == 77
-    printf("R0: %u\n", jit.Regs()[0]);
-}
+CPU::CPU(Memory& mem) : mem(mem), env(mem) {}
 
 void CPU::reset() {
-    jit.Regs().fill(0);
     // ARM mode, all flags disabled, interrupts and aborts all enabled, user mode
     setCPSR(0x00000010);
+
+    jit.Reset();
+    jit.ClearCache();
+    jit.Regs().fill(0);
 }
 
 #endif // CPU_DYNARMIC
\ No newline at end of file
diff --git a/src/core/elf.cpp b/src/core/elf.cpp
index 4e65e54f..d456ebf5 100644
--- a/src/core/elf.cpp
+++ b/src/core/elf.cpp
@@ -1,4 +1,6 @@
 #include "memory.hpp"
+
+#include <cstring>
 #include "elfio/elfio.hpp"
 
 using namespace ELFIO;
@@ -10,14 +12,35 @@ std::optional<u32> Memory::loadELF(std::filesystem::path& path) {
 		return std::nullopt;
 	}
 
-    auto seg_num = reader.segments.size();
-    printf("Number of segments: %d\n", seg_num);
-    for (int i = 0; i < seg_num; ++i) {
-        const auto pseg = reader.segments[i];
-        std::cout << "  [" << i << "] 0x" << std::hex << pseg->get_flags()
-            << "\t0x" << pseg->get_virtual_address() << "\t0x"
-            << pseg->get_file_size() << "\t0x" << pseg->get_memory_size()
-            << std::endl;
+    auto segNum = reader.segments.size();
+    printf("Number of segments: %d\n", segNum);
+    for (int i = 0; i < segNum; ++i) {
+        const auto seg = reader.segments[i];
+        const auto flags = seg->get_flags();
+        const u32 vaddr = static_cast<u32>(seg->get_virtual_address()); // Vaddr the segment is loaded in
+        u32 fileSize = static_cast<u32>(seg->get_file_size()); // Size of segment in file
+        u32 memorySize = static_cast<u32>(seg->get_memory_size()); // Size of segment in memory
+        u8* data = (u8*)seg->get_data();
+
+        // Get read/write/execute permissions for segment
+        const bool r = (flags & 0b100) != 0;
+        const bool w = (flags & 0b010) != 0;
+        const bool x = (flags & 0b001) != 0;
+
+        printf(" #  Perms       Vaddr           File Size       Mem Size\n");
+        printf("[%d] (%c%c%c)\t%08X\t%08X\t%08X\n", i, r ? 'r' : '-', w ? 'w' : '-', x ? 'x' : '-', vaddr, fileSize, memorySize);
+    
+        // Assert that the segment will be loaded in the executable region. If it isn't then panic.
+        // The executable region starts at 0x00100000 and has a maximum size of 0x03F00000
+        u64 endAddress = (u64)vaddr + (u64)fileSize;
+        const bool isGood = vaddr >= VirtualAddrs::ExecutableStart && endAddress < VirtualAddrs::ExecutableEnd;
+        if (!isGood) {
+            Helpers::panic("ELF is loaded at invalid place");
+            return std::nullopt;
+        }
+
+        u32 fcramAddr = vaddr - VirtualAddrs::ExecutableStart;
+        std::memcpy(&fcram[fcramAddr], data, fileSize);
     }
 
     return static_cast<u32>(reader.get_entry());
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index c038d55c..b7ab4793 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -2,11 +2,66 @@
 
 Memory::Memory() {
 	fcram = new uint8_t[128_MB]();
+	readTable.resize(totalPageCount, 0);
+	writeTable.resize(totalPageCount, 0);
+
+	// Map 63MB of FCRAM in the executable section as read/write
+	// TODO: This should probably be read-only, but making it r/w shouldn't hurt?
+	// Because if that were the case then writes would cause data aborts, so games wouldn't write to read-only memory
+	u32 executablePageCount = VirtualAddrs::MaxExeSize / pageSize;
+	u32 page = VirtualAddrs::ExecutableStart / pageSize;
+	for (u32 i = 0; i < executablePageCount; i++) {
+		const auto pointer = (uintptr_t)&fcram[i * pageSize];
+		readTable[page] = pointer;
+		writeTable[page++] = pointer;
+	}
+}
+
+u8 Memory::read8(u32 vaddr) {
+	Helpers::panic("Unimplemented 8-bit read, addr: %08X", vaddr);
+}
+
+u16 Memory::read16(u32 vaddr) {
+	Helpers::panic("Unimplemented 16-bit read, addr: %08X", vaddr);
+}
+
+u32 Memory::read32(u32 vaddr) {
+	const u32 page = vaddr >> pageShift;
+	const u32 offset = vaddr & pageMask;
+
+	uintptr_t pointer = readTable[page];
+	if (pointer != 0) [[likely]] {
+		printf("Read %08X from %08X\n", *(u32*)(pointer + offset), vaddr);
+		return *(u32*)(pointer + offset);
+	} else {
+		Helpers::panic("Unimplemented 32-bit read, addr: %08X", vaddr);
+	}
+}
+
+void Memory::write8(u32 vaddr, u8 value) {
+	Helpers::panic("Unimplemented 8-bit write, addr: %08X, val: %02X", vaddr, value);
+}
+
+void Memory::write16(u32 vaddr, u16 value) {
+	Helpers::panic("Unimplemented 16-bit write, addr: %08X, val: %04X", vaddr, value);
+}
+
+void Memory::write32(u32 vaddr, u32 value) {
+	const u32 page = vaddr >> pageShift;
+	const u32 offset = vaddr & pageMask;
+
+	uintptr_t pointer = writeTable[page];
+	if (pointer != 0) [[likely]] {
+		printf("Wrote %08X to %08X\n", value, vaddr);
+		*(u32*)(pointer + offset) = value;
+	} else {
+		Helpers::panic("Unimplemented 32-bit write, addr: %08X, val: %08X", vaddr, value);
+	}
 }
 
 void* Memory::getReadPointer(u32 address) {
-	const auto page = address >> pageShift;
-	const auto offset = address & pageMask;
+	const u32 page = address >> pageShift;
+	const u32 offset = address & pageMask;
 
 	uintptr_t pointer = readTable[page];
 	if (pointer == 0) return nullptr;
@@ -14,8 +69,8 @@ void* Memory::getReadPointer(u32 address) {
 }
 
 void* Memory::getWritePointer(u32 address) {
-	const auto page = address >> pageShift;
-	const auto offset = address & pageMask;
+	const u32 page = address >> pageShift;
+	const u32 offset = address & pageMask;
 
 	uintptr_t pointer = writeTable[page];
 	if (pointer == 0) return nullptr;
diff --git a/src/emulator.cpp b/src/emulator.cpp
index f5e4b4ad..fcb0c841 100644
--- a/src/emulator.cpp
+++ b/src/emulator.cpp
@@ -30,10 +30,7 @@ void Emulator::run() {
 }
 
 void Emulator::runFrame() {
-    constexpr u32 freq = 268 * 1024 * 1024;
-    for (u32 i = 0; i < freq; i += 2) {
-        step();
-    }
+    cpu.runFrame();
 }
 
 bool Emulator::loadELF(std::filesystem::path& path) {
@@ -41,5 +38,6 @@ bool Emulator::loadELF(std::filesystem::path& path) {
     if (!entrypoint.has_value())
         return false;
 
-    Helpers::panic("Entrypoint: %08X\n", entrypoint.value());
+    cpu.setReg(15, entrypoint.value()); // Set initial PC
+    return true;
 }
\ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index b7c91866..7d52a53b 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -8,7 +8,7 @@ int main (int argc, char *argv[]) {
     }
 
     auto elfPath = std::filesystem::current_path() / (argc > 1 ? argv[1] : "simple_tri.elf");
-    if (emu.loadELF(elfPath)) {
+    if (!emu.loadELF(elfPath)) {
         Helpers::panic("Failed to load ELF file: %s", elfPath.c_str());
     }
     emu.run();