packages feed

soyuz-0.0.0: DCPU16/Assembler.hs

-- | Assembler.
--
-- Works with "DCPU16.Instructions". Does no optimizations, just converts
-- labels to direct addresses and produces machine code.
--
-- Needs to be extended to support relative addressing. Extending labels to do
-- arithmetic: @sub pc,this-loop@ is a necessary step. I see this as more of an
-- optimization problem, since these kinds of rewrites would be neat to perform
-- automatically...
--
-- We'll see which direction it gets approached from.
module DCPU16.Assembler 
    ( assemble
    -- * Utility
    , labelAddrs
    ) where
import DCPU16.Instructions
import DCPU16.Instructions.Size
import Data.Word (Word16)
import Data.Vector (Vector)
import Data.ByteString (ByteString)
import qualified Data.ByteString as B
import qualified Data.Vector as V
import Data.Generics.Uniplate.Data
import qualified Data.Map as M
import Data.Map (Map)
import Data.Serialize

-- | Turn labels into direct addresses, and output machine code.
assemble :: Vector Instruction -> ByteString
assemble = B.concat . V.toList . V.map (runPut . put) . labelsToAddrs


-- | Build address lookup table for label definitions.
labelAddrs :: Vector Instruction -> Map ByteString Word16
labelAddrs = snd . V.foldl f (0,M.empty) where
    f (addr,map) i = (addr',map') where
        addr' = addr + size i
        map' = case i of
            (Label s) -> M.insert s addr map
            _ -> map

-- | Replace every reference to a label with an address.
--
-- Strips comments and label definitions as well, assuming they won't be needed.
labelsToAddrs :: Vector Instruction -> Vector Instruction
labelsToAddrs is = 
    let is' =  V.filter (not . isComment) is
        lut = labelAddrs is'
    in  V.map (remap lut) . V.filter (not . isLabel) $ is'
  where
    isComment (Comment _ _) = True
    isComment _ = False
    isLabel (Label _) = True
    isLabel _ = False
    remap lut = transformBi f where
        f (LabelAddr s) = case M.lookup s lut of
            Just addr -> Const addr
            Nothing -> error $ "Undefined label: "++show s
        f x = x