diff --git a/ARMeilleure/Decoders/Decoder.cs b/ARMeilleure/Decoders/Decoder.cs
index 46774f416..e4839bf7b 100644
--- a/ARMeilleure/Decoders/Decoder.cs
+++ b/ARMeilleure/Decoders/Decoder.cs
@@ -18,7 +18,7 @@ namespace ARMeilleure.Decoders
         // For lower code quality translation, we set a lower limit since we're blocking execution.
         private const int MaxInstsPerFunctionLowCq = 500;
 
-        public static Block[] Decode(IMemoryManager memory, ulong address, ExecutionMode mode, bool highCq, bool singleBlock)
+        public static Block[] Decode(IMemoryManager memory, ulong address, ExecutionMode mode, bool highCq, DecoderMode dMode)
         {
             List<Block> blocks = new List<Block>();
 
@@ -38,7 +38,7 @@ namespace ARMeilleure.Decoders
                 {
                     block = new Block(blkAddress);
 
-                    if ((singleBlock && visited.Count >= 1) || opsCount > instructionLimit || !memory.IsMapped(blkAddress))
+                    if ((dMode != DecoderMode.MultipleBlocks && visited.Count >= 1) || opsCount > instructionLimit || !memory.IsMapped(blkAddress))
                     {
                         block.Exit = true;
                         block.EndAddress = blkAddress;
@@ -96,6 +96,12 @@ namespace ARMeilleure.Decoders
                         }
                     }
 
+                    if (dMode == DecoderMode.SingleInstruction)
+                    {
+                        // Only read at most one instruction
+                        limitAddress = currBlock.Address + 1;
+                    }
+
                     FillBlock(memory, mode, currBlock, limitAddress);
 
                     opsCount += currBlock.OpCodes.Count;
@@ -143,7 +149,7 @@ namespace ARMeilleure.Decoders
                 throw new InvalidOperationException($"Decoded a single empty exit block. Entry point = 0x{address:X}.");
             }
 
-            if (!singleBlock)
+            if (dMode == DecoderMode.MultipleBlocks)
             {
                 return TailCallRemover.RunPass(address, blocks);
             }
diff --git a/ARMeilleure/Decoders/DecoderMode.cs b/ARMeilleure/Decoders/DecoderMode.cs
new file mode 100644
index 000000000..553620847
--- /dev/null
+++ b/ARMeilleure/Decoders/DecoderMode.cs
@@ -0,0 +1,9 @@
+namespace ARMeilleure.Decoders
+{
+    enum DecoderMode
+    {
+        MultipleBlocks,
+        SingleBlock,
+        SingleInstruction,
+    }
+}
\ No newline at end of file
diff --git a/ARMeilleure/Translation/Translator.cs b/ARMeilleure/Translation/Translator.cs
index 611716e22..389adf292 100644
--- a/ARMeilleure/Translation/Translator.cs
+++ b/ARMeilleure/Translation/Translator.cs
@@ -209,6 +209,17 @@ namespace ARMeilleure.Translation
             return nextAddr;
         }
 
+        public ulong Step(State.ExecutionContext context, ulong address)
+        {
+            TranslatedFunction func = Translate(address, context.ExecutionMode, highCq: false, singleStep: true);
+
+            address = func.Execute(context);
+
+            EnqueueForDeletion(address, func);
+
+            return address;
+        }
+
         internal TranslatedFunction GetOrTranslate(ulong address, ExecutionMode mode)
         {
             if (!Functions.TryGetValue(address, out TranslatedFunction func))
@@ -242,7 +253,7 @@ namespace ARMeilleure.Translation
             }
         }
 
-        internal TranslatedFunction Translate(ulong address, ExecutionMode mode, bool highCq)
+        internal TranslatedFunction Translate(ulong address, ExecutionMode mode, bool highCq, bool singleStep = false)
         {
             var context = new ArmEmitterContext(
                 Memory,
@@ -255,7 +266,7 @@ namespace ARMeilleure.Translation
 
             Logger.StartPass(PassName.Decoding);
 
-            Block[] blocks = Decoder.Decode(Memory, address, mode, highCq, singleBlock: false);
+            Block[] blocks = Decoder.Decode(Memory, address, mode, highCq, singleStep ? DecoderMode.SingleInstruction : DecoderMode.MultipleBlocks);
 
             Logger.EndPass(PassName.Decoding);
 
@@ -285,14 +296,14 @@ namespace ARMeilleure.Translation
 
             var options = highCq ? CompilerOptions.HighCq : CompilerOptions.None;
 
-            if (context.HasPtc)
+            if (context.HasPtc && !singleStep)
             {
                 options |= CompilerOptions.Relocatable;
             }
 
             CompiledFunction compiledFunc = Compiler.Compile(cfg, argTypes, retType, options);
 
-            if (context.HasPtc)
+            if (context.HasPtc && !singleStep)
             {
                 Hash128 hash = Ptc.ComputeHash(Memory, address, funcSize);