{- 
   Fields: Mackerel register fields
   
  Part of Mackerel: a strawman device definition DSL for Barrelfish
   
  Copyright (c) 2007, 2008, ETH Zurich.
  All rights reserved.
  
  This file is distributed under the terms in the attached LICENSE file.
  If you do not find this file, copies can be found by writing to:
  ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
-}  

module Fields where

import Attr
import Data.Bits
import Text.ParserCombinators.Parsec
import MackerelParser
import TypeName as TN

data Rec = Rec { name :: String,
                 size :: Integer,
                 offset :: Integer,
                 attr :: Attr,
                 initial :: Integer,
                 tpe :: Maybe TN.Name,
                 desc :: String,
                 pos :: SourcePos,
                 is_anon :: Bool }
           deriving (Show,Eq)

is_writeonly :: Rec -> Bool
is_writeonly f = attr_is_writeonly (attr f)

is_readable :: Rec -> Bool
is_readable  f = attr_is_readable (attr f)

is_writeable :: Rec -> Bool
is_writeable f = attr_is_writeable (attr f)

is_rsvd :: Rec -> Bool
is_rsvd Rec { attr = RSVD } = True
is_rsvd _ = False

--
-- Create a list of fields, in the right order, with the right default
-- attribute, from a set of declarations.
--
make_list :: String -> Attr -> BitOrder -> Integer -> [AST] -> [Rec]
make_list dn dflt order 0 decls 
    = make_list_from_word dn dflt order 0 decls
make_list dn dflt order word_size decls 
    = make_list_of_words dn dflt order word_size 0 decls []


make_list_of_words :: String -> Attr -> BitOrder -> Integer -> Integer
                      -> [AST] -> [AST] -> [Rec]
make_list_of_words dn dflt order word_size off decls acc 
    = let acc_length = foldl (+) 0 [ s | (RegField _ s _ _ _ _) <- acc ]
      in
        if acc_length >= word_size then
            let al = make_list_from_word dn dflt order off acc
                new_off = (offset $ last al) + (size $ last al)
            in 
              al ++ (make_list_of_words dn dflt order word_size new_off decls [])
        else 
            if (length decls) == 0 then
                 make_list_from_word dn dflt order off acc
            else
                make_list_of_words dn dflt order word_size off (tail decls) (acc ++ [head decls])


make_list_from_word :: String -> Attr -> BitOrder -> Integer -> [AST] -> [Rec]
make_list_from_word dn dflt LSBFIRST init_offset decls = 
    -- Cons up a list of the offsets of each field in the structure. 
    let add_sizes decls = 
            foldl (\t (RegField _ s _ _ _ _) -> t ++ [(last t) + s]) [init_offset] decls
    in map (make_field dn dflt False) $ zip decls (add_sizes decls)
make_list_from_word dn dflt MSBFIRST init_offset decls = 
    make_list_from_word dn dflt LSBFIRST init_offset (reverse decls)

--   make_list_from_word dflt MSBFIRST init_offset decls =
-- -    make_list dflt LSBFIRST init_offset (reverse decls)
-- +    make_list_from_word dflt LSBFIRST init_offset (reverse decls)
--
-- Create a list of fields, in the right order, with the right 
-- attribute, from a set of other fields (e.g. from a type).
-- The inheritance rules for attributes are as follows:
--
inherit_list :: Attr -> [Rec] -> [Rec]
inherit_list regattr ftlist = 
    [ r { attr = (if (attr r) == NOATTR then regattr else (attr r)) } | r <- ftlist ]

--
-- Fix up default attributes.  Anything without an attributed defaults
-- to the attribute of the register (dflt here), unless it's "_", in
-- which case it defaults to RSVD.
--
make_field :: String -> Attr -> Bool -> (AST, Integer) -> Rec
make_field dn dflt anon ((RegField id sz a t dsc p), off) 
    | id == "_" = 
        make_field dn RSVD True ((RegField ("_anon" ++ show off) sz a t "_" p), off)
    | otherwise = 
        Rec { name = id, 
              size = sz, 
              offset = off,
              initial = if a == MB1 then (shift 1 $ fromInteger sz) - 1 else 0,
              attr = if a == NOATTR then dflt else a,
              tpe = make_ftype t dn,
              desc = dsc, 
              pos = p, 
              is_anon = anon }

make_ftype :: AST -> String -> Maybe TN.Name
make_ftype NoBitFieldType _ = Nothing
make_ftype t@(TypeRef _ _) dn = Just (TN.fromRef t dn)

--
-- Generate masks and shifts for isolating this field.  These functions
-- are polymorphic so that they don't need to know how large the total
-- load unit is (32 bits? 8 bits?) etc.
--
extract_mask :: (Num a, Bits a) => Rec -> Integer -> a
extract_mask f sz = 
    foldl setBit 0 (enumFromTo (fromInteger $ offset f) 
                               (fromInteger $ (offset f) + (size f) - 1))
insert_mask :: (Num a, Bits a) => Rec -> Integer -> a
insert_mask f sz = 
    foldl complementBit (extract_mask f sz) (enumFromTo 0 (fromInteger sz - 1))

extract_shift :: Rec -> Integer
extract_shift f = - (insert_shift f)

insert_shift :: Rec -> Integer
insert_shift f = offset f

initial_mask :: Rec -> Integer
initial_mask f = shift (initial f) (fromInteger $ insert_shift f)
