diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright (c) 2012 Andrew Kay
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,5 @@
+module Main where
+
+import Distribution.Simple
+
+main = defaultMain
diff --git a/hbcd.cabal b/hbcd.cabal
new file mode 100644
--- /dev/null
+++ b/hbcd.cabal
@@ -0,0 +1,28 @@
+Name:               hbcd
+Version:            1.0
+Synopsis:           Packed binary-coded decimal (BCD) serialization
+Description:        A module containing packed binary-coded decimal (BCD) serialization
+                    functions.  Packed BCD is commonly used to encode numbers in mainframe
+                    datasets.
+License:            MIT
+License-File:       LICENSE.txt
+Copyright:          Andrew Kay, 2012
+Author:             Andrew Kay
+Maintainer:         andrewjkay@gmail.com
+Stability:          provisional
+Category:           Data
+Cabal-Version:      >= 1.6
+Build-Type:         Simple
+
+Library
+  Exposed-Modules:  Data.BCD.Packed
+  HS-Source-Dirs:   src
+  Build-Depends:    base >= 4 && < 5,
+                    bytestring >= 0.9.1.10,
+                    Decimal >= 0.2.3,
+                    digits >= 0.2,
+                    split >= 0.1.4.3
+
+Source-Repository head
+  Type:             hg
+  Location:         https://bitbucket.org/andrewjkay/hbcd
diff --git a/src/Data/BCD/Packed.hs b/src/Data/BCD/Packed.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/BCD/Packed.hs
@@ -0,0 +1,96 @@
+{- |
+Module      :  Data.BCD.Packed
+Copyright   :  Andrew Kay, 2012
+License:    :  MIT
+
+Maintainer  :  andrewjkay@gmail.com
+Stability   :  experimental
+Portability :  portable
+
+A module containing packed binary-coded decimal (BCD) serialization functions
+-}
+
+module Data.BCD.Packed (
+	bytesRequired,
+	packInteger,
+	unpackInteger,
+	packDecimal,
+	unpackDecimal ) where
+
+import Data.Word
+import Data.Bits
+import qualified Data.ByteString as BS
+import Data.Decimal (Decimal, DecimalRaw(Decimal), decimalMantissa, decimalPlaces, roundTo)
+import Data.Digits (digits)
+import Data.List.Split (splitEvery)
+
+-- | Pack two digits into a byte
+packByte :: [Word8] -> Word8
+packByte [m, l] = (shiftL m 4) .|. l
+
+-- | Pack every pair of two digits into a byte resulting in a ByteString half as long as
+--   the input
+packBytes :: [Word8] -> BS.ByteString
+packBytes bx = BS.pack $ map packByte $ splitEvery 2 bx
+
+-- | Unpack two digits from a byte
+unpackByte :: Word8 -> [Word8]
+unpackByte b = [shiftR (b .&. 0xf0) 4, b .&. 0x0f]
+
+-- | Unpack two digits from every byte in a ByteString resulting in a new ByteString twice
+--   as long as the input
+unpackBytes :: BS.ByteString -> BS.ByteString
+unpackBytes bs = BS.concatMap (\b -> BS.pack $ unpackByte b) bs
+
+-- | Encode the sign
+encodeSign :: Integer -> Word8
+encodeSign s
+    | s < 0     = 0xd
+    | otherwise = 0xc
+
+-- | Decode the sign
+decodeSign :: Word8 -> Integer
+decodeSign n = case n of
+    0xc -> 1
+    0xd -> -1
+    otherwise -> error "Unsupported sign value"
+
+-- | Calculate the bytes required to store a number of digits
+bytesRequired :: Int    -- ^ Number of digits (including leading zeros)
+              -> Int    -- ^ Number of bytes required
+bytesRequired l = ceiling (((fromIntegral l) + 1) / 2)
+
+-- | Pack an Integer into a ByteString
+packInteger :: Int              -- ^ Number of digits (including leading zeros)
+            -> Integer          -- ^ Value 
+            -> BS.ByteString    -- ^ Packed BCD
+packInteger l n
+    | dsl > l = error "Number is to large for field"
+    | otherwise = packBytes $ (replicate (nl - dsl - 1) 0) ++ ds ++ [s]
+    where ds = map (\d -> fromIntegral d) (digits 10 (abs n))
+          s = encodeSign (signum n)
+          nl = (bytesRequired l) * 2
+          dsl = length ds
+
+-- | Unpack an Integer from a ByteString
+unpackInteger :: BS.ByteString    -- ^ Packed BCD
+              -> Integer          -- ^ Value
+unpackInteger bs = n * s
+    where ubs = unpackBytes bs
+          n = BS.foldl (\n d -> (n * 10) + (toInteger d)) 0 $ BS.init ubs
+          s = decodeSign $ BS.last ubs
+
+-- | Pack a Decimal into a ByteString
+packDecimal :: Int              -- ^ Number of digits (including leading zeros and decimal places)
+            -> Word8            -- ^ Number of decimal places
+            -> Decimal          -- ^ Value
+            -> BS.ByteString    -- ^ Packed BCD
+packDecimal l d n
+    | (decimalPlaces n) > d = error "Decimal places to large for field"
+    | otherwise = packInteger l $ decimalMantissa $ roundTo d n
+
+-- | Unpack a Decimal from a ByteString
+unpackDecimal :: Word8            -- ^ Number of decimal places
+              -> BS.ByteString    -- ^ Packed BCD
+              -> Decimal          -- ^ Value
+unpackDecimal d bs = Decimal d $ unpackInteger bs
