{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Nes.FlagRegister (FlagRegister (..), getFlag, setFlag, clearFlag, setFlag') where

import Data.Bits
import Data.Kind (Type)
import Nes.Memory

class FlagRegister a where
    type Flag a :: Type
    fromByte :: Byte -> a
    toByte :: a -> Byte
    flagToBitOffset :: Flag a -> Int

setFlag :: (FlagRegister a) => Flag a -> a -> a
setFlag :: forall a. FlagRegister a => Flag a -> a -> a
setFlag Flag a
flag = Flag a -> Bool -> a -> a
forall a. FlagRegister a => Flag a -> Bool -> a -> a
setFlag' Flag a
flag Bool
True

clearFlag :: (FlagRegister a) => Flag a -> a -> a
clearFlag :: forall a. FlagRegister a => Flag a -> a -> a
clearFlag Flag a
flag = Flag a -> Bool -> a -> a
forall a. FlagRegister a => Flag a -> Bool -> a -> a
setFlag' Flag a
flag Bool
False

setFlag' :: forall a. (FlagRegister a) => Flag a -> Bool -> a -> a
setFlag' :: forall a. FlagRegister a => Flag a -> Bool -> a -> a
setFlag' Flag a
flag Bool
bool a
r = let byte :: Byte
byte = a -> Byte
forall a. FlagRegister a => a -> Byte
toByte a
r in Byte -> a
forall a. FlagRegister a => Byte -> a
fromByte (Byte -> a) -> Byte -> a
forall a b. (a -> b) -> a -> b
$ (if Bool
bool then Byte -> Int -> Byte
forall a. Bits a => a -> Int -> a
setBit else Byte -> Int -> Byte
forall a. Bits a => a -> Int -> a
clearBit) Byte
byte (forall a. FlagRegister a => Flag a -> Int
flagToBitOffset @a Flag a
flag)

getFlag :: forall a. (FlagRegister a) => Flag a -> a -> Bool
getFlag :: forall a. FlagRegister a => Flag a -> a -> Bool
getFlag Flag a
f a
r = Byte -> Int -> Bool
forall a. Bits a => a -> Int -> Bool
testBit (a -> Byte
forall a. FlagRegister a => a -> Byte
toByte a
r) (forall a. FlagRegister a => Flag a -> Int
flagToBitOffset @a Flag a
f)