packages feed

emhell-0.1.1.0: hocd/Main.hs

{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}

module Main where

import Control.Monad.IO.Class (liftIO)
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.Reader (ReaderT, runReaderT, ask)
import Data.Default.Class (Default(def))
import Data.SVD.Types (Device)
import EmHell.SVD.Selector (Selector(..))
import HOCD (OCDT, OCDConfig(..))
import System.Console.Repline
  ( HaskelineT
  , MultiLine(..)
  , CompleterStyle(Prefix)
  , CompletionFunc
  , ExitDecision(Exit)
  )
import Prettyprinter ((<+>), annotate, pretty)
import Prettyprinter.Render.Terminal (Color(..), bold, color)

import qualified Control.Monad
import qualified Data.Bits.Pretty
import qualified Data.ByteString.Char8
import qualified Data.Maybe
import qualified Data.SVD.IO
import qualified Data.SVD.Pretty.Explore
import qualified Data.Text.IO
import qualified EmHell.Options
import qualified EmHell.SVD.Completion
import qualified EmHell.SVD.Query
import qualified EmHell.SVD.Manipulation
import qualified EmHell.SVD.Selector
import qualified HOCD
import qualified Prettyprinter
import qualified Prettyprinter.Render.Terminal
import qualified System.Console.Repline

import Options.Applicative

type Repl a =
  HaskelineT
    (ReaderT Device
      (OCDT IO)
    ) a

main :: IO ()
main = do
  opts <- runOpts

  liftIO
    $ putStrLn
    $ "Loading SVD file " <> optsSVD opts

  x <- Data.SVD.IO.parseSVD
         (optsSVD opts)
  dev <- case x of
    Left err -> error err
    Right dev -> pure dev

  let ocdConfig =
        def
          { ocdHost =
              Data.Maybe.fromMaybe
                (ocdHost def)
                (optsHost opts)
          , ocdPort =
              Data.Maybe.fromMaybe
                (ocdPort def)
                (optsPort opts)
          }

  _ <- HOCD.runOCDConfig ocdConfig $ do
    Control.Monad.void
      $ (`runReaderT` dev)
      $ runRepl
  pure ()

runRepl :: ReaderT Device (OCDT IO) ()
runRepl = do
  System.Console.Repline.evalRepl
    banner'
    (replCmd)
    options
    (Just ':')
    (Just "paste")
    completion
    greeter
    finalizer
  where
    banner' =
        pure
      . \case
          SingleLine -> "hocd> "
          MultiLine -> "| "

    options :: [(String, String -> Repl ())]
    options = [
        ("halt", halt)
      , ("reset", pure . lift $ HOCD.reset)
      , ("resume", pure . lift $ HOCD.resume)
      , ("set", setReg)
      , ("step", pure . lift $ HOCD.step)
      , ("version", version)
      ]

    completion :: CompleterStyle (ReaderT Device (OCDT IO))
    completion =
       Prefix
        ( EmHell.SVD.Completion.compFunc
          $ (ask >>=)
          . flip EmHell.SVD.Completion.svdCompleter
        )
        defaultMatcher

    defaultMatcher :: [(String, CompletionFunc (ReaderT Device (OCDT IO)))]
    defaultMatcher =
      [ ( ":set"
        , EmHell.SVD.Completion.compFunc
            $ (ask >>=)
            . flip EmHell.SVD.Completion.svdCompleterFields
        )
      ]

    greeter =
      liftIO
      $ putStrLn "Welcome to hocd"

    finalizer = pure Exit

replCmd :: String -> Repl ()
replCmd input = lift $ do
  dev <- ask
  case EmHell.SVD.Selector.parseSelector input of
    Left _e -> pure ()
    Right sel ->
      case
          EmHell.SVD.Query.getRegWithAddr
            (selPeriph sel)
            (selReg sel)
            dev
        of
          Right (reg, regAddr) -> do
            regVal <- lift
              . HOCD.readMem32
              . HOCD.memAddr
              $ fromIntegral regAddr
            liftIO
              $ Data.SVD.Pretty.Explore.exploreRegister
                  regVal
                  regAddr
                  reg

          Left e -> liftIO $ putStrLn e

halt :: String -> Repl ()
halt _ = do
 out <- lift $ HOCD.halt
 case out of
  "" -> pure ()
  x ->
    liftIO
      $ Data.ByteString.Char8.putStr x

setReg :: String -> Repl ()
setReg input = lift $ do
  dev <- ask
  case EmHell.SVD.Selector.parseSelectorValue input of
    Left e ->
      liftIO
        $ putStrLn
        $ "No parse " <> e
    Right (sel, v) ->
      case
          EmHell.SVD.Query.getRegWithAddr
            (selPeriph sel)
            (selReg sel)
            dev
        of
          Right (reg, regAddr) -> do
            let regMemAddr =
                  HOCD.memAddr
                  $ fromIntegral regAddr

            origRegVal <-
              lift $ HOCD.readMem32 regMemAddr

            let eNewVal = case selField sel of
                  Just f ->
                    EmHell.SVD.Manipulation.setField
                      reg
                      origRegVal
                      f
                      v
                  Nothing ->
                    pure v
            case eNewVal of
              Left e ->
                liftIO
                  . Data.Text.IO.putStrLn
                  . Prettyprinter.Render.Terminal.renderStrict
                  $ Prettyprinter.layoutPretty
                      Prettyprinter.defaultLayoutOptions
                      e
              Right newVal -> do
                liftIO
                  . Data.Text.IO.putStrLn
                  . Prettyprinter.Render.Terminal.renderStrict
                  $ Prettyprinter.layoutPretty
                      Prettyprinter.defaultLayoutOptions
                      (annotate
                        (bold <> color Green)
                        (   "Writing"
                        <+> pretty (Data.Bits.Pretty.formatHex newVal)
                        <+> "to"
                        <+> pretty (selPeriph sel)
                        <>  "."
                        <>  pretty (selReg sel)
                        )
                      )
                lift
                  $ HOCD.writeMem32
                      regMemAddr
                      [newVal]

                newRegVal <- lift $ HOCD.readMem32 regMemAddr

                liftIO
                  $ Data.SVD.Pretty.Explore.exploreRegister
                      newRegVal
                      regAddr
                      reg

          Left e -> liftIO $ putStrLn e

version :: String -> Repl ()
version _ =
  lift $ HOCD.version
  >>= liftIO . Data.ByteString.Char8.putStrLn

data Options = Options
  { optsSVD :: FilePath
  , optsHost :: Maybe String
  , optsPort :: Maybe Int
  } deriving Show

parseOptions :: Parser Options
parseOptions = Options <$>
      EmHell.Options.parseSVD
  <*> optional (strOption $
          long "host"
       <> metavar "HOST"
       <> help "Host where OpenOCD is listening")
  <*> optional
        (
          read
          <$> strOption
            (  long "port"
            <> help "OpenOCD RPC port"
            <> showDefault
            <> value "6666"
            )
        )

runOpts :: IO Options
runOpts =
  execParser
  $ info
      (parseOptions <**> helper)
      (fullDesc <> progDesc "hocd")