diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,16 @@
+# Version [0.1.2.0](https://github.com/DistRap/hocd/compare/0.1.1.1...0.1.2.0) (2024-01-03)
+
+* Add execution control commands
+  * `reset`
+  * `resetHalt`
+  * `resetHaltInit`
+  * `resume`
+  * `resumeAt`
+  * `step`
+  * `stepTo`
+
+* Add `version` query
+
 # Version [0.1.1.1](https://github.com/DistRap/hocd/compare/0.1.1.0...0.1.1.1) (2023-12-27)
 
 * Export `runOCDConfig`
diff --git a/README.lhs b/README.lhs
--- a/README.lhs
+++ b/README.lhs
@@ -41,6 +41,8 @@
   writeMem gpioaOdr [odr+1]
   r <- readMem32 gpioaOdr
 
+  resume
+
   pure (rccCr, r)
 ```
 
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -41,6 +41,8 @@
   writeMem gpioaOdr [odr+1]
   r <- readMem32 gpioaOdr
 
+  resume
+
   pure (rccCr, r)
 ```
 
diff --git a/hocd.cabal b/hocd.cabal
--- a/hocd.cabal
+++ b/hocd.cabal
@@ -1,6 +1,6 @@
 cabal-version:       2.2
 name:                hocd
-version:             0.1.1.1
+version:             0.1.2.0
 synopsis:            OpenOCD Haskell interface
 description:         Support for OpenOCDs TCL interface
 homepage:            https://github.com/DistRap/hocd
diff --git a/src/HOCD/Command.hs b/src/HOCD/Command.hs
--- a/src/HOCD/Command.hs
+++ b/src/HOCD/Command.hs
@@ -8,9 +8,14 @@
 module HOCD.Command
   ( Command(..)
   , Halt(..)
+  , Reset(..)
+  , ResetMode(..)
+  , Resume(..)
+  , Step(..)
   , Capture(..)
   , ReadMemory(..)
   , WriteMemory(..)
+  , Version(..)
   , subChar
   , parseMem
   ) where
@@ -56,15 +61,67 @@
   show = pure "halt"
 
 instance Command Halt where
-  type Reply Halt = ByteString
-  reply _ = ocdReply
+  type Reply Halt = ()
+  reply _ = voidOcdReply
 
+data ResetMode
+  = ResetMode_Run -- ^ Let the target run after reset
+  | ResetMode_Halt -- ^ Halt target after reset
+  | ResetMode_Init -- ^ Halt target after reset and execute reset-init script
+  deriving (Eq, Ord)
+
+instance Show ResetMode where
+  show ResetMode_Run = "run"
+  show ResetMode_Halt = "halt"
+  show ResetMode_Init = "init"
+
+data Reset = Reset ResetMode
+
+instance Show Reset where
+  show (Reset mode) =
+    unwords
+      [ "reset"
+      , show mode
+      ]
+
+instance Command Reset where
+  type Reply Reset = ()
+  reply _ = voidOcdReply
+
+data Resume = Resume (Maybe MemAddress)
+
+instance Show Resume where
+  show (Resume Nothing) = "resume"
+  show (Resume (Just resumeWhere)) =
+    unwords
+      [ "resume"
+      , show $ unMemAddress resumeWhere
+      ]
+
+instance Command Resume where
+  type Reply Resume = ()
+  reply _ = voidOcdReply
+
+data Step = Step (Maybe MemAddress)
+
+instance Show Step where
+  show (Step Nothing) = "step"
+  show (Step (Just stepTo)) =
+    unwords
+      [ "step"
+      , show $ unMemAddress stepTo
+      ]
+
+instance Command Step where
+  type Reply Step = ()
+  reply _ = voidOcdReply
+
 data Capture a = Capture a
 
 instance Show a => Show (Capture a) where
   show (Capture x) =
     unwords
-      ["capture"
+      [ "capture"
       , show $ show x -- escaping
       ]
 
@@ -150,11 +207,25 @@
   type Reply (WriteMemory a) = ()
   reply _ = ocdReply >>= pure . Control.Monad.void
 
+data Version = Version
+
+instance Show Version where
+  show = pure "version"
+
+instance Command Version where
+  type Reply Version = ByteString
+  reply _ = ocdReply
+
 ocdReply :: ByteString -> Either OCDError ByteString
 ocdReply r | Data.ByteString.Char8.last r /= subChar =
   Left $ OCDError_ReplyMissingSubOnEnd r
 ocdReply r | otherwise =
   Right $ Data.ByteString.Char8.init r
+
+voidOcdReply :: ByteString -> Either OCDError ()
+voidOcdReply =
+  ocdReply
+  >>= pure . Control.Monad.void
 
 subChar :: Char
 subChar = '\SUB'
diff --git a/src/HOCD/Monad.hs b/src/HOCD/Monad.hs
--- a/src/HOCD/Monad.hs
+++ b/src/HOCD/Monad.hs
@@ -13,11 +13,19 @@
   , MonadOCD(..)
   , halt
   , halt'
+  , reset
+  , resetHalt
+  , resetHaltInit
+  , resume
+  , resumeAt
+  , step
+  , stepTo
   , readMem
   , readMem32
   , readMemCount
   , writeMem
   , writeMem32
+  , version
   ) where
 
 import Control.Monad.Catch (MonadCatch, MonadMask, MonadThrow)
@@ -35,8 +43,13 @@
   ( Command(..)
   , Capture(..)
   , Halt(..)
+  , Resume(..)
+  , Reset(..)
+  , ResetMode(..)
+  , Step(..)
   , ReadMemory(..)
   , WriteMemory(..)
+  , Version(..)
   , subChar
   )
 import HOCD.Error (OCDError(..))
@@ -141,9 +154,55 @@
   => m ()
 halt' = halt >> pure ()
 
+-- | Reset target (default "reset run")
+reset
+  :: MonadOCD m
+  => m ()
+reset = rpc $ Reset ResetMode_Run
+
+-- | Reset target and halt execution
+resetHalt
+  :: MonadOCD m
+  => m ()
+resetHalt = rpc $ Reset ResetMode_Halt
+
+-- | Reset target, halt execution
+-- and execute reset-init script
+resetHaltInit
+  :: MonadOCD m
+  => m ()
+resetHaltInit = rpc $ Reset ResetMode_Init
+
+-- | Resume execution
+resume
+  :: MonadOCD m
+  => m ()
+resume = rpc $ Resume Nothing
+
+-- | Resume execution at @MemAddress@
+resumeAt
+  :: MonadOCD m
+  => MemAddress
+  -> m ()
+resumeAt = rpc . Resume . Just
+
+-- | Single-step target at its current code position
+step
+  :: MonadOCD m
+  => m ()
+step = rpc $ Step Nothing
+
+-- | Single-step target to code position
+-- at @MemAddress@
+stepTo
+  :: MonadOCD m
+  => MemAddress
+  -> m ()
+stepTo = rpc . Step . Just
+
 -- | Read multiple memory segments from @MemAddress@
 -- according to count argument. Segment size depends
--- on passed in Word type.
+-- on Word type.
 readMemCount
   :: forall a m
    . ( MonadOCD m
@@ -161,7 +220,7 @@
       }
 
 -- | Read single memory segment from @MemAddress@
--- Segment size depends on passed in Word type.
+-- Segment size depends on Word type.
 readMem
   :: forall a m
    . ( MonadOCD m
@@ -208,3 +267,9 @@
   -> [Word32] -- ^ Data to write
   -> m ()
 writeMem32 = writeMem @Word32
+
+-- | Query OpenOCD version
+version
+  :: MonadOCD m
+  => m ByteString
+version = rpc Version
