{-| Module : Lazyboy.IO Description : IO library for Lazyboy Copyright : (c) Rose 2019 License : BSD3 Maintainer : rose@lain.org.uk Stability : experimental Portability : POSIX This module defines IO and primitive graphics operations for Lazyboy. -} {-# LANGUAGE BinaryLiterals #-} {-# LANGUAGE RecordWildCards #-} module Lazyboy.IO where import Control.Monad.Trans.RWS.Lazy import Data.Bits import Data.Word import Lazyboy.Constants import Lazyboy.Control import Lazyboy.Types -- | A typeclass for packing types into Word8. class Bitfield a where pack :: a -> Word8 -- | A type representing the monochrome shades available on the hardware. data Color = White | Light | Dark | Black deriving (Eq, Ord) instance Bitfield Color where pack White = 0b00 pack Light = 0b01 pack Dark = 0b10 pack Black = 0b11 -- | A type representing the LCD screen control state. data LCDControl = LCDControl { lcdDisplayEnable :: Bool , lcdWindowTileMap :: Bool , lcdEnableWindowDisplay :: Bool , lcdWindowSelect :: Bool , lcdTileMapSelect :: Bool , lcdObjSize :: Bool , lcdEnableObjects :: Bool , lcdBackgroundEnable :: Bool } -- | The default LCDControl state - all flags set to False (0). -- In effect, this turns the screen off. defaultLCDControl :: LCDControl defaultLCDControl = LCDControl False False False False False False False False instance Bitfield LCDControl where pack lcds = zeroBits .|. lcdDE .|. lcdWTM .|. lcdEWD .|. lcdWS .|. lcdTMS .|. lcdOS .|. lcdEO .|. lcdBE where lcdDE = if lcdDisplayEnable lcds then 0b10000000 else 0 lcdWTM = if lcdWindowTileMap lcds then 0b01000000 else 0 lcdEWD = if lcdEnableWindowDisplay lcds then 0b00100000 else 0 lcdWS = if lcdWindowSelect lcds then 0b00010000 else 0 lcdTMS = if lcdTileMapSelect lcds then 0b00001000 else 0 lcdOS = if lcdObjSize lcds then 0b00000100 else 0 lcdEO = if lcdEnableObjects lcds then 0b00000010 else 0 lcdBE = if lcdBackgroundEnable lcds then 0b00000001 else 0 -- | A convenience function which executes setLCDControl with the defaultLCDControl state. -- This turns the screen off. disableLCD :: Lazyboy () disableLCD = setLCDControl defaultLCDControl -- | Sets the LCD control state to a given value. setLCDControl :: LCDControl -> Lazyboy () setLCDControl lcd = write (Address lcdc) $ pack lcd -- | A type representing the background palette. data BackgroundPalette = BackgroundPalette { bgpColor3 :: Color , bgpColor2 :: Color , bgpColor1 :: Color , bgpColor0 :: Color } -- | The default monochrome background palette. defaultPalette :: BackgroundPalette defaultPalette = BackgroundPalette Black Dark Light White instance Bitfield BackgroundPalette where pack BackgroundPalette {..} = zeroBits .|. zero .|. one .|. two .|. three where zero = pack bgpColor0 one = pack bgpColor1 `shiftL` 2 two = pack bgpColor2 `shiftL` 4 three = pack bgpColor3 `shiftL` 6 -- | Sets the background palette to a given palette. setBackgroundPalette :: BackgroundPalette -> Lazyboy () setBackgroundPalette pal = write (Address bgp) $ pack pal -- | Writes a Word8 to a Register8. byte :: Register8 -> Word8 -> Lazyboy () byte reg val = tell [LDrn reg val] -- | Loads a Word8 into a Location. write :: Location -> Word8 -> Lazyboy () write addr val = tell [LDrrnn HL addr, LDHLn val] -- | Copy a region of memory (limit 255 bytes) to a destination. memcpy :: Location -> Location -> Word8 -> Lazyboy () memcpy src dest len = do -- load the destination into DE, source into HL and length into B tell [LDrrnn HL src, LDrrnn DE dest, LDrn B len] withLocalLabel $ \label -> do tell [LDAHLI] -- load a byte from [HL] into A and increment tell [LDrrA DE, INCrr DE, DECr B, JPif NonZero (Name label)] -- | Sets a region of memory to a Word8 value (limit 255 bytes). memset :: Location -> Word8 -> Word8 -> Lazyboy () memset dest len value = do -- load the destination into HL, length into B and value into A tell [LDrrnn HL dest, LDrn B len, LDrn A value] withLocalLabel $ \label -> do tell [LDHLAI] -- load A into [HL] and increment tell [DECr B, JPif NonZero (Name label)] -- | Executes an action when vertical blank occurs. onVblank :: Lazyboy () -> Lazyboy () onVblank block = do withLocalLabel $ \label -> do tell [LDAnn $ Address ly, CPn 145] tell [JPif NonZero $ Name label] block