module Nes.APU.State.Triangle (
Triangle (..),
newTriangle,
getTriangleOutput,
tickTriangle,
tickTriangleLinearCounter,
) where
import Nes.APU.State.LengthCounter
data Triangle = MkT
{ Triangle -> Bool
controlFlag :: {-# UNPACK #-} !Bool
, Triangle -> Bool
reloadFlag :: {-# UNPACK #-} !Bool
, Triangle -> Int
reloadValue :: {-# UNPACK #-} !Int
, Triangle -> LengthCounter
lengthCounter :: !LengthCounter
, Triangle -> Int
linearCounter :: {-# UNPACK #-} !Int
, Triangle -> Int
period :: {-# UNPACK #-} !Int
, Triangle -> Int
timer :: {-# UNPACK #-} !Int
, Triangle -> Int
sequenceStep :: {-# UNPACK #-} !Int
}
newTriangle :: Triangle
newTriangle :: Triangle
newTriangle = MkT{Bool
Int
LengthCounter
controlFlag :: Bool
reloadFlag :: Bool
reloadValue :: Int
lengthCounter :: LengthCounter
linearCounter :: Int
period :: Int
timer :: Int
sequenceStep :: Int
controlFlag :: Bool
reloadFlag :: Bool
reloadValue :: Int
lengthCounter :: LengthCounter
period :: Int
linearCounter :: Int
sequenceStep :: Int
timer :: Int
..}
where
controlFlag :: Bool
controlFlag = Bool
False
reloadFlag :: Bool
reloadFlag = Bool
False
reloadValue :: Int
reloadValue = Int
0
lengthCounter :: LengthCounter
lengthCounter = LengthCounter
newLengthCounter
period :: Int
period = Int
0
linearCounter :: Int
linearCounter = Int
0
sequenceStep :: Int
sequenceStep = Int
0
timer :: Int
timer = Int
0
{-# INLINE getSequenceValue #-}
getSequenceValue :: Triangle -> Int
getSequenceValue :: Triangle -> Int
getSequenceValue Triangle
t = if Int
step Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
15 then Int
15 Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
step else Int
step Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
16
where
step :: Int
step = Triangle -> Int
sequenceStep Triangle
t
{-# INLINE getTriangleOutput #-}
getTriangleOutput :: Triangle -> Int
getTriangleOutput :: Triangle -> Int
getTriangleOutput Triangle
t = if LengthCounter -> Int
remainingLength (Triangle -> LengthCounter
lengthCounter Triangle
t) Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
/= Int
0 then Triangle -> Int
getSequenceValue Triangle
t else Int
0
instance HasLengthCounter Triangle where
getLengthCounter :: Triangle -> LengthCounter
getLengthCounter = Triangle -> LengthCounter
lengthCounter
setLengthCounter :: LengthCounter -> Triangle -> Triangle
setLengthCounter LengthCounter
lc Triangle
t = Triangle
t{lengthCounter = lc}
tickTriangle :: Triangle -> Triangle
tickTriangle :: Triangle -> Triangle
tickTriangle Triangle
t = Triangle
t{timer = newTimer, sequenceStep = newSequenceStep}
where
newTimer :: Int
newTimer = if Triangle -> Int
timer Triangle
t Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 then Triangle -> Int
period Triangle
t else Triangle -> Int
timer Triangle
t Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1
tickSequence :: Bool
tickSequence = Triangle -> Int
timer Triangle
t Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 Bool -> Bool -> Bool
&& Triangle -> Int
linearCounter Triangle
t Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
/= Int
0 Bool -> Bool -> Bool
&& LengthCounter -> Int
remainingLength (Triangle -> LengthCounter
lengthCounter Triangle
t) Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
/= Int
0
newSequenceStep :: Int
newSequenceStep = if Bool
tickSequence then (Triangle -> Int
sequenceStep Triangle
t Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` Int
32 else Triangle -> Int
sequenceStep Triangle
t
tickTriangleLinearCounter :: Triangle -> Triangle
tickTriangleLinearCounter :: Triangle -> Triangle
tickTriangleLinearCounter Triangle
t = Triangle
t2
where
t1 :: Triangle
t1 =
if Triangle -> Bool
reloadFlag Triangle
t
then Triangle
t{linearCounter = reloadValue t}
else Triangle
t{linearCounter = max 0 (linearCounter t - 1)}
t2 :: Triangle
t2 =
if Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ Triangle -> Bool
controlFlag Triangle
t1
then Triangle
t1{reloadFlag = False}
else Triangle
t1