retroclash-lib-0.1.0: src/RetroClash/BCD.hs
{-# OPTIONS_GHC -fconstraint-solver-iterations=5 #-}
module RetroClash.BCD
( Digit, toDigit
, BCD
, fromBCD
, BCDSize
, toBCD
, ShiftAdd, initBCD, stepBCD
, prop_BCD
) where
import Clash.Prelude hiding (shift, add)
import RetroClash.Utils
type Digit = Index 10
type BCD n = Vec n Digit
toDigit :: Unsigned 4 -> Digit
toDigit = bitCoerce
fromBCD :: BCD n -> Integer
fromBCD = foldl (\x d -> x * 10 + fromIntegral d) 0
type BCDSize n = CLog 10 (2 ^ n)
type ShiftAdd n = (Vec (BCDSize n) (Unsigned 4), Unsigned n)
{-# INLINE initBCD #-}
initBCD :: (KnownNat n) => Unsigned n -> ShiftAdd n
initBCD = (,) (repeat 0)
stepBCD :: (KnownNat n) => ShiftAdd n -> ShiftAdd n
stepBCD = shift . add
where
shift :: (KnownNat n) => ShiftAdd n -> ShiftAdd n
shift = bitwise (`shiftL` 1)
add :: ShiftAdd n -> ShiftAdd n
add (digits, buf) = (map add3 digits, buf)
where
add3 d = if d >= 5 then d + 3 else d
{-# INLINE toBCD #-}
toBCD :: forall n. (KnownNat n) => Unsigned n -> BCD (BCDSize n)
toBCD = map toDigit . fst . last . iterate (SNat @(n + 1)) stepBCD . initBCD
roundtrip :: (KnownNat n) => Unsigned n -> Unsigned n
roundtrip = fromIntegral . fromBCD . map bitCoerce . toBCD
prop_BCD :: (KnownNat n) => Unsigned n -> Bool
prop_BCD x = x == roundtrip x