module Nes.APU.State.LengthCounter (
LengthCounter (..),
newLengthCounter,
tickLengthCounter,
loadLengthCounter,
clearAndHaltLengthCounter,
enableLengthCounter,
disableLengthCounter,
HasLengthCounter (..),
withLengthCounter,
isSilencedByLengthCounter,
) where
import Data.List ((!?))
data LengthCounter = MkLC
{ LengthCounter -> Int
remainingLength :: {-# UNPACK #-} !Int
, LengthCounter -> Bool
isHalted :: {-# UNPACK #-} !Bool
, LengthCounter -> Bool
isEnabled :: {-# UNPACK #-} !Bool
}
newLengthCounter :: LengthCounter
newLengthCounter :: LengthCounter
newLengthCounter = Int -> Bool -> Bool -> LengthCounter
MkLC Int
0 Bool
False Bool
False
tickLengthCounter :: LengthCounter -> LengthCounter
tickLengthCounter :: LengthCounter -> LengthCounter
tickLengthCounter LengthCounter
lc =
if LengthCounter -> Int
remainingLength LengthCounter
lc Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0 Bool -> Bool -> Bool
&& Bool -> Bool
not (LengthCounter -> Bool
isHalted LengthCounter
lc)
then LengthCounter
lc{remainingLength = remainingLength lc - 1}
else LengthCounter
lc
{-# INLINE clearAndHaltLengthCounter #-}
clearAndHaltLengthCounter :: LengthCounter -> LengthCounter
clearAndHaltLengthCounter :: LengthCounter -> LengthCounter
clearAndHaltLengthCounter LengthCounter
lc = LengthCounter
lc{remainingLength = 0, isHalted = True}
loadLengthCounter :: Int -> LengthCounter -> LengthCounter
loadLengthCounter :: Int -> LengthCounter -> LengthCounter
loadLengthCounter Int
idx LengthCounter
lc
| Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ LengthCounter -> Bool
isEnabled LengthCounter
lc = LengthCounter
lc
| Bool
otherwise = case [Int]
lengthTable [Int] -> Int -> Maybe Int
forall a. [a] -> Int -> Maybe a
!? Int
idx of
Just Int
l -> LengthCounter
lc{remainingLength = l}
Maybe Int
Nothing -> LengthCounter
lc
disableLengthCounter :: LengthCounter -> LengthCounter
disableLengthCounter :: LengthCounter -> LengthCounter
disableLengthCounter LengthCounter
lc = LengthCounter
lc{isEnabled = False, remainingLength = 0}
enableLengthCounter :: LengthCounter -> LengthCounter
enableLengthCounter :: LengthCounter -> LengthCounter
enableLengthCounter LengthCounter
lc = LengthCounter
lc{isEnabled = True}
class HasLengthCounter a where
getLengthCounter :: a -> LengthCounter
setLengthCounter :: LengthCounter -> a -> a
{-# INLINE withLengthCounter #-}
withLengthCounter :: (HasLengthCounter a) => (LengthCounter -> LengthCounter) -> a -> a
withLengthCounter :: forall a.
HasLengthCounter a =>
(LengthCounter -> LengthCounter) -> a -> a
withLengthCounter LengthCounter -> LengthCounter
f a
a = LengthCounter -> a -> a
forall a. HasLengthCounter a => LengthCounter -> a -> a
setLengthCounter (LengthCounter -> LengthCounter
f (LengthCounter -> LengthCounter) -> LengthCounter -> LengthCounter
forall a b. (a -> b) -> a -> b
$ a -> LengthCounter
forall a. HasLengthCounter a => a -> LengthCounter
getLengthCounter a
a) a
a
{-# INLINE isSilencedByLengthCounter #-}
isSilencedByLengthCounter :: (HasLengthCounter a) => a -> Bool
isSilencedByLengthCounter :: forall a. HasLengthCounter a => a -> Bool
isSilencedByLengthCounter = (Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0) (Int -> Bool) -> (a -> Int) -> a -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LengthCounter -> Int
remainingLength (LengthCounter -> Int) -> (a -> LengthCounter) -> a -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> LengthCounter
forall a. HasLengthCounter a => a -> LengthCounter
getLengthCounter
lengthTable :: [Int]
lengthTable :: [Int]
lengthTable =
[ Int
10
, Int
254
, Int
20
, Int
2
, Int
40
, Int
4
, Int
80
, Int
6
, Int
160
, Int
8
, Int
60
, Int
10
, Int
14
, Int
12
, Int
26
, Int
14
, Int
12
, Int
16
, Int
24
, Int
18
, Int
48
, Int
20
, Int
96
, Int
22
, Int
192
, Int
24
, Int
72
, Int
26
, Int
16
, Int
28
, Int
32
, Int
30
]