module Nes.APU.BusInterface.DMC (write4010, write4011, write4012, write4013) where

import Data.Bits
import Nes.APU.Monad
import Nes.APU.State
import Nes.APU.State.DMC
import Nes.Internal.MonadState
import Nes.Memory

{-# INLINE write4010 #-}
write4010 :: Byte -> APU r ()
write4010 :: forall r. Byte -> APU r ()
write4010 Byte
byte = do
    let irq :: Bool
irq = Byte
byte Byte -> Int -> Bool
forall a. Bits a => a -> Int -> Bool
`testBit` Int
7
        loop :: Bool
loop = Byte
byte Byte -> Int -> Bool
forall a. Bits a => a -> Int -> Bool
`testBit` Int
6
        rateIdx :: Int
rateIdx = Byte -> Int
byteToInt (Byte -> Int) -> Byte -> Int
forall a b. (a -> b) -> a -> b
$ Byte
byte Byte -> Byte -> Byte
forall a. Bits a => a -> a -> a
.&. Byte
0b1111
        rate :: Int
rate = Int -> Int
getPeriodValue Int
rateIdx
    (DMC -> Identity DMC) -> APUState -> Identity APUState
Lens' APUState DMC
dmc ((DMC -> Identity DMC) -> APUState -> Identity APUState)
-> (DMC -> DMC) -> APU r ()
forall s (m :: * -> *) a.
MonadState s m =>
ASetter' s a -> (a -> a) -> m ()
%= \DMC
dmc' ->
        DMC
dmc'
            { interruptFlag = irq
            , loopFlag = loop
            , period = rate
            }

{-# INLINE write4011 #-}
write4011 :: Byte -> APU r ()
write4011 :: forall r. Byte -> APU r ()
write4011 Byte
byte = do
    let directLoad :: Int
directLoad = Byte -> Int
byteToInt (Byte -> Int) -> Byte -> Int
forall a b. (a -> b) -> a -> b
$ Byte
byte Byte -> Byte -> Byte
forall a. Bits a => a -> a -> a
.&. Byte
0b1111111
    -- TODO If the timer is outputting a clock at the same time, the output level is occasionally not changed properly.
    (DMC -> Identity DMC) -> APUState -> Identity APUState
Lens' APUState DMC
dmc ((DMC -> Identity DMC) -> APUState -> Identity APUState)
-> (DMC -> DMC) -> APU r ()
forall s (m :: * -> *) a.
MonadState s m =>
ASetter' s a -> (a -> a) -> m ()
%= \DMC
dmc' -> DMC
dmc'{outputLevel = directLoad}

{-# INLINE write4012 #-}
write4012 :: Byte -> APU r ()
write4012 :: forall r. Byte -> APU r ()
write4012 Byte
byte = do
    let sampleAddr :: Addr
sampleAddr = Addr
0xC000 Addr -> Addr -> Addr
forall a. Num a => a -> a -> a
+ (Byte -> Addr
byteToAddr Byte
byte Addr -> Addr -> Addr
forall a. Num a => a -> a -> a
* Addr
64)
    (DMC -> Identity DMC) -> APUState -> Identity APUState
Lens' APUState DMC
dmc ((DMC -> Identity DMC) -> APUState -> Identity APUState)
-> (DMC -> DMC) -> APU r ()
forall s (m :: * -> *) a.
MonadState s m =>
ASetter' s a -> (a -> a) -> m ()
%= \DMC
dmc' -> DMC
dmc'{sampleOgAddr = sampleAddr}

{-# INLINE write4013 #-}
write4013 :: Byte -> APU r ()
write4013 :: forall r. Byte -> APU r ()
write4013 Byte
byte = do
    let sampleLength :: Int
sampleLength = (Byte -> Int
byteToInt Byte
byte Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
16) Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1
    (DMC -> Identity DMC) -> APUState -> Identity APUState
Lens' APUState DMC
dmc ((DMC -> Identity DMC) -> APUState -> Identity APUState)
-> (DMC -> DMC) -> APU r ()
forall s (m :: * -> *) a.
MonadState s m =>
ASetter' s a -> (a -> a) -> m ()
%= \DMC
dmc' -> DMC
dmc'{sampleOgLength = sampleLength}