libriscv-0.1.0.0: lib/LibRISCV/Effects/Operations/Default/Interpreter.hs
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
-- | Implements the default (concrete) interpreter for the 'Operations' effect.
module LibRISCV.Effects.Operations.Default.Interpreter (
ArchState (..),
mkArchState,
dumpState,
defaultInstructions,
) where
import Control.Monad.Freer (type (~>))
import Control.Monad.IO.Class (MonadIO (..))
import Data.Array.IO (IOUArray)
import Data.BitVector (BV, bitVec)
import Data.Int (Int32)
import Data.Word (Word16, Word32, Word8)
import LibRISCV (Address)
import qualified LibRISCV.Effects.Operations.Default.Machine.Memory as MEM
import qualified LibRISCV.Effects.Operations.Default.Machine.Register as REG
import LibRISCV.Effects.Operations.Language (
Operations (..),
Size (Byte, Half, Word),
)
import Numeric (showHex)
-- | Representation of the concrete architectural state of the interpreter.
data ArchState = ArchState
{ getReg :: REG.RegisterFile IOUArray Int32
-- ^ Register file implementation of the architectural state.
, getMem :: MEM.Memory IOUArray Word8
-- ^ Memory implementation of the architectural state.
}
-- | Create a new t'ArchState' based on a memory start address and a memory size.
mkArchState :: Address -> Word32 -> IO ArchState
mkArchState memStart memSize = do
reg <- REG.mkRegFile 0
mem <- MEM.mkMemory memStart memSize
pure $ ArchState reg mem
-- | Write a textual representation of the t'ArchState' to standard output.
dumpState :: ArchState -> IO ()
dumpState ArchState{getReg = r} =
REG.dumpRegs (showHex . fromIntegral @Int32 @Word32) r >>= putStr
-- | Implements concrete interpretation of the 'Operations' effect based on a 'BV' value representation.
defaultInstructions :: (MonadIO m) => ArchState -> Operations BV ~> m
defaultInstructions (ArchState regFile mem) =
liftIO . \case
ReadRegister idx -> bitVec 32 <$> REG.readRegister regFile (toEnum $ fromIntegral idx)
WriteRegister idx reg -> REG.writeRegister regFile (toEnum $ fromIntegral idx) (fromIntegral reg)
Load size addr -> case size of
Byte -> bitVec 8 <$> MEM.loadByte mem (fromIntegral addr)
Half -> bitVec 16 <$> (MEM.loadHalf mem (fromIntegral addr) :: IO Word16)
Word -> bitVec 32 <$> MEM.loadWord @_ @_ @BV mem (fromIntegral addr)
Store size addr w -> case size of
Byte -> MEM.storeByte mem (fromIntegral addr) (fromIntegral w)
Half -> MEM.storeHalf mem (fromIntegral addr) w
Word -> MEM.storeWord mem (fromIntegral addr) w
WritePC w -> REG.writePC regFile (fromIntegral w)
ReadPC -> bitVec 32 <$> REG.readPC regFile
Exception pc msg -> error $ "[0x" ++ showHex pc "" ++ "] " ++ msg
Ecall pc -> putStrLn $ "ecall at 0x" ++ showHex pc ""
Ebreak pc -> putStrLn $ "ebreak at 0x" ++ showHex pc ""