packages feed

hs2bf-0.6: SAM.hs

-- | Sequential Access Machine
--
-- This language makes implementation of various features easier by providing common C-like syntax.
--
-- Later this will be converted to very abstract graph representation and heavy optimization is
-- applied there (rule-based, mathematically-sound). It will be directly converted to BF.
--
-- This is the last language where direct debugging is possible.
--
--
-- Choice between 'Memory' and 'Locate'
--
-- * 'Memory' is for local operation(in a frame), and you can expect it to be heavily optimzied.
--  (Why not use 'Locate' manually? - special register optimization is possible for 'Memory')
--
-- * 'Locate' causes permanent change, and should be used for moving between frames
--  by not-predetermined amount.
--
-- * So in principle, you should minimize use of 'Locate', and use 'Memory' instead.
--
-- Multi-byte support direction:
--
-- * multiplication etc. is supported in this layer (manually)
--
-- * 'Val' 'Dispatch' 'Clear' 'While' 'Alloc' 'Delete' 'Move' should be expanded by a new 'Pointer'
--
-- * Difference from Integer support in Prelude: fixed size
module SAM where
import Control.Arrow
import Control.Monad
import Control.Monad.State
import Data.Char
import Data.Either
import Data.Graph
import Data.List
import Data.Maybe
import Data.Ord
import Data.Word
import qualified Data.Map as M
import qualified Data.Set as S
import Numeric
import Text.Printf

import Util
import SCGR
import Brainfuck


compile :: SAM -> Process SCGR
compile (SAM _ [SProc _ [] ss])=return $ BF $ soptBF $ concatMap compileS ss

soptBF []=[]
soptBF xs=case head xs of
    BFPInc -> sopAux 0 xs
    BFPDec -> sopAux 0 xs
    BFVInc -> sovAux 0 xs
    BFVDec -> sovAux 0 xs
    BFLoop s -> BFLoop (soptBF s):xs'
    BFInput -> BFInput:xs'
    BFOutput -> BFOutput:xs'
    where xs'=soptBF $ tail xs

sopAux n (BFPInc:xs)=sopAux (n+1) xs
sopAux n (BFPDec:xs)=sopAux (n-1) xs
sopAux n xs=dP n++soptBF xs

sovAux n (BFVInc:xs)=sovAux (n+1) xs
sovAux n (BFVDec:xs)=sovAux (n-1) xs
sovAux n xs=dV n++soptBF xs

compileS (Move p ps)=compileS $ While p $ Val p (-1):map (flip Val 1) ps
compileS (While (Memory _ d) ss)=concat
    [dP d
    ,[BFLoop $ concat [dP (negate d),concatMap compileS ss,dP d]]
    ,dP (negate d)]
compileS (Val (Memory _ d) v)=concat [dP d,dV v,dP $ negate d]
compileS (Input (Memory _ d))=dP d++[BFInput]++dP (negate d)
compileS (Output (Memory _ d))=dP d++[BFOutput]++dP (negate d)
compileS (Locate d)=dP d


dP x=replicateZ x BFPDec BFPInc
dV x=replicateZ x BFVDec BFVInc


replicateZ x m p
    |x==0 = []
    |x>0  = replicate x p
    |x<0  = replicate (negate x) m



-- | Apply this before 'SAM.compile'
--
-- * 'flatten': expand all inline calls
--
-- * 'desugar': 'Dispatch' -> 'While' 'Clear' -> 'Move' 'Copy' -> 'Move'
--     (don't expand 'Move' here, since they are good for later optimization)
--
-- * 'foldMemory': allocate registers
simplify :: SAM -> Process SAM
simplify s=
    checkSAM "SAM" s       >>= return . flatten "^" >>=
    checkSAM "SAM:flat"    >>= return . desugar >>=
    checkSAM "SAM:desugar" >>= return . allocateRegister >>=
    checkSAM "SAM:ralloc"  >>= return . foldMemory >>=
    checkSAM "SAM:folded"


-- | no register access
foldMemory :: SAM -> SAM
foldMemory (SAM rs [SProc name [] ss])=SAM [""] [SProc name [] $ map (foldMS (length rs) rs) ss]

foldMS n m (Move p ps)=Move (foldMP n m p) (map (foldMP n m) ps)
foldMS n m (While p ss)=While (foldMP n m p) (map (foldMS n m) ss)
foldMS n m (Val p d)=Val (foldMP n m p) d
foldMS n m (Locate d)=Locate $ n*d
foldMS n m (Input p)=Input (foldMP n m p)
foldMS n m (Output p)=Output (foldMP n m p)

foldMP n m (Memory r x)=Memory "" $ (fromJust $ elemIndex r m)+x*n


-- | /very bad/ register allocator
allocateRegister :: SAM -> SAM
allocateRegister (SAM rs [SProc name [] ss])
    |rs' `eqRS` [] = SAM (rs++["R"]) [SProc name [] $ concat sss]
    |otherwise     = error $ "allocateRegister: leaking register: "++show rs'
    where (rs',sss)=mapAccumL allocateRS [] ss

allocateRS :: [Maybe RegName] -> Stmt -> ([Maybe RegName],[Stmt])
allocateRS rs (Alloc r)=case elemIndex Nothing rs of
    Nothing -> (rs++[Just r],[])
    Just ix -> (mapAt ix (const $ Just r) rs,[])
allocateRS rs (Delete r)=case elemIndex (Just r) rs of
    Just ix -> (mapAt ix (const Nothing) rs,[Move (Memory "R" ix) []])
    Nothing -> error $ "allocateRS: deleting unknown register: "++r
allocateRS rs (Move p ps)=(rs,[Move (allocateRP rs p) (map (allocateRP rs) ps)])
allocateRS rs (While p ss)
    |eqRS rs rs' = (rs,[While (allocateRP rs p) $ concat sss])
    |otherwise   = error "allocateRS: unmatched register scope in while"
    where (rs',sss)=mapAccumL allocateRS rs ss
allocateRS rs (Val p d)=(rs,[Val (allocateRP rs p) d])
allocateRS rs (Input p)=(rs,[Input (allocateRP rs p)])
allocateRS rs (Output p)=(rs,[Output (allocateRP rs p)])
{-
allocateRS rs (Locate d)
    |d/=0 = (rs',mv++[Locate d])
    |d==0 = (rs,[])
    where
        rs'=map (\ix->lookup (ix-d) table') area
        table'=map (snd3 &&& thr3) table
        area=[0..maximum (map snd3 table)-d-1]
        
        mv=map (\(fr,to,_)->Move (Memory "R" fr) [Memory "R" to]) table
        table=repackRS S.empty (map (second fromJust) $ filter (isJust . snd) $ zip [0..] rs) d
-}
-- just works (TM)
allocateRS rs (Locate d)
    |d>0 = (rs,mvPos++[Locate d])
    |d<0 = (rs,mvNeg++[Locate d])
    |d==0 = (rs,[])
    where
        mvPos=reverse mvNeg
        mvNeg=map (gen . fst) $ filter (isJust . snd) $ zip [0..] rs
        gen ix=Move (Memory "R" ix) [Memory "R" $ ix+d]

-- | repack registers as densely as possible without causing collision
repackRS :: S.Set Int -> [(Int,RegName)] -> Int -> [(Int,Int,RegName)]
repackRS _ [] d=[]
repackRS al rs d
    |S.null cand = error "repackRS: unknown situation" -- what to do? (this is actually possible)
    |not $ S.null nocost = allocate (S.findMin nocost) (S.findMin nocost)
    |otherwise = allocate (fst $ head rs)  (S.findMin cand)
    where
        allocate fr to=(fr,to,maybe undefined id $ lookup fr rs):rs'
            where rs'=repackRS (S.insert to al) (filter ((/=fr) . fst) rs) d
        
        nocost=S.intersection cand from
        cand=S.fromList [d..d+length rs-1] S.\\ al
        from=S.fromList $ map fst rs



eqRS :: [Maybe RegName] -> [Maybe RegName] -> Bool
eqRS [] []=True
eqRS (Nothing:xs) []=eqRS xs []
eqRS [] (Nothing:ys)=eqRS [] ys
eqRS (x:xs) (y:ys)=(x==y) && (xs `eqRS` ys)
eqRS _ _=False




allocateRP :: [Maybe RegName] -> Pointer -> Pointer
allocateRP rs (Memory x d)=Memory x d
allocateRP rs (Register r)
    =maybe (error $ "allocateRP: non-allocated register: "++r) (Memory "R") $ elemIndex (Just r) rs




desugar :: SAM -> SAM
desugar (SAM rs [SProc name [] ss])=SAM rs [SProc name [] ss']
    where
        ss'=[Alloc "_dt"]++concatMap desugarStmt ss++[Delete "_dt"]

desugarStmt :: Stmt -> [Stmt]
desugarStmt (Dispatch r cs)=concatMap desugarStmt $ expandDispatch r $ sortBy (comparing fst) cs
desugarStmt (While ptr ss)=[While ptr $ concatMap desugarStmt ss]
desugarStmt (Clear ptr)=[Move ptr []]
desugarStmt (Copy p ps)=[Alloc "_ct",Move p [Register "_ct"],Move (Register "_ct") (p:ps),Delete "_ct"]
desugarStmt (Comment _)=[]
desugarStmt s=[s]

-- | Case numbers must be sorted in ascending order.
-- _dt must be 0 before and after dispatch
-- r must be 0 after dispatch
expandDispatch r []=error "expandDispatch: empty dispatch"
expandDispatch r [(n,e)]
    |abs n<3   = [Val (Register r) $ negate n]++e
    |otherwise = [Clear (Register r)]++e
expandDispatch r ((n0,e0):cs)=
    [Val (Register "_dt") 1
    ,Val (Register r) (negate $ n0)
    ,While (Register r) $
        Val (Register "_dt") (-1):
        expandDispatch r (map (\(n,e)->(n-n0,e)) cs)
    ,While (Register "_dt") $
        Val (Register "_dt") (-1):e0
    ]



-- | Sequential Access Machine
data SAM=SAM [Region] [SProc] deriving(Show)

data SProc=SProc ProcName [RegName] [Stmt] deriving(Show)

procName :: SProc -> ProcName
procName (SProc name _ _)=name

-- | Statement set of SAM.
--
-- Operations with 'RegName' in their arguments changes scope
data Stmt
    =Locate Int -- ^ ptr+=n
    |While Pointer [Stmt]
    |Val Pointer Int
    |Alloc RegName
    |Delete RegName
    |Move Pointer [Pointer]
    |Copy Pointer [Pointer] -- ^ syntax sugar of 'Move'
    |Clear Pointer -- ^ syntax sugar of Move p []
    |Dispatch RegName [(Int,[Stmt])] -- ^ in case alts, given RegName will be out of scope. This instruction is erratic in many ways...
    |Inline ProcName [RegName]
    |Input Pointer
    |Output Pointer
    |Comment String -- ^ one-line comment
    deriving(Show)

data Pointer
    =Register RegName
    |Memory Region Int

instance Show Pointer where
    show (Register x)=x
    show (Memory region n)
        |n==0 = "$"++region
        |n>0  = "$"++region++"+"++show n
        |n<0  = "$"++region++show n

type Region=String
type ProcName=String
type RegName=String




pprint :: SAM -> String
pprint (SAM rs ps)=compileSB $ Group
    [Line $ Span $ map Prim rs
    ,EmptyLine
    ,EmptyLine
    ,Group $ intersperse EmptyLine $ map pprintSP ps
    ]

pprintSP :: SProc -> SBlock
pprintSP (SProc name args st)=Group
    [Line $ Span [Prim "pr",Pack $ Prim name:darg]
    ,Indent $ Group $ map pprintStmt st
    ]
    where
        darg|null args = []
            |otherwise = [Prim "/",Span $ map Prim args]

pprintStmt :: Stmt -> SBlock
pprintStmt (While ptr ss)=Group $
    [Line $ Span [Prim "while",Prim $ show ptr]
    ,Indent $ Group $ map pprintStmt ss]
pprintStmt (Dispatch n cs)=Group $
    [Line $ Span [Prim "dispatch",Prim n]
    ,Indent $ Group $ map (f . first show) cs
    ]
    where f (l,ss)=Group [Line $ Prim l,Indent $ Group $ map pprintStmt ss]
pprintStmt s=Line $ Span $ case s of
    Val p n     -> [Prim "val",Prim $ show p,Prim $ show n]
    Alloc n     -> [Prim "alloc",Prim n]
    Delete n    -> [Prim "delete",Prim n]
    Move d ss   -> Prim "move":map (Prim . show) (d:ss)
    Copy d ss   -> Prim "copy":map (Prim . show) (d:ss)
    Locate n    -> [Prim "locate",Prim $ show n]
    Inline n rs -> map Prim ("inline":n:rs)
    Clear r     -> [Prim "clear",Prim $ show r]
    Input p     -> [Prim "in",Prim $ show p]
    Output p    -> [Prim "out",Prim $ show p]
    Comment s   -> [Prim "--",Prim s]






-- | Flatten procedures from given root.
flatten :: ProcName -> SAM -> SAM
flatten root (SAM rs ps)
    |not $ null cycles = error $ "flatten: dependency cycles:\n"++unlines (map unwords cycles)
    |otherwise         = SAM rs [m2p root $ foldl expandProc (ps2m ps) vs]
    where
        (cycles,vs)=partitionEithers $ map f $ stronglyConnComp $ map procNode ps
        f (AcyclicSCC x)=Right x
        f (CyclicSCC xs)=Left xs

        ps2m=M.fromList . map (\(SProc name args ss)->(name,(args,ss)))
        m2p r m=uncurry (SProc r) $ m M.! r



-- | Construct a node for procedure dependecy graph
procNode :: SProc -> (ProcName,ProcName,[ProcName])
procNode (SProc n args ss)=(n,n,S.toList $ S.unions $ map stmtDep ss)

-- | Collect 'Inline'd procedures from 'Stmt'
stmtDep :: Stmt -> S.Set ProcName
stmtDep (While _ ss)=S.unions $ map stmtDep ss
stmtDep (Dispatch _ cs)=S.unions $ map stmtDep $ concatMap snd cs
stmtDep (Inline n _)=S.singleton n
stmtDep _=S.empty

-- | Expand the given proc in the map non-recursively.
expandProc :: M.Map ProcName ([RegName],[Stmt]) -> ProcName -> M.Map ProcName ([RegName],[Stmt])
expandProc m r=M.adjust (second $ expandStmts m) r m

expandStmts :: M.Map ProcName ([RegName],[Stmt]) -> [Stmt] -> [Stmt]
expandStmts m=concatMap (expandStmt m)

expandStmt :: M.Map ProcName ([RegName],[Stmt]) -> Stmt -> [Stmt]
expandStmt m (Inline n rsP)=map (replaceStmt f) ss
    where
        (rsC,ss)=M.findWithDefault (error $ "flattenProc:unknown proc "++n) n m
        f reg=case lookup reg $ zip rsC rsP of
                  Just rsp -> rsp
                  Nothing  -> n++"/"++reg
expandStmt m (While p ss)=[While p $ expandStmts m ss]
expandStmt m (Dispatch p cs)=[Dispatch p $ map (second $ expandStmts m) cs]
expandStmt _ s=[s]


-- | Apply register name transformation.
replaceStmt :: (RegName -> RegName) -> Stmt -> Stmt
replaceStmt f (While ptr ss)=While (replacePtr f ptr) $ map (replaceStmt f) ss
replaceStmt f (Dispatch n cs)=Dispatch (f n) $ map (second (map $ replaceStmt f)) cs
replaceStmt f (Val p n)=Val (replacePtr f p) n
replaceStmt f (Alloc n)=Alloc $ f n
replaceStmt f (Delete n)=Delete $ f n
replaceStmt f (Clear p)=Clear $ replacePtr f p
replaceStmt f (Move p ps)=Move (replacePtr f p) (map (replacePtr f) ps)
replaceStmt f (Copy p ps)=Copy (replacePtr f p) (map (replacePtr f) ps)
replaceStmt f (Inline n ss)=error "replaceStmt: Inline: re-check expansion order"
replaceStmt f (Input p)=Input (replacePtr f p)
replaceStmt f (Output p)=Output (replacePtr f p)
replaceStmt _ s=s

replacePtr :: (RegName -> RegName) -> Pointer -> Pointer
replacePtr f (Register x)=Register $ f x
replacePtr _ p=p





-- | 'NRM' instance for use in 'checkProc'
type NMRE a=NMR String String a

-- | Just a wrapper of 'checkProc' for 'SAM'. No additional checks.
checkSAM :: String -> SAM -> Process SAM
checkSAM loc s@(SAM x procs)
    |null errors = return s
    |otherwise   = throwError errors
    where
        errors=map (\(pos,msg)->CompileErrorN loc msg pos) $ snd $ runNMR $ mapM_ checkProc procs


-- | Find static erros in a 'SProc'.
-- 
-- What's being done here is usual variable scope analysis. But the data dependecy graph will be a
-- DAG, not tree.
--
-- * unknown registers
--
-- * unmatched register in 'While' and 'Dispatch'
--
-- TODO:
--
-- * 'Alloc' or 'Delete' of argument registers
--
-- * modification of flag register in 'Dispatch'
checkProc :: SProc -> NMRE ()
checkProc (SProc name args ss)=within ("proc "++name) $ do
    let rs=S.fromList args
    when (S.size rs/=length args) $ report "duplicate arguments"
    rs'<-checkStmt ss rs
    when (rs/=rs') $ report $ "leaking registers: "++unwords (S.toList $ rs' S.\\ rs)


checkStmt :: [Stmt] -> S.Set RegName -> NMRE (S.Set RegName)
checkStmt [] rs=return rs
checkStmt ((While ptr ss):xs) rs=do
    within "while flag" $ checkPointer ptr rs
    rs'<-within "while body" $ checkStmt ss rs
    when (rs/=rs') $ within "while" $ report $ "leaking registers: "++unwords (S.toList $ rs' S.\\ rs)
    checkStmt xs rs
checkStmt ((Dispatch n cs):xs) rs=do
    unless (S.member n rs) $ within "dispatch header" $ report $ "unknown register:"++show n
    let integrity rsB=when (rsB/=rs) $ report $ "leaking registers:"++unwords (S.toList $ rsB S.\\ rs)
    forM_ cs (\(n,ss)->within ("dispatch clause "++show n) $ checkStmt ss rs >>= integrity)
    checkStmt xs rs
checkStmt ((Alloc n):xs) rs=do
    when (S.member n rs) $ report $ "duplicated allocation of "++n
    checkStmt xs $ S.insert n rs
checkStmt ((Delete n):xs) rs=do
    unless (S.member n rs) $ report $ "deleting unallocated register "++n
    checkStmt xs $ S.delete n rs
checkStmt ((Move p ps):xs) rs=mapM_ (\x->within "move" $ checkPointer x rs) (p:ps) >> checkStmt xs rs
checkStmt ((Copy p ps):xs) rs=mapM_ (\x->within "copy" $ checkPointer x rs) (p:ps) >> checkStmt xs rs
checkStmt ((Val p _):xs) rs=within "val" (checkPointer p rs) >> checkStmt xs rs
checkStmt ((Clear p):xs) rs=within "clear" (checkPointer p rs) >> checkStmt xs rs
checkStmt ((Inline name ns):xs) rs=do
    let s=S.fromList ns
    unless (s `S.isSubsetOf` rs) $ within ("inline "++name) $ report $ "unknown registers: " ++unwords (S.toList $s S.\\ rs)
    checkStmt xs rs
checkStmt ((Input p):xs) rs=within "input" (checkPointer p rs) >> checkStmt xs rs
checkStmt ((Output p):xs) rs=within "output" (checkPointer p rs) >> checkStmt xs rs
checkStmt (_:xs) rs=checkStmt xs rs


checkPointer :: Pointer -> S.Set RegName -> NMRE ()
checkPointer (Register x) rs=unless (S.member x rs) $ within "pointer" $ report $ "unknown register: "++x
checkPointer _ rs=return ()



-- | Interpreter of 'SAM', usable for all phases.
interpret :: SAM -> IO ()
interpret=runProcessWithIO f . checkSAM "SAMi"
    where f (SAM rs procs)=let ptb0=M.fromList $ map (procName &&& id) procs
                               mtb0=(M.fromList $ zip rs $ repeat minit)
                               st0=SAMInternal ptb0 mtb0 M.empty 0
                           in evalStateT (enterProc "^" []) st0

data SAMInternal=SAMInternal
    {procTable :: M.Map ProcName SProc
    ,memTable :: MemTable
    ,regTable :: RegTable
    ,pointer :: Int
    }

type MemTable=M.Map Region FlatMemory
type RegTable=M.Map ProcName (M.Map RegName Word8,M.Map RegName (ProcName,RegName))

type SAMST=StateT SAMInternal


enterProc :: ProcName -> [(ProcName,RegName)] -> SAMST IO ()
enterProc name args=do
    liftIO $ putStrLn $ "entering:"++name
    dumpRegisters
    dumpMemory
    
    ptb<-liftM procTable get
    rtb<-liftM regTable get
    let SProc _ rs ss=M.findWithDefault (error $ "SAMi: procedure not found: "++name) name ptb
    when (length rs/=length args) $ error $ "SAMi: procedure arity error: "++show (name,rs,args) 
    when (M.member name rtb) $ error $ "SAMi: re-entring to precedure: "++name
    
    let rtb'=M.insert name (M.empty,M.fromList $ zipWith (\org to->(to,uncurry (reduceReg rtb) org)) args rs) rtb
    modify (\x->x{regTable=rtb'})
    execStmts name ss
    modify (\x->x{regTable=M.delete name $ regTable x})
    liftIO $ putStrLn $ "leaving:"++name

dumpMemory :: SAMST IO ()
dumpMemory=do
    t<-liftM memTable get
    p<-liftM pointer get
    let maxAddr=max 0 $ maximum (map msize $ M.elems t)-1
        ss=map (\x->dumpMemoryBetween p t (x*w,(x+1)*w-1)) [0..maxAddr `div` w]
    liftIO $ putStr $ unlines ss
    where w=30

dumpMemoryBetween :: Int -> MemTable -> (Int,Int) -> String
dumpMemoryBetween p t (a0,a1)=unlines $ map dumpKey ks
    where
        ks=M.keys t
        head=maximum $ map length ks
        dumpKey k=printf ("%"++show head++"s|") k++dump (t M.! k)
        dump fm=concatMap (\x->showAddr x $ mread fm x) [a0..a1]
        showAddr a v=(if a==p then ">" else " ")++(printf "%02s" $ showHex v "")

dumpRegisters :: SAMST IO ()
dumpRegisters=do
    r<-liftM regTable get
    let ss=map (uncurry dumpRegisterP) $ M.assocs r
    liftIO $ putStr $ concat ss

dumpRegisterP :: ProcName -> (M.Map RegName Word8,M.Map RegName (ProcName,RegName)) -> String
dumpRegisterP proc (m0,m1)=unlines $ ("in "++proc++":"):rs
    where
        rs=(map (\(n,v)->"  "++n++": "++showHex v "") $ M.assocs m0)++
           (map (\(n,a)->"  "++n++" -> "++show a) $ M.assocs m1)

    
execStmts p=mapM_ (\x->execStmt p x >> liftIO (putStrLn (p++" "++(take 50 $ show x))) >> dumpRegisters >> dumpMemory >> liftIO (putStrLn ""))

execStmt p (Alloc r)=modifyRT $ M.adjust (first $ M.insert r 0) p
execStmt p (Delete r)=modifyRT $ M.adjust (first $ M.delete r) p
execStmt p (Inline n rs)=enterProc n (zip (repeat p) rs)
execStmt p (Val ptr d)=liftM (+fromIntegral d) (readPtr p ptr) >>= writePtr p ptr
execStmt p s0@(While ptr ss)=do
    x<-readPtr p ptr
    when (x/=0) $ execStmts p ss >> execStmt p s0
execStmt p (Move ptr ptrs)=forM (ptr:ptrs) (readPtr p) >>= zipWithM_ (\ptr x->writePtr p ptr x) (ptr:ptrs) . f
    where f (x:xs)=0:map (+x) xs
execStmt p (Copy ptr ptrs)=forM (ptr:ptrs) (readPtr p) >>= zipWithM_ (\ptr x->writePtr p ptr x) ptrs . f
    where f (x:xs)=map (+x) xs
execStmt p (Locate d)=modifyPointer (+d)
execStmt p (Dispatch r cs)=do
    x<-readPtr p (Register r)
    writePtr p (Register r) 0
    let caluse=lookup (fromIntegral x) cs
    maybe (error $ "SAMi: dispatch:"++show (x,r,p)) (execStmts p) caluse
execStmt p (Clear ptr)=writePtr p ptr 0
execStmt p (Input ptr)=liftIO getChar >>= writePtr p ptr . fromIntegral . ord
execStmt p (Output ptr)=readPtr p ptr >>= liftIO . putChar . chr . fromIntegral
execStmt p (Comment _)=return ()



readPtr :: Monad m => ProcName -> Pointer -> SAMST m Word8
readPtr p (Memory r d)=do
    dp<-liftM pointer get
    when (dp+d<0) $ error $ "readPtr: invalid op:"++show (p,r,dp,d)
    liftM (flip mread (dp+d) . (M.! r) . memTable) get
readPtr p (Register r)=liftM (flip (flip readReg p) r . regTable) get


writePtr :: Monad m => ProcName -> Pointer -> Word8 -> SAMST m ()
writePtr p (Memory r d) x=do
    dp<-liftM pointer get
    when (dp+d<0) $ error $ "writePtr: invalid op:"++show (p,r,dp,d)
    modifyMT $ M.adjust (flip (flip mwrite (dp+d)) x) r
writePtr p (Register r) x=modifyRT (\t->writeReg t p r x)




readReg :: RegTable -> ProcName -> RegName -> Word8
readReg t p r=(fst (t M.! p')) M.! r'
    where (p',r')=reduceReg t p r

writeReg :: RegTable -> ProcName -> RegName -> Word8 -> RegTable
writeReg t p r x=M.adjust (first $ M.insert r' x) p' t
    where (p',r')=reduceReg t p r


reduceReg :: RegTable -> ProcName -> RegName -> (ProcName,RegName)
reduceReg t p r
    |M.member r org = (p,r)
    |otherwise      = alias M.! r
    where (org,alias)=t M.! p



modifyRT :: Monad m => (RegTable -> RegTable) -> SAMST m ()
modifyRT f=modify $ \x->x{regTable=f $ regTable x}

modifyMT :: Monad m => (MemTable -> MemTable) -> SAMST m ()
modifyMT f=modify $ \x->x{memTable=f $ memTable x}

modifyPointer :: Monad m => (Int -> Int) -> SAMST m ()
modifyPointer f=modify $ \x->x{pointer=g $ pointer x}
    where g x=let y=f x in if x<0 then error $ "modifyPointer: invalid pos: "++show y else y