136 lines
3.6 KiB
Haskell
136 lines
3.6 KiB
Haskell
{-# LANGUAGE OverloadedStrings #-}
|
|
{-# LANGUAGE RecordWildCards #-}
|
|
|
|
module Mima.Flag
|
|
( AddressRange
|
|
, lowerAddress
|
|
, upperAddress
|
|
, range
|
|
, rangeToAddresses
|
|
, rangeContains
|
|
, simplifyRanges
|
|
, AddressSpec
|
|
, rangesToSpec
|
|
, specToRanges
|
|
, specNull
|
|
, specContains
|
|
, Flag(..)
|
|
, allFlags
|
|
, flagChar
|
|
, Flags(..)
|
|
, rawFlags
|
|
, flagChecks
|
|
, impotentChecks
|
|
) where
|
|
|
|
import Data.List
|
|
import qualified Data.Map as Map
|
|
import qualified Data.Set as Set
|
|
|
|
import Mima.Word
|
|
|
|
data AddressRange = AddressRange
|
|
{ lowerAddress :: MimaAddress
|
|
, upperAddress :: MimaAddress
|
|
} deriving (Show, Eq, Ord)
|
|
|
|
range :: MimaAddress -> MimaAddress -> AddressRange
|
|
range a b
|
|
| a <= b = AddressRange a b
|
|
| otherwise = AddressRange b a
|
|
|
|
rangeToAddresses :: AddressRange -> [MimaAddress]
|
|
rangeToAddresses r = [lowerAddress r..upperAddress r]
|
|
|
|
rangeContains :: AddressRange -> MimaAddress -> Bool
|
|
rangeContains (AddressRange a b) c = a <= c && c <= b
|
|
|
|
simplifyRanges :: [AddressRange] -> [AddressRange]
|
|
simplifyRanges = helper . sort
|
|
where
|
|
helper :: [AddressRange] -> [AddressRange]
|
|
helper (r1:r2:rs)
|
|
| upperAddress r1 >= lowerAddress r2 = helper (merge r1 r2 : rs)
|
|
| otherwise = r1 : helper (r2:rs)
|
|
helper a = a
|
|
|
|
merge :: AddressRange -> AddressRange -> AddressRange
|
|
merge (AddressRange a1 b1) (AddressRange _ b2) = AddressRange a1 (max b1 b2)
|
|
|
|
|
|
newtype AddressSpec = AddressSpec [AddressRange]
|
|
deriving (Show)
|
|
|
|
rangesToSpec :: [AddressRange] -> AddressSpec
|
|
rangesToSpec = AddressSpec . simplifyRanges
|
|
|
|
specToRanges :: AddressSpec -> [AddressRange]
|
|
specToRanges (AddressSpec ranges) = ranges
|
|
|
|
specNull :: AddressSpec -> Bool
|
|
specNull = null . specToRanges
|
|
|
|
specContains :: AddressSpec -> MimaAddress -> Bool
|
|
specContains as addr = any (`rangeContains` addr) $ specToRanges as
|
|
|
|
{- Enough preamble, let's get to the flags -}
|
|
|
|
data Flag = Breakpoint | Executable | ReadOnly
|
|
deriving (Show, Eq, Ord)
|
|
|
|
allFlags :: [Flag]
|
|
allFlags = [Breakpoint, Executable, ReadOnly]
|
|
|
|
flagChar :: Flag -> Char
|
|
flagChar Breakpoint = 'b'
|
|
flagChar Executable = 'e'
|
|
flagChar ReadOnly = 'r'
|
|
|
|
data Flags a = Flags
|
|
{ flagBreakpoint :: a
|
|
, flagExecutable :: a
|
|
, flagReadOnly :: a
|
|
} deriving (Show)
|
|
|
|
instance Functor Flags where
|
|
fmap f Flags{..} = Flags
|
|
{ flagBreakpoint = f flagBreakpoint
|
|
, flagExecutable = f flagExecutable
|
|
, flagReadOnly = f flagReadOnly
|
|
}
|
|
|
|
instance Applicative Flags where
|
|
pure a = Flags a a a
|
|
f <*> a = Flags
|
|
{ flagBreakpoint = flagBreakpoint f $ flagBreakpoint a
|
|
, flagExecutable = flagExecutable f $ flagExecutable a
|
|
, flagReadOnly = flagReadOnly f $ flagReadOnly a
|
|
}
|
|
|
|
rawFlags :: Flags Flag
|
|
rawFlags = Flags
|
|
{ flagBreakpoint = Breakpoint
|
|
, flagExecutable = Executable
|
|
, flagReadOnly = ReadOnly
|
|
}
|
|
|
|
flagChecks :: Map.Map AddressRange (Set.Set Flag) -> Flags (MimaAddress -> Bool)
|
|
flagChecks m =
|
|
let getAddressSpec :: Flag -> AddressSpec
|
|
getAddressSpec f = rangesToSpec $ map fst $ filter (Set.member f . snd) $ Map.assocs m
|
|
|
|
conditions :: Flags (AddressSpec -> MimaAddress -> Bool)
|
|
conditions = Flags
|
|
{ flagBreakpoint = specContains
|
|
, flagExecutable = \as -> if specNull as then const True else specContains as
|
|
, flagReadOnly = specContains
|
|
}
|
|
in conditions <*> (getAddressSpec <$> rawFlags)
|
|
|
|
-- | Flag checks that should not alter the behaviour of the MiMa.
|
|
impotentChecks :: Flags (MimaAddress -> Bool)
|
|
impotentChecks = Flags
|
|
{ flagBreakpoint = const False
|
|
, flagExecutable = const True
|
|
, flagReadOnly = const False
|
|
}
|