module Nes.CPU.Instructions.Access (
    -- * Load register value
    lda,
    ldx,
    ldy,

    -- * Store register value
    sta,
    stx,
    sty,

    -- * Unofficial
    las,
) where

import Control.Monad
import Data.Bits
import Nes.CPU.Instructions.Addressing
import Nes.CPU.Instructions.After (setZeroAndNegativeFlags)
import Nes.CPU.Monad
import Nes.CPU.State
import Nes.Internal.MonadState
import Nes.Memory

-- | Load Register A
--
-- https://www.nesdev.org/obelisk-6502-guide/reference.html#LDA
lda :: AddressingMode -> CPU r ()
lda :: forall r. AddressingMode -> CPU r ()
lda = Register -> AddressingMode -> CPU r ()
forall r. Register -> AddressingMode -> CPU r ()
loadRegisterFromMemory Register
A

-- | Load Register X
--
-- https://www.nesdev.org/obelisk-6502-guide/reference.html#LDX
ldx :: AddressingMode -> CPU r ()
ldx :: forall r. AddressingMode -> CPU r ()
ldx = Register -> AddressingMode -> CPU r ()
forall r. Register -> AddressingMode -> CPU r ()
loadRegisterFromMemory Register
X

-- | Load Register Y
--
-- https://www.nesdev.org/obelisk-6502-guide/reference.html#LDY
ldy :: AddressingMode -> CPU r ()
ldy :: forall r. AddressingMode -> CPU r ()
ldy = Register -> AddressingMode -> CPU r ()
forall r. Register -> AddressingMode -> CPU r ()
loadRegisterFromMemory Register
Y

{-# INLINE loadRegisterFromMemory #-}
loadRegisterFromMemory :: Register -> AddressingMode -> CPU r ()
loadRegisterFromMemory :: forall r. Register -> AddressingMode -> CPU r ()
loadRegisterFromMemory Register
reg AddressingMode
mode = do
    Addr
argAddr <- AddressingMode -> CPU r Addr
forall r. AddressingMode -> CPU r Addr
getOperandAddr AddressingMode
mode
    Byte
param <- Addr -> () -> CPU r Byte
forall a (m :: * -> *). MemoryInterface a m => Addr -> a -> m Byte
readByte Addr
argAddr ()
    Register -> Lens' CPUState Byte
register Register
reg ((Byte -> Identity Byte) -> CPUState -> Identity CPUState)
-> Byte -> CPU r ()
forall s (m :: * -> *) a.
MonadState s m =>
ASetter' s a -> a -> m ()
.= Byte
param
    Byte -> CPU r ()
forall r. Byte -> CPU r ()
setZeroAndNegativeFlags Byte
param

-- | Store the value of the A register in memory
--
-- https://www.nesdev.org/obelisk-6502-guide/reference.html#STA
sta :: AddressingMode -> CPU r ()
sta :: forall r. AddressingMode -> CPU r ()
sta = Register -> AddressingMode -> CPU r ()
forall r. Register -> AddressingMode -> CPU r ()
storeRegisterInMemory Register
A

-- | Store the value of the X register in memory
--
-- https://www.nesdev.org/obelisk-6502-guide/reference.html#STX
stx :: AddressingMode -> CPU r ()
stx :: forall r. AddressingMode -> CPU r ()
stx = Register -> AddressingMode -> CPU r ()
forall r. Register -> AddressingMode -> CPU r ()
storeRegisterInMemory Register
X

-- | Store the value of the Y register in memory
--
-- https://www.nesdev.org/obelisk-6502-guide/reference.html#STY
sty :: AddressingMode -> CPU r ()
sty :: forall r. AddressingMode -> CPU r ()
sty = Register -> AddressingMode -> CPU r ()
forall r. Register -> AddressingMode -> CPU r ()
storeRegisterInMemory Register
Y

{-# INLINE storeRegisterInMemory #-}
storeRegisterInMemory :: Register -> AddressingMode -> CPU r ()
storeRegisterInMemory :: forall r. Register -> AddressingMode -> CPU r ()
storeRegisterInMemory Register
reg AddressingMode
mode = do
    (Addr
addr, Bool
crosses) <- AddressingMode -> CPU r (Addr, Bool)
forall r. AddressingMode -> CPU r (Addr, Bool)
getOperandAddr' AddressingMode
mode
    Byte
value <- Getting Byte CPUState Byte -> CPU r Byte
forall s (m :: * -> *) a. MonadState s m => Getting a s a -> m a
use (Getting Byte CPUState Byte -> CPU r Byte)
-> Getting Byte CPUState Byte -> CPU r Byte
forall a b. (a -> b) -> a -> b
$ Register -> Lens' CPUState Byte
register Register
reg
    -- https://www.nesdev.org/wiki/Cycle_counting
    -- assumes the worst case of page crossing and always spends 1 extra read cycle.
    let shouldDummyRead :: Bool
shouldDummyRead = (Bool
crosses Bool -> Bool -> Bool
&& (AddressingMode
mode AddressingMode -> AddressingMode -> Bool
forall a. Eq a => a -> a -> Bool
/= AddressingMode
AbsoluteX)) Bool -> Bool -> Bool
|| (Bool -> Bool
not Bool
crosses Bool -> Bool -> Bool
&& (AddressingMode
mode AddressingMode -> AddressingMode -> Bool
forall a. Eq a => a -> a -> Bool
== AddressingMode
AbsoluteX Bool -> Bool -> Bool
|| AddressingMode
mode AddressingMode -> AddressingMode -> Bool
forall a. Eq a => a -> a -> Bool
== AddressingMode
AbsoluteY Bool -> Bool -> Bool
|| AddressingMode
mode AddressingMode -> AddressingMode -> Bool
forall a. Eq a => a -> a -> Bool
== AddressingMode
IndirectY))
    Bool -> CPU r () -> CPU r ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
shouldDummyRead (CPU r () -> CPU r ()) -> CPU r () -> CPU r ()
forall a b. (a -> b) -> a -> b
$ CPU r Byte -> CPU r ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (CPU r Byte -> CPU r ()) -> CPU r Byte -> CPU r ()
forall a b. (a -> b) -> a -> b
$ Addr -> () -> CPU r Byte
forall a (m :: * -> *). MemoryInterface a m => Addr -> a -> m Byte
readByte Addr
addr () -- NOTE Not sure addr is correct
    Byte -> Addr -> () -> CPU r ()
forall a (m :: * -> *).
MemoryInterface a m =>
Byte -> Addr -> a -> m ()
writeByte Byte
value Addr
addr ()

las :: AddressingMode -> CPU r ()
las :: forall r. AddressingMode -> CPU r ()
las AddressingMode
mode = do
    Addr
addr <- AddressingMode -> CPU r Addr
forall r. AddressingMode -> CPU r Addr
getOperandAddr AddressingMode
mode
    Byte
byte <- Addr -> () -> CPU r Byte
forall a (m :: * -> *). MemoryInterface a m => Addr -> a -> m Byte
readByte Addr
addr ()
    Byte
s <- Getting Byte CPUState Byte -> CPU r Byte
forall s (m :: * -> *) a. MonadState s m => Getting a s a -> m a
use Getting Byte CPUState Byte
Lens' CPUState Byte
registerS
    let res :: Byte
res = Byte
byte Byte -> Byte -> Byte
forall a. Bits a => a -> a -> a
.&. Byte
s
    (Byte -> Identity Byte) -> CPUState -> Identity CPUState
Lens' CPUState Byte
registerA ((Byte -> Identity Byte) -> CPUState -> Identity CPUState)
-> Byte -> CPU r ()
forall s (m :: * -> *) a.
MonadState s m =>
ASetter' s a -> a -> m ()
.= Byte
res
    (Byte -> Identity Byte) -> CPUState -> Identity CPUState
Lens' CPUState Byte
registerX ((Byte -> Identity Byte) -> CPUState -> Identity CPUState)
-> Byte -> CPU r ()
forall s (m :: * -> *) a.
MonadState s m =>
ASetter' s a -> a -> m ()
.= Byte
res
    (Byte -> Identity Byte) -> CPUState -> Identity CPUState
Lens' CPUState Byte
registerS ((Byte -> Identity Byte) -> CPUState -> Identity CPUState)
-> Byte -> CPU r ()
forall s (m :: * -> *) a.
MonadState s m =>
ASetter' s a -> a -> m ()
.= Byte
res
    Byte -> CPU r ()
forall r. Byte -> CPU r ()
setZeroAndNegativeFlags Byte
res