retroclash-sim-0.1.0: src/RetroClash/Sim/VGA.hs
{-# LANGUAGE NumericUnderscores, RecordWildCards #-}
{-# LANGUAGE RankNTypes #-}
module RetroClash.Sim.VGA
( vgaSink
, initSink
, SinkState
) where
import RetroClash.Utils
import RetroClash.VGA
import Control.Monad.State
import Data.Foldable (for_)
import Control.Lens hiding (Index)
import Clash.Prelude hiding (lift)
import Data.Proxy
import Debug.Trace
vgaRetrace :: VGATiming visible -> (Int, Bit)
vgaRetrace VGATiming{..} = (snatToNum pulseWidth + snatToNum postWidth - 1, toActiveDyn polarity True)
data SinkState n
= Visible (Index n)
| WaitSync Bool
| Retrace Int
deriving (Show)
{-# INLINE vgaSink #-}
vgaSink
:: forall w h rgb m ps. (KnownNat w, KnownNat h, MonadState (SinkState w, SinkState h) m)
=> VGATimings ps w h
-> (Index w -> Index h -> rgb -> m ())
-> (Bit, Bit, rgb)
-> m Bool
vgaSink VGATimings{..} paint (hsync0, vsync0, color) = do
(x, endLine) <- stateZoom _1 $ direction w horizRetrace hsync
(y, endFrame) <- stateZoomIf endLine _2 $ direction h vertRetrace vsync
for_ (liftA2 (,) x y) $ \(x, y) -> paint x y color
return $ endLine && endFrame
where
(horizRetrace, hsyncTarget) = vgaRetrace vgaHorizTiming
(vertRetrace, vsyncTarget) = vgaRetrace vgaVertTiming
vsync = vsync0 == vsyncTarget
hsync = hsync0 == hsyncTarget
w = snatToNum (SNat @w)
h = snatToNum (SNat @h)
direction vis retrace sync s = case s of
Retrace n -> ((Nothing, False), s')
where
n' = n + 1
s' = if n' == retrace then Visible 0 else Retrace n'
Visible i -> ((Just i, False), s')
where
s' = maybe (WaitSync sync) Visible $ succIdx i
WaitSync prevSync -> ((Nothing, end), s')
where
end = not prevSync && sync
s' = if end then Retrace 0 else WaitSync sync
initSink :: (SinkState w, SinkState h)
initSink = (WaitSync False, WaitSync False)
{-# INLINE stateZoom #-}
stateZoom :: (MonadState s0 m) => Lens' s0 s -> (s -> (a, s)) -> m a
stateZoom = stateZoomIf True
{-# INLINE stateZoomIf #-}
stateZoomIf :: (MonadState s0 m) => Bool -> Lens' s0 s -> (s -> (a, s)) -> m a
stateZoomIf update l f = state $ \s0 ->
let s = view l s0
(x, s') = f s
s0' = s0 & l .~ s'
in (x, if update then s0' else s0)