lazyboy-0.2.0.1: src/Lazyboy/IO.hs
{-|
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