diff --git a/src/backend/A64/a32_interface.cpp b/src/backend/A64/a32_interface.cpp
index 2fe93de1..531bd7b8 100644
--- a/src/backend/A64/a32_interface.cpp
+++ b/src/backend/A64/a32_interface.cpp
@@ -42,7 +42,7 @@ static RunCodeCallbacks GenRunCodeCallbacks(const A32::UserConfig& config, CodeP
 
 struct Jit::Impl {
     Impl(Jit* jit, A32::UserConfig config)
-            : block_of_code(GenRunCodeCallbacks(config, &GetCurrentBlock, this), JitStateInfo{jit_state})
+            : block_of_code(GenRunCodeCallbacks(config, &GetCurrentBlockThunk, this), JitStateInfo{jit_state})
             , emitter(block_of_code, config, jit)
             , config(std::move(config))
             , jit_interface(jit)
@@ -74,6 +74,10 @@ struct Jit::Impl {
         block_of_code.RunCode(&jit_state, current_codeptr);
     }
 
+    void Step() {
+        block_of_code.StepCode(&jit_state, GetCurrentSingleStep());
+    }
+
     std::string Disassemble(const IR::LocationDescriptor& descriptor) {
         auto block = GetBasicBlock(descriptor);
         std::string result = fmt::format("address: {}\nsize: {} bytes\n", block.entrypoint, block.size);
@@ -122,16 +126,21 @@ struct Jit::Impl {
 private:
     Jit* jit_interface;
 
-    static CodePtr GetCurrentBlock(void* this_voidptr) {
+    static CodePtr GetCurrentBlockThunk(void* this_voidptr) {
         Jit::Impl& this_ = *static_cast<Jit::Impl*>(this_voidptr);
-        A32JitState& jit_state = this_.jit_state;
+        return this_.GetCurrentBlock();
+    }
 
-        u32 pc = jit_state.Reg[15];
-        A32::PSR cpsr{jit_state.Cpsr()};
-        A32::FPSCR fpscr{jit_state.upper_location_descriptor};
-        A32::LocationDescriptor descriptor{pc, cpsr, fpscr};
+    IR::LocationDescriptor GetCurrentLocation() const {
+        return IR::LocationDescriptor{jit_state.GetUniqueHash()};
+    }
 
-        return this_.GetBasicBlock(descriptor).entrypoint;
+    CodePtr GetCurrentBlock() {
+        return GetBasicBlock(GetCurrentLocation()).entrypoint;
+    }
+
+    CodePtr GetCurrentSingleStep() {
+        return GetBasicBlock(A32::LocationDescriptor{GetCurrentLocation()}.SetSingleStepping(true)).entrypoint;
     }
 
     A32EmitA64::BlockDescriptor GetBasicBlock(IR::LocationDescriptor descriptor) {
@@ -173,6 +182,18 @@ void Jit::Run() {
     impl->PerformCacheInvalidation();
 }
 
+void Jit::Step() {
+    ASSERT(!is_executing);
+    is_executing = true;
+    SCOPE_EXIT { this->is_executing = false; };
+
+    impl->jit_state.halt_requested = true;
+
+    impl->Step();
+
+    impl->PerformCacheInvalidation();
+}
+
 void Jit::ClearCache() {
     impl->invalidate_entire_cache = true;
     impl->RequestCacheInvalidation();
diff --git a/src/backend/A64/block_of_code.cpp b/src/backend/A64/block_of_code.cpp
index 15f54923..e0bb92e0 100644
--- a/src/backend/A64/block_of_code.cpp
+++ b/src/backend/A64/block_of_code.cpp
@@ -127,6 +127,8 @@ void BlockOfCode::RunCode(void* jit_state, CodePtr code_ptr) const {
     run_code(jit_state, code_ptr);
 }
 
+void BlockOfCode::StepCode(void* jit_state, CodePtr code_ptr) const {
+    step_code(jit_state, code_ptr);
 }
 
 void BlockOfCode::ReturnFromRunCode(bool fpscr_already_exited) {
@@ -166,6 +168,19 @@ void BlockOfCode::GenRunCode() {
     SwitchFpscrOnEntry();
     BR(Arm64Gen::X25);
 
+    AlignCode16();
+    step_code = (RunCodeFuncType) GetWritableCodePtr();
+
+    ABI_PushCalleeSaveRegistersAndAdjustStack(*this);
+
+    MOV(Arm64Gen::X28, ABI_PARAM1);
+    
+    MOVI2R(Arm64Gen::X26, 1);
+    STR(Arm64Gen::INDEX_UNSIGNED, Arm64Gen::X26, Arm64Gen::X28, jsi.offsetof_cycles_to_run);    
+
+    SwitchFpscrOnEntry();
+    BR(ABI_PARAM2);
+
     enter_fpscr_then_loop = GetCodePtr();
     SwitchFpscrOnEntry();
     loop = GetCodePtr();
diff --git a/src/backend/A64/block_of_code.h b/src/backend/A64/block_of_code.h
index 171973f8..44f5c9a0 100644
--- a/src/backend/A64/block_of_code.h
+++ b/src/backend/A64/block_of_code.h
@@ -48,6 +48,8 @@ public:
 
     /// Runs emulated code from code_ptr.
     void RunCode(void* jit_state, CodePtr code_ptr) const;
+    /// Runs emulated code from code_ptr for a single cycle.
+    void StepCode(void* jit_state, CodePtr code_ptr) const;
     /// Code emitter: Returns to dispatcher
     void ReturnFromRunCode(bool fpscr_already_exited = false);
     /// Code emitter: Returns to dispatcher, forces return to host
@@ -133,6 +135,7 @@ private:
 
     using RunCodeFuncType = void(*)(void*, CodePtr);
     RunCodeFuncType run_code = nullptr;
+    RunCodeFuncType step_code = nullptr;
     static constexpr size_t FPSCR_ALREADY_EXITED = 1 << 0;
     static constexpr size_t FORCE_RETURN = 1 << 1;
     std::array<const void*, 4> return_from_run_code;
@@ -141,4 +144,4 @@ private:
     //Xbyak::util::Cpu cpu_info;
 };
 
-} // namespace Dynarmic::BackendX64
+} // namespace Dynarmic::BackendA64