packages feed

libriscv-0.1.0.0: lib/LibRISCV/Effects/Operations/Default/Machine/Register.hs

{-# LANGUAGE FlexibleContexts #-}

-- | Provides a polymorphic implementation of a register file. This module is
-- intended to be used internally by interpreters for the
-- 'LibRISCV.Effects.Operations.Operations' effect. This register file
-- implementation also provides facilities for storing a concrete program
-- counter.
module LibRISCV.Effects.Operations.Default.Machine.Register where

import Control.Monad (unless)
import Data.Array.IO (
    MArray (newArray),
    getElems,
    readArray,
    writeArray,
 )
import Data.IORef (IORef, newIORef, readIORef, writeIORef)
import Data.Word (Word32)
import LibRISCV

-- | Register file addressed by 'RegIdx'. The type is parameterized over an array
-- implementation (such as 'Data.Array.IO.IOUArray') and a generic value type
-- (used to represent instruction operands).
data RegisterFile t a = RegisterFile
    { pc :: IORef Word32
    -- ^ The current program counter (always concrete in this implementation).
    , regs :: t RegIdx a
    -- ^ The underlying array to store the register values.
    }

-- | Create a new register file, initializing all registers with the given default
-- value. This value /must/ represent the zero value in the chosen value type.
mkRegFile :: (MArray t a IO) => a -> IO (RegisterFile t a)
mkRegFile defValue = RegisterFile <$> newIORef 0 <*> newArray (minBound, maxBound) defValue

-- | Dump the current register file state as a 'String'.
dumpRegs :: (MArray t a IO) => (a -> ShowS) -> RegisterFile t a -> IO String
dumpRegs sh =
    fmap
        ( foldr (\(a, v) s -> show a ++ "\t= 0x" ++ sh v "\n" ++ s) ""
            . zip [(minBound :: RegIdx) .. maxBound]
        )
        . getElems
        . regs

------------------------------------------------------------------------

-- | Read register value at given register index. For the 'Zero' register index, the
-- zero/default value (as passed to 'mkRegFile' is always returned.
readRegister :: (MArray t a IO) => RegisterFile t a -> RegIdx -> IO a
readRegister = readArray . regs

-- | Write register at given register index. Writes to the 'Zero' register are ignored.
writeRegister :: (MArray t a IO) => RegisterFile t a -> RegIdx -> a -> IO ()
writeRegister RegisterFile{regs = r} idx val = unless (idx == Zero) $ writeArray r idx val

------------------------------------------------------------------------

-- | Returs the current program counter value.
readPC :: RegisterFile t a -> IO Word32
readPC = readIORef . pc

-- | Write a new program counter value.
writePC :: RegisterFile t a -> Word32 -> IO ()
writePC = writeIORef . pc