diff --git a/Data/Binary/ISO8583.hs b/Data/Binary/ISO8583.hs
new file mode 100644
--- /dev/null
+++ b/Data/Binary/ISO8583.hs
@@ -0,0 +1,238 @@
+----------------------------------------------------------------------------
+-- |
+-- Module      :  Data.Binary.Bitmap
+-- Copyright   :  (c) Ilya Portnov 2014
+-- License     :  BSD3-style (see LICENSE)
+--
+-- Maintainer  :  portnov84@rambler.ru
+-- Stability   :  unstable
+-- Portability :  not tested
+--
+-- This module contains Get and Put method implementations for ISO 8583 style
+-- bitmaps, and also utility methods for getting/putting fields in formats
+-- commonly used in ISO 8583 specification.
+--
+----------------------------------------------------------------------------
+
+module Data.Binary.ISO8583
+ (-- * Usage
+  -- $usage
+  getBitmapFieldNumbers, 
+  getBitmap,
+  mergeFieldNumbers,
+  putBitmap, putBitmap',
+  embeddedLen,
+  asciiNumber, asciiNumberF,
+  putAsciiNumber, putEmbeddedLen,
+  putByteStringPad, putLazyByteStringPad,
+  toBS, fromBS ) where
+
+import Control.Monad
+import Data.Binary
+import Data.Binary.Get
+import Data.Binary.Put
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Lazy as L
+import qualified Data.ByteString.Char8 as C8
+import Data.Int
+import Data.Word
+import Data.Bits
+import Data.Char
+import Data.List
+import qualified Data.Map as M
+import Data.Maybe
+import Text.Printf
+
+--import Debug.Trace
+
+-- $usage
+--
+-- Typical usage is:
+--
+-- > data Message = Message {pan :: B.ByteString, stan :: Integer}
+-- > deriving (Eq, Show)
+--
+-- > data FieldValue = String B.ByteString | Int Integer
+-- > deriving (Eq, Show)
+--
+-- > getField :: Int -> Maybe (Get FieldValue)
+-- > getField 2 = Just $ String `fmap` embeddedLen 2
+-- > getField 11 = Just $ Int `fmap` asciiNumberF 11 6
+-- > getField _ = Nothing
+--
+-- > getMessage :: Get Message
+-- > getMessage = do
+-- >   m <- getBitmap getField
+-- >   let Just (String pan) = M.lookup 2 m
+-- >   let Just (Int stan) = M.lookup 11 m
+-- >   return $ Message pan stan
+--
+-- > putMessage :: Message -> Put
+-- > putMessage (Message pan stan) = do
+-- >   putBitmap [(2, putEmbeddedLen 2 pan),
+-- >              (11, putAsciiNumber 6 stan)] 
+--
+
+bytes :: [(Word8, Word8)]
+bytes = let digits = "0123456789ABCDEF"
+        in  [(fromIntegral (ord digit), n) | (digit, n) <- zip digits [0..]]
+
+bits :: [(Word8, [Int])]
+bits = [(digit, [i | i <- [1..4] , testBit n (4-i)]) | (digit, n) <- bytes]
+
+bitsSet :: Int -> Word8 -> [Int]
+bitsSet byteNr b = 
+  let fs = case lookup b bits of
+             Just x -> x
+             Nothing -> error $ "Unknown byte: " ++ [chr (fromIntegral b)]
+  in [4*byteNr + n | n <- fs]
+
+-- | Parse bitmap. Return numbers of fields present.
+-- NB: only two bitmaps are supported as for now (Primary and Secondary bitmaps in ISO 8583 notation).
+getBitmapFieldNumbers :: Get [Int]
+getBitmapFieldNumbers = do
+  bm <- getByteString 16
+  let xs = [bitsSet i b | (i,b) <- zip [0..] (B.unpack bm)]
+  return $ concat xs
+
+mergeFieldNumbersW :: [Int] -> [Word64]
+mergeFieldNumbersW fs =
+  let (fs1,fs2) = partition (<65) fs
+      go b f = setBit b (63-f)
+      w1 = foldl go 0 $ map (\f -> f - 1) fs1
+      w2 = foldl go 0 $ map (\f -> f - 65) fs2
+  in  if w2 == 0
+        then [w1]
+        else [setBit w1 63, w2]
+
+-- | Merge numbers of fields present into ISO bitmap.
+mergeFieldNumbers :: [Int] -> B.ByteString
+mergeFieldNumbers fs =
+  -- TODO: usage of toUpper . printf for hexadecimal number is pretty slow.
+  let hex n = toBS $ map toUpper $ printf "%016x" n
+  in case mergeFieldNumbersW fs of
+       [w1] -> hex w1
+       [w1,w2] -> hex w1 `B.append` hex w2
+
+-- | Parse ISO 8583-style bitmap.
+-- Fails if unsupported field is present in message.
+getBitmap :: (Int -> Maybe (Get f))  -- ^ Parser for n'th field, or Nothing if field is not supported
+          -> Get (M.Map Int f)
+getBitmap getter = do
+  fs1 <- getBitmapFieldNumbers
+  fs2 <- if 1 `elem` fs1
+          then getBitmapFieldNumbers
+          else return []
+  let fs = fs1 ++ map (64+) fs2
+  res <- forM fs $ \f ->
+            if f == 1
+              then return []
+              else
+                case getter f of
+                  Nothing -> fail $ "Unsupported field #" ++ show f
+                  Just fn -> do
+                    offset <- bytesRead
+                    --trace ("Parsing field #" ++ show f ++ " at " ++ show offset) $ return ()
+                    fn >>= (\x -> return [(f, x)])
+  return $ M.fromList $ concat res
+               
+-- | Put ISO 8583-style bitmap.
+putBitmap :: [(Int, Put)] -- ^ (Field number, field putter)
+          -> Put
+putBitmap fs = do
+  let fieldNumbers = map fst fs
+  --trace ("Putting fields: " ++ show fieldNumbers) $ return ()
+  putByteString $ mergeFieldNumbers fieldNumbers
+  mapM_ snd fs
+
+-- | Put ISO 8583-style bitmap.
+putBitmap' :: [(Int, Maybe Put)] -- ^ (Field number, field putter or Nothing if field is not present)
+           -> Put
+putBitmap' fs = do
+  let fs' = [(f, p) | (f, Just p) <- fs]
+  let fieldNumbers = map fst fs'
+  --trace ("Putting fields: " ++ show fieldNumbers) $ return ()
+  putByteString $ mergeFieldNumbers fieldNumbers
+  mapM_ snd fs'
+
+-- | Parse string with embedded length (LLVAR/LLLVAR in ISO 8583 notation)
+embeddedLen :: Int              -- ^ Field number (to be used in error message)
+            -> Int              -- ^ Number of bytes used for length (2 for LLVAR, 3 for LLLVAR and so on)
+            -> Get B.ByteString
+embeddedLen f n = do
+  sz <- asciiNumberF f n
+  getByteString (fromIntegral sz)
+
+-- | Parse number of given length in ASCII notation; 
+-- Report bitmap field number in case of error.
+asciiNumberF :: Int          -- ^ Field number, to be used in error message
+             -> Int          -- ^ Number length
+             -> Get Integer
+asciiNumberF f n = do
+  bs <- getByteString n
+  case C8.readInteger bs of
+    Just (res, s)
+      | C8.null s -> return res
+    _ -> fail $ "Cannot parse number: <" ++ fromBS bs ++ "> in field #" ++ show f
+
+-- | Parse number of given length in ASCII notation
+asciiNumber :: Int      -- ^ Number length
+            -> Get Int
+asciiNumber n = do
+  bs <- getByteString n
+  case reads (fromBS bs) of
+    [(res, "")] -> return res
+    _ -> fail $ "Cannot parse number: " ++ fromBS bs
+
+-- | Put number of given length in ASCII notation
+putAsciiNumber :: Int      -- ^ Number length
+               -> Integer  -- ^ Number
+               -> Put
+putAsciiNumber sz n = do
+  let s = show n
+      m = length s
+      s' = if m < sz
+             then replicate (sz-m) '0' ++ s
+             else if m == sz
+                    then s
+                    else drop (m-sz) s
+      bs = toBS s'
+  putByteString bs
+
+-- | Put string with embedded length (LLVAR/LLLVAR in ISO 8583 notation)
+putEmbeddedLen :: Int          -- ^ Number of bytes used for length (2 for LLVAR, 3 for LLLVAR and so on)
+               -> B.ByteString -- ^ String to put
+               -> Put
+putEmbeddedLen sz bstr = do
+  let len = fromIntegral $ B.length bstr
+  putAsciiNumber sz len
+  putByteString bstr
+
+-- | Put space-padded string of given length
+putByteStringPad :: Int           -- ^ Field length
+                 -> B.ByteString  -- ^ String to put
+                 -> Put
+putByteStringPad sz bstr = do
+  let len = B.length bstr
+  let bstr' = if len < sz
+                then B.replicate (sz-len) 0x20 `B.append` bstr
+                else bstr
+  putByteString bstr'
+
+-- | Put space-padded string of given length
+putLazyByteStringPad :: Int64           -- ^ Field length
+                 -> L.ByteString  -- ^ String to put
+                 -> Put
+putLazyByteStringPad sz bstr = do
+  let len = L.length bstr
+  let bstr' = if len < sz
+                then L.replicate (sz-len) 0x20 `L.append` bstr
+                else bstr
+  putLazyByteString bstr'
+
+toBS :: String -> B.ByteString
+toBS str = B.pack $ map (fromIntegral . ord) str
+
+fromBS :: B.ByteString -> String
+fromBS bstr = map (chr . fromIntegral) $ B.unpack bstr
+
diff --git a/Data/Binary/ISO8583/TH.hs b/Data/Binary/ISO8583/TH.hs
new file mode 100644
--- /dev/null
+++ b/Data/Binary/ISO8583/TH.hs
@@ -0,0 +1,314 @@
+{-# LANGUAGE TemplateHaskell, ExistentialQuantification #-}
+----------------------------------------------------------------------------
+-- |
+-- Module      :  Data.Binary.Bitmap.TH
+-- Copyright   :  (c) Ilya Portnov 2014
+-- License     :  BSD3-style (see LICENSE)
+--
+-- Maintainer  :  portnov84@rambler.ru
+-- Stability   :  unstable
+-- Portability :  not tested
+--
+-- This module contains QuasiQuoter for declarative description of
+-- ISO 8583-based message formats.
+--
+----------------------------------------------------------------------------
+
+module Data.Binary.ISO8583.TH
+  (-- * Usage
+   -- $usage
+   FieldType (..),
+   FieldValue (..),
+   Field (..),
+   pData,
+   string2data,
+   binaryQ, binary
+  ) where
+
+import Control.Monad
+import Text.Parsec
+import qualified Text.Parsec.Token as P
+import Text.Parsec.Language (haskellDef)
+import Text.Parsec.String
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Lazy as L
+import Data.ByteString (ByteString)
+import qualified Data.Map as M
+import Data.Generics
+
+import Language.Haskell.TH.Syntax
+import Language.Haskell.TH.Quote
+import Language.Haskell.TH.Lift
+
+import Data.Binary
+import Data.Binary.Get
+import Data.Binary.Put
+
+import Data.Binary.ISO8583
+
+-- $usage
+--
+-- Typical usage is:
+--
+-- > [binary|
+-- >   Message
+-- >   2 pan embedded 2
+-- >   4 amount int 12
+-- >   11 stan int 6
+-- >   43 termAddress TermAddress 222
+-- > |]
+--
+-- > data TermAddress = TermAddress ...
+--
+-- > instance Binary TermAddress where ...
+--
+-- Quasi-quote format is:
+--
+--  - First line - name of data type to generate
+--
+--  - Each of other lines describes one field, in following format:
+--  {Field number} {Field name} {data type} {type parameter}
+--
+--  Internally supported data types are: 
+--
+--  - int - integer field. Parameter defines size of field.
+--
+--  - str - fixed-length string field. Parameter defines size of field.
+--  
+--  - embedded - embedded-length string field. Parameter defines number of bytes used to store field length (2 for LLVAR, 3 for LLLVAR and so on).
+--
+--  Any other data type, instance of Binary class, may be used as well.
+--
+--  Quasi-quoter generates data type definition, and also following functions:
+--
+--  - get[Message] :: Int -> Maybe (Get FieldValue)
+--
+--  - put[Message] :: Message -> [(Int, Maybe Put)]
+--
+--  - construct[Message] :: M.Map Int FieldValue -> Message
+--
+-- Concrete ISO 8583-based formats usually use some kind of message header, or 
+-- use bitmap only as small part of overall format. So, it's usually no point of
+-- generating instance Binary Message - it's better to write instance for your
+-- format by using functions mentioned above.
+--
+-- > instance Binary Message where
+-- >   get = do
+-- >     -- parse some kind of header here, then:
+-- >     m <- getBitmap getMessage
+-- >     return $ constructMessage m
+-- >   
+-- >   put msg = do
+-- >     -- write some kind of header here, then:
+-- >     putBitmap' (putMessage msg)
+--
+
+-- | Supported field types
+data FieldType =
+       TInt Int          -- ^ Integer field of given size
+     | TString Int       -- ^ Fixed-length string field
+     | TEmbeddedLen Int  -- ^ Variable-length string field with embedded length (LLVAR and so on)
+     | TOther String Int -- ^ User-defined field - any data type with instance Binary. NB: second parameter is not used currently.
+  deriving (Eq, Show)
+
+deriveLift ''FieldType
+
+-- | Field format description
+data Field =
+      Field {
+        fNumber :: Int      -- ^ Field number
+      , fName :: String     -- ^ Field name
+      , fType :: FieldType  -- ^ Field type description
+    }
+  deriving (Eq, Show)
+
+-- | Supported field values
+data FieldValue =
+       FInt Integer         -- ^ Integer field
+     | FString ByteString   -- ^ String fields
+     | forall t. (Binary t, Show t, Typeable t) => FOther t -- ^ User-defined field
+
+instance Eq FieldValue where
+  (FInt x) == (FInt y) = x == y
+  (FString x) == (FString y) = x == y
+  (FOther x) == (FOther y) = runPut (put x) == runPut (put y)
+  _ == _ = False
+
+instance Show FieldValue where
+  show (FInt x) = show x
+  show (FString x) = show x
+  show (FOther x) = show $ runPut (put x)
+
+unpackFInt :: FieldValue -> Integer
+unpackFInt (FInt x) = x
+unpackFInt x = error $ "Internal error: not an integer: " ++ show x
+
+unpackFString :: FieldValue -> ByteString
+unpackFString (FString str) = str
+unpackFString x = error $ "Internal error: not a string: " ++ show x
+
+toStrict :: L.ByteString -> B.ByteString
+toStrict s = B.concat $ L.toChunks s
+
+fromStrict :: B.ByteString -> L.ByteString
+fromStrict s = L.fromChunks [s]
+
+unpackOther :: FieldValue -> ByteString
+unpackOther (FOther x) = toStrict $ runPut (put x)
+unpackOther _ = error $ "Internal error: not a FOther"
+
+lexer = P.makeTokenParser haskellDef
+identifier = P.identifier lexer
+number = P.natural lexer
+
+pField :: Parser Field
+pField = do
+  skipMany (space <|> newline)
+  n <- number
+  skipMany space
+  name <- identifier
+  skipMany space
+  t <- identifier
+  skipMany space
+  param <- number
+  ft <- case t of
+          "int" -> return $ TInt (fromIntegral param)
+          "str" -> return $ TString (fromIntegral param)
+          "embedded" -> return $ TEmbeddedLen (fromIntegral param)
+          _ -> return $ TOther t (fromIntegral param)
+  return $ Field (fromIntegral n) name ft
+
+pFields :: Parser [Field]
+pFields = do
+  fs <- pField `sepEndBy1` many newline
+  eof
+  return fs
+
+-- | Parse data format definition
+pData :: Parser (String, [Field])
+pData = do
+  skipMany (space <|> newline)
+  name <- identifier
+  skipMany newline
+  fs <- pFields
+  return (name, fs)
+
+-- | Generate `data Message = Message {...'
+generateData :: String    -- ^ Data type name
+             -> [Field]   -- ^ Fields description
+             -> Q [Dec]
+generateData nameStr fields = do
+    let name = mkName nameStr
+    fields' <- mapM convertField fields
+    let constructor = RecC name fields'
+    return [ DataD [] name [] [constructor] [] ]
+  where
+    convertField (Field _ fname ftype) = do
+      t <- convertType ftype
+      return (mkName fname, NotStrict, t)
+
+    convertType (TInt _) = [t| Maybe Integer |]
+    convertType (TString _) = [t| Maybe B.ByteString |]
+    convertType (TEmbeddedLen _) = [t| Maybe B.ByteString |]
+    convertType (TOther n _) = do
+        let name = mkName n
+        return $ ConT (mkName "Maybe") `AppT` ConT name
+
+-- | Generate `getMessage 2 = Just $ ...'
+mkGetter :: String -> [Field] -> Q [Dec]
+mkGetter nameStr fields = do
+    let name = mkName nameStr
+    clauses <- forM fields $ \(Field f _ ft) -> mkGetClause f ft
+    unsupportedClause <- mkUnsupportedClause 
+    return [FunD name (clauses ++ [unsupportedClause])]
+  where
+    mkGetClause :: Int -> FieldType -> Q Clause
+    mkGetClause n ft = do
+      let pats = [LitP (IntegerL (fromIntegral n))]
+      let int x = return $ LitE $ IntegerL (fromIntegral x)
+      body <- case ft of 
+                TInt sz -> [| Just $ FInt `fmap` asciiNumberF $(int n) $(int sz) |]
+                TString sz -> [| Just $ FString `fmap` getByteString $(int sz) |]
+                TEmbeddedLen sz -> [| Just $ FString `fmap` embeddedLen $(int n) $(int sz) |]
+                TOther name _ -> [| Just $ FOther `fmap` (get :: Get $(return $ ConT $ mkName name) ) |]
+      return $ Clause pats (NormalB body) []
+
+    mkUnsupportedClause :: Q Clause
+    mkUnsupportedClause = do
+      body <- [| Nothing |]
+      let pats = [ WildP ]
+      return $ Clause pats (NormalB body) []
+
+-- | Generate `putMessage'
+mkPutter :: String -> [Field] -> Q [Dec]
+mkPutter nameStr fields = do
+    msg <- newName "msg"
+    let name = mkName nameStr
+        pat = VarP msg
+    body <- ListE `fmap` mapM (mkBody msg) fields
+    let clause = Clause [pat] (NormalB body) []
+    return [FunD name [clause]]
+  where
+    mkBody msg (Field f fname ftype) = do
+      let getter = return $ VarE $ mkName fname
+      let msgvar = return $ VarE msg
+      let wrapper = case ftype of
+                     TInt _ -> return $ ConE $ mkName "FInt"
+                     TString _    -> return $ ConE $ mkName "FString"
+                     TEmbeddedLen _    -> return $ ConE $ mkName "FString"
+                     TOther name _ -> return $ ConE $ mkName "FOther"
+      let putter = [| putField $(lift ftype) ( $(wrapper) `fmap` $(getter) $(msgvar) ) |]
+      [| ( $(lift f) , $(putter) ) |]
+
+putField :: FieldType -> Maybe FieldValue -> Maybe Put
+putField _ Nothing = Nothing
+putField (TInt sz) (Just (FInt n)) = Just $ putAsciiNumber sz n
+putField (TString sz) (Just (FString s)) = Just $ putByteStringPad sz s
+putField (TEmbeddedLen sz) (Just (FString s)) = Just $ putEmbeddedLen sz s
+putField (TOther _ _) (Just (FOther x)) = Just $ put x
+putField t (Just v) = fail $ "Internal error: field value " ++ show v ++ " does not match type " ++ show t
+
+mkConstructor :: String -> String -> [Field] -> Q [Dec]
+mkConstructor prefix nameStr fields = do
+    msg <- newName "msg"
+    let name = mkName nameStr
+        pat = VarP msg
+    body <- RecConE name `fmap` mapM (mkBody msg) fields
+    let clause = Clause [pat] (NormalB body) []
+    return [FunD (mkName $ prefix ++ nameStr) [clause]]
+  where
+    mkBody msg (Field f fname ftype) = do
+      let msgvar = return $ VarE msg
+      let unpack = case ftype of
+                     TInt _ -> [| unpackFInt |]
+                     TString _ -> [| unpackFString |]
+                     TEmbeddedLen _ -> [| unpackFString |]
+                     TOther _ _ -> [| runGet get . fromStrict . unpackOther |]
+      rhs <- [| $(unpack) `fmap` M.lookup $(lift f) $(msgvar) |]
+      return (mkName fname, rhs)
+
+-- | Generate only data type definition
+string2data :: String -> Q [Dec]
+string2data str = do
+  case parse pData "<input>" str of
+    Right (name, fields) -> generateData name fields
+    Left err -> fail $ show err
+
+binaryQ :: String -> Q [Dec]
+binaryQ str = do
+  case parse pData "<input>" str of
+    Right (name, fields) -> do
+        dtype <- generateData name fields
+        getter <- mkGetter ("get" ++ name) fields
+        putter <- mkPutter ("put" ++ name) fields
+        cons <- mkConstructor "construct" name fields
+        return $ dtype ++ getter ++ putter ++ cons
+    Left err -> fail $ show err
+
+dataOnly :: QuasiQuoter
+dataOnly = QuasiQuoter {quoteDec = string2data, quoteExp=undefined, quotePat=undefined, quoteType=undefined}
+
+-- | Main function here.
+binary :: QuasiQuoter
+binary = QuasiQuoter {quoteDec = binaryQ,  quoteExp=undefined, quotePat=undefined, quoteType=undefined}
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2014, IlyaPortnov
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of IlyaPortnov nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/Test.hs b/Test.hs
new file mode 100644
--- /dev/null
+++ b/Test.hs
@@ -0,0 +1,76 @@
+{-# LANGUAGE TemplateHaskell, QuasiQuotes, StandaloneDeriving, DeriveDataTypeable #-}
+
+module Test where
+
+import Control.Applicative
+import Control.Monad
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Lazy as L
+import Data.Binary
+import Data.Binary.Get
+import Data.Binary.Put
+import qualified Data.Map as M
+import Data.Generics
+
+import Data.Binary.ISO8583
+import Data.Binary.ISO8583.TH
+
+[binary|
+  Message
+  2 pan embedded 2
+  4 amount int 12
+  11 stan int 6
+  43 termAddress TermAddress 222
+|]
+
+deriving instance Eq Message
+deriving instance Show Message
+
+data TermAddress = TermAddress {
+      tOwner :: B.ByteString,
+      tCity :: B.ByteString,
+      tOther :: L.ByteString }
+  deriving (Eq, Show, Typeable)
+
+instance Binary TermAddress where
+  -- NB: this implementation is smth odd and usable only for this testcase.
+  get =
+    TermAddress
+      <$> B.filter (/= 0x20) `fmap` getByteString 30
+      <*> B.filter (/= 0x20) `fmap` getByteString 30
+      <*> L.filter (/= 0x20) `fmap` getRemainingLazyByteString
+
+  put (TermAddress owner city other) = do
+    putByteStringPad 30 owner
+    putByteStringPad 30 city
+    putLazyByteStringPad 162 other
+
+instance Binary Message where
+  get = do
+    m <- getBitmap getMessage
+    return $ constructMessage m
+  
+  put msg = do
+    putBitmap' (putMessage msg)
+    
+testMsg :: Message
+testMsg = Message {
+  pan = Just $ toBS "12345678",
+  amount = Just $ 100500,
+  stan = Just $ 123456,
+  termAddress = Just $ TermAddress {
+                  tOwner = toBS "TestBank",
+                  tCity = toBS "Magnitogorsk",
+                  tOther = L.empty }
+}
+
+test :: IO ()
+test = do
+  let bstr = encode testMsg
+      msg = decode bstr
+  if msg /= testMsg
+    then fail $ "Encode/decode mismatch:\n" ++
+           show testMsg ++ "\n /= \n" ++
+           show msg
+    else putStrLn "passed."
+
diff --git a/iso8583-bitmaps.cabal b/iso8583-bitmaps.cabal
new file mode 100644
--- /dev/null
+++ b/iso8583-bitmaps.cabal
@@ -0,0 +1,41 @@
+-- Initial bitmaps.cabal generated by cabal init.  For further 
+-- documentation, see http://haskell.org/cabal/users-guide/
+
+name:                iso8583-bitmaps
+version:             0.1.0.0
+synopsis:            Parse and merge ISO 8583-style bitmaps
+description:         This package provides utility methods for writing
+                     Get and Put methods (in terms of Data.Binary) for parsing and
+                     merging ISO 8583-style bitmaps, and also parsing and merging
+                     most commonly used field formats (LLVAR/LLLVAR, ASCII numbers
+                     and so on).
+                     Moreover, this package provides TH quasiquoter for declarative
+                     specification of ISO 8583-based message formats.
+
+license:             BSD3
+license-file:        LICENSE
+author:              IlyaPortnov
+maintainer:          portnov84@rambler.ru
+-- copyright:           
+category:            Data
+build-type:          Simple
+cabal-version:       >=1.8
+
+extra-source-files: Test.hs
+
+library
+  exposed-modules:     Data.Binary.ISO8583,
+                       Data.Binary.ISO8583.TH
+  -- other-modules:       
+  build-depends:       base > 4 && < 5,
+                       binary >=0.5,
+                       bytestring >=0.9,
+                       containers >=0.4,
+                       parsec >=3.1,
+                       template-haskell >=2.7,
+                       th-lift >=0.6,
+                       syb >= 0.3.6
+
+source-repository head
+  type: git
+  location: https://github.com/portnov/iso8583-bitmaps.git
