diff --git a/.gitmodules b/.gitmodules
index 25980054..1f1d11fc 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -67,3 +67,6 @@
 [submodule "third_party/Catch2"]
 	path = third_party/Catch2
 	url = https://github.com/catchorg/Catch2.git
+[submodule "third_party/capstone"]
+	path = third_party/capstone
+	url = https://github.com/capstone-engine/capstone
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3dc7e467..6d3901b6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -153,8 +153,15 @@ if(HOST_X64 OR HOST_ARM64)
 else()
     message(FATAL_ERROR "Currently unsupported CPU architecture")
 endif()
+
 add_subdirectory(third_party/teakra EXCLUDE_FROM_ALL)
 
+set(CAPSTONE_ARCHITECTURE_DEFAULT OFF)
+set(CAPSTONE_ARM_SUPPORT ON)
+set(CAPSTONE_BUILD_MACOS_THIN ON)
+add_subdirectory(third_party/capstone)
+include_directories(third_party/capstone/include)
+
 set(SOURCE_FILES src/emulator.cpp src/io_file.cpp src/config.cpp
                  src/core/CPU/cpu_dynarmic.cpp src/core/CPU/dynarmic_cycles.cpp
                  src/core/memory.cpp src/renderer.cpp src/core/renderer_null/renderer_null.cpp
@@ -234,7 +241,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp
                  include/PICA/dynapica/shader_rec_emitter_arm64.hpp include/scheduler.hpp include/applets/error_applet.hpp
                  include/audio/dsp_core.hpp include/audio/null_core.hpp include/audio/teakra_core.hpp
                  include/audio/miniaudio_device.hpp include/ring_buffer.hpp include/bitfield.hpp include/audio/dsp_shared_mem.hpp
-                 include/audio/hle_core.hpp
+                 include/audio/hle_core.hpp include/capstone.hpp
 )
 
 cmrc_add_resource_library(
@@ -399,7 +406,7 @@ set(ALL_SOURCES ${SOURCE_FILES} ${FS_SOURCE_FILES} ${CRYPTO_SOURCE_FILES} ${KERN
 target_sources(AlberCore PRIVATE ${ALL_SOURCES})
 
 target_link_libraries(AlberCore PRIVATE dynarmic cryptopp glad resources_console_fonts teakra)
-target_link_libraries(AlberCore PUBLIC glad)
+target_link_libraries(AlberCore PUBLIC glad capstone)
 
 if(ENABLE_DISCORD_RPC AND NOT ANDROID)
     target_compile_definitions(AlberCore PUBLIC "PANDA3DS_ENABLE_DISCORD_RPC=1")
diff --git a/include/capstone.hpp b/include/capstone.hpp
new file mode 100644
index 00000000..e4d6b7d7
--- /dev/null
+++ b/include/capstone.hpp
@@ -0,0 +1,53 @@
+#pragma once
+#include <capstone/capstone.h>
+
+#include <cstdio>
+#include <span>
+#include <string>
+
+#include "helpers.hpp"
+
+namespace Common {
+	class CapstoneDisassembler {
+		csh handle;                       // Handle to our disassembler object
+		cs_insn* instructions = nullptr;  // Pointer to instruction object
+		bool initialized = false;
+
+	  public:
+		void init(cs_arch arch, cs_mode mode) { initialized = (cs_open(arch, mode, &handle) == CS_ERR_OK); }
+
+		CapstoneDisassembler() {}
+		CapstoneDisassembler(cs_arch arch, cs_mode mode) { init(arch, mode); }
+
+		// Returns the number of instructions successfully disassembled
+		// pc: program counter of the instruction to disassemble
+		// bytes: Byte representation of instruction
+		// buffer: text buffer to output the disassembly too
+		usize disassemble(std::string& buffer, u32 pc, std::span<u8> bytes, u64 offset = 0) {
+			if (!initialized) {
+				buffer = "Capstone was not properly initialized";
+				return 0;
+			}
+
+			usize count = cs_disasm(handle, bytes.data(), bytes.size(), pc, offset, &instructions);
+			if (count == 0) {
+				// Error in disassembly, quit early and return empty string
+				buffer = "Error disassembling instructions with Capstone";
+				return 0;
+			}
+
+			buffer = "";
+			for (usize i = 0; i < count; i++) {
+				buffer += std::string(instructions[i].mnemonic) + " " + std::string(instructions[i].op_str);
+
+				if (i < count - 1) {
+					// Append newlines between instructions, sans the final instruction
+					buffer += "\n";
+				}
+			}
+
+			cs_free(instructions, count);
+			return count;
+		}
+	};
+}  // namespace Common
\ No newline at end of file
diff --git a/src/lua.cpp b/src/lua.cpp
index d12faf7e..209ea32e 100644
--- a/src/lua.cpp
+++ b/src/lua.cpp
@@ -1,10 +1,13 @@
 #ifdef PANDA3DS_ENABLE_LUA
+#include <array>
+
+#include "capstone.hpp"
 #include "emulator.hpp"
 #include "lua_manager.hpp"
 
 #ifndef __ANDROID__
 extern "C" {
-	#include "luv.h"
+#include "luv.h"
 }
 #endif
 
@@ -203,6 +206,27 @@ static int getButtonThunk(lua_State* L) {
 	return 1;
 }
 
+static int disassembleARMThunk(lua_State* L) {
+	static Common::CapstoneDisassembler disassembler(CS_ARCH_ARM, CS_MODE_ARM);
+
+	const u32 pc = u32(lua_tonumber(L, 1));
+	const u32 instruction = u32(lua_tonumber(L, 2));
+
+	std::string disassembly;
+	// Convert instruction to byte array to pass to Capstone
+	std::array<u8, 4> bytes = {
+		instruction & 0xff,
+		(instruction >> 8) & 0xff,
+		(instruction >> 16) & 0xff,
+		(instruction >> 24) & 0xff,
+	};
+
+	disassembler.disassemble(disassembly, pc, std::span(bytes));
+	lua_pushstring(L, disassembly.c_str());
+
+	return 1;
+}
+
 // clang-format off
 static constexpr luaL_Reg functions[] = {
 	{ "__read8", read8Thunk },
@@ -214,13 +238,14 @@ static constexpr luaL_Reg functions[] = {
 	{ "__write32", write32Thunk },
 	{ "__write64", write64Thunk },
 	{ "__getAppID", getAppIDThunk },
-	{ "__pause", pauseThunk}, 
-	{ "__resume", resumeThunk},
-	{ "__reset", resetThunk},
-	{ "__loadROM", loadROMThunk},
-	{ "__getButtons", getButtonsThunk},
-	{ "__getCirclepad", getCirclepadThunk},
-	{ "__getButton", getButtonThunk},
+	{ "__pause", pauseThunk }, 
+	{ "__resume", resumeThunk },
+	{ "__reset", resetThunk },
+	{ "__loadROM", loadROMThunk },
+	{ "__getButtons", getButtonsThunk },
+	{ "__getCirclepad", getCirclepadThunk },
+	{ "__getButton", getButtonThunk },
+	{ "__disassembleARM", disassembleARMThunk },
 	{ nullptr, nullptr },
 };
 // clang-format on
@@ -254,6 +279,8 @@ void LuaManager::initializeThunks() {
 		getButton = function(button) return GLOBALS.__getButton(button) end,
 		getCirclepad = function() return GLOBALS.__getCirclepad() end,
 
+		disassembleARM = function(pc, instruction) return GLOBALS.__disassembleARM(pc, instruction) end,
+
 		Frame = __Frame,
 		ButtonA = __ButtonA,
 		ButtonB = __ButtonB,
diff --git a/third_party/capstone b/third_party/capstone
new file mode 160000
index 00000000..eb4fc2d7
--- /dev/null
+++ b/third_party/capstone
@@ -0,0 +1 @@
+Subproject commit eb4fc2d7612db10379adf7aeb287a7923dcc0fc7