{-|
    Module      : Lazyboy.Types
    Description : Hardware type definitions for Lazyboy
    Copyright   : (c) Rose 2019
    License     : BSD3
    Maintainer  : rose@lain.org.uk
    Stability   : experimental
    Portability : POSIX

    This module defines datatypes for the various aspects of the target hardware
    including registers and instructions.
-}

module Lazyboy.Types where

import           Control.Monad                (replicateM)
import           Control.Monad.IO.Class       (liftIO)
import           Control.Monad.Trans.Class    (lift)
import           Control.Monad.Trans.RWS.Lazy
import           Data.Int
import           Data.Word

-- | A type alias that defines Lazyboy as a specialization of the
-- RWS monad transformer stack. Reader goes unused, Writer is utilized
-- for an output list of Instructions, and State is merely an integer
-- which counts labels, thus naming them.
type Lazyboy a = RWS () [Instruction] Integer a

-- | Executes an action and returns a list of Instructions.
execLazyboy :: Lazyboy a -> [Instruction]
execLazyboy m = snd $ execRWS m () 1

-- | A type which represents an address or label.
data Location = Address Word16 | Name Label
  deriving (Eq)

-- | A type representing Condition flags on the hardware.
data Condition = Zero | NonZero | Carry | NoCarry
  deriving (Read, Show, Eq)

-- | Named 8-bit registers.
data Register8 = A | B | C | D | E | H | L
  deriving (Read, Show, Eq)

-- | Named 16-bit registers.
data Register16 = BC | DE | HL | AF | SP | PC
  deriving (Read, Show, Eq)

-- | A type which represents a label, which may be local or global in scope.
data Label = Local Integer | Global Integer
  deriving (Eq)

-- | Type-level representations of instructions and primitive special forms.
data Instruction =
    LDrr Register8 Register8   -- ^ Load the value in one Register8 into another.
  | LDrn Register8 Word8       -- ^ Load the immediate Word8 into a Register8.
  | LDrHL Register8            -- ^ Load the Word8 stored at the address in HL into a Register8.
  | LDHLr Register8            -- ^ Load the Word8 stored in a Register8 into the address in HL.
  | LDHLn Word8                -- ^ Load the immediate Word8 into the address in HL.
  | LDArr Register16           -- ^ Load the value at the address contained in a Register16 into A.
  | LDrrA Register16           -- ^ Load A into the address contained in a Register16.
  | LDAnn Location             -- ^ Load the Word8 stored in the Location into A.
  | LDnnA Location             -- ^ Load the Word8 stored in A into the Location.
  | LDAIO Word8                -- ^ Read into A from IO port n (FF00 + Word8).
  | LDIOA Word8                -- ^ Store the Word8 in A into IO port n (FF00 + Word8).
  | LDAIOC                     -- ^ Read from IO port FF00+C into A.
  | LDIOCA                     -- ^ Store the Word8 in A into IO port FF00+C.
  | LDHLAI                     -- ^ Store value in register A into byte pointed by HL and post-increment HL.
  | LDAHLI                     -- ^ Store value in address in HL in A and post-increment HL.
  | LDHLAD                     -- ^ Store value in register A into byte pointed by HL and post-decrement HL.
  | LDAHLD                     -- ^ Store value in address in HL in A and post-decrement HL.
  | LDrrnn Register16 Location -- ^ Load a Location into a Register16.
  | LDSPHL                     -- ^ Set the stack pointer (SP) to the value in HL.
  | PUSH Register16            -- ^ Push Register16 onto the stack.
  | POP Register16             -- ^ Pop Register16 from the stack.

  -- Jump & Call instructions
  | JP Location               -- ^ Immediately and unconditionally jump to a Location.
  | JPHL                      -- ^ Immediately and unconditionally jump to the value contained in HL.
  | JPif Condition Location   -- ^ Conditionally jump to a Location.
  | CALL Location             -- ^ Call a Location.
  | CALLif Condition Location -- ^ Conditionally call a Location. 
  | RET                       -- ^ Return from a labelled block.
  | RETif Condition           -- ^ Conditionally return from a labelled block. 
  | RETi                      -- ^ Return and enable interrupts.
  | RST Word8                 -- ^ Call a restart vector.

  -- Arithmetic & Logical instructions
  | ADDAr Register8          -- ^ Add the value contained in a Register8 to A.
  | ADDAn Word8              -- ^ Add a Word8 to the value contained in A.
  | ADDHL                    -- ^ Add the value contained in the address stored in HL to A.
  | ADCAr Register8          -- ^ Add the value in a Register8 + the carry flag to A.
  | ADCAn Word8              -- ^ Add a Word8 + the carry flag to A.
  | ADCHL                    -- ^ Add the value pointed to by HL + the carry flag to A.
  | SUBAr Register8          -- ^ Subtract the value contained in a Register8 from A.
  | SUBAn Word8              -- ^ Subtract a Word8 from A.
  | SUBHL                    -- ^ Subtract from A the value contained at the address in HL.
  | SBCAr Register8          -- ^ Subtract from A the value contained in a Register8 + the carry flag.
  | SBCAn Word8              -- ^ Subtract from A a Word8 + the carry flag.
  | SBCAHL                   -- ^ Subtract from A the value contained in the address in HL + the carry flag.
  | ANDr Register8           -- ^ Assign to A the value contained in a Register8 & A itself.
  | ANDn Word8               -- ^ Assign to A a Word8 & A itself.
  | ANDHL                    -- ^ Assign to A itself & the value in the address in HL.
  | XORr Register8           -- ^ Assign to A the value contained in a register ^ A itself
  | XORn Word8               -- ^ Assign to A a Word8 ^ itself.
  | XORHL                    -- ^ Assign to A itself ^ the value in the address in HL.
  | ORr Register8            -- ^ Assign to A the value contained in a register | A itself.
  | ORn Word8                -- ^ Assign to A a Word8 | itself.
  | ORHL                     -- ^ Assign to A itself | the value in the address in HL
  | CPr Register8            -- ^ Subtract from A the value in a Register8 and set flags accordingly, but don't store the result.
  | CPn Word8                -- ^ Subtract from A a Word8 and set flags accordingly, but don't store the result.
  | CPHL                     -- ^ Subtract from A the value in the address in HL, set flags, but don't store the result.
  | INCr Register8           -- ^ Increment the value in a Register8.
  | INCHL                    -- ^ Increment the value at the address in HL.
  | DECr Register8           -- ^ Decrement the value in a Register8.
  | DECHL                    -- ^ Decrement the value at the address in HL.
  | DAA                      -- ^ Decimal-adjust register A.
  | CPL                      -- ^ Complement accumulator (A = ~A).
  | ADDHLrr Register16       -- ^ Add the value contained in a Register16 to HL.
  | INCrr Register16         -- ^ Increment the value in a Register16.
  | DECrr Register16         -- ^ Decrement the value in a Register16.
  | ADDSPn Int8              -- ^ Add an Int8 to the stack pointer.
  | LDHLSPn Int8             -- ^ Load into HL the stack pointer + an Int8.

  -- Single-bit instructions
  | BITnr Word8 Register8    -- ^ Test bit n in a Register8, set the zero flag if not set.
  | BITnHL Word8             -- ^ Test bit n in the Word8 pointed by HL, set the zero flag if not set.
  | SETnr Word8 Register8    -- ^ Set bit n in a Register8.
  | SETnHL Word8             -- ^ Set bit n in the Word8 pointed by HL.
  | RESnr Word8 Register8    -- ^ Unset bit n in Register8.
  | RESnHL Word8             -- ^ Unset bit n in the Word8 pointed by HL.

  -- Rotate & shift instructions
  | RLCA                     -- ^ Rotate accumulator left.
  | RLA                      -- ^ Rotate accumulator left through carry.
  | RRCA                     -- ^ Rotate accumulator right.
  | RRA                      -- ^ Rotate accumulator rit through carry.
  | RLC Register8            -- ^ Rotate Register8 left.
  | RLCHL                    -- ^ Rotate value contained at address in HL left.
  | RL Register8             -- ^ Rotate Register8 left through carry.
  | RLHL                     -- ^ Rotate value contained at address in HL left through carry.
  | RRC Register8            -- ^ Rotate Register8 right.
  | RRCHL                    -- ^ Rotate value contained at address in HL right.
  | RR Register8             -- ^ Rotate Register8 right through carry.
  | RRHL                     -- ^ Rotate value contained at address in HL right through carry.
  | SLA Register8            -- ^ Shift Register8 left arithmetic.
  | SLAHL                    -- ^ Shift left arithmetic (HL pointer).
  | SWAP Register8           -- ^ Exchange low and high nibbles in Register8.
  | SWAPHL                   -- ^ Exchange low and high nibbles in HL pointer.
  | SRA Register8            -- ^ Shift Register8 right arithmetic.
  | SRAHL                    -- ^ Shift right arithmetic in HL pointer.
  | SRL Register8            -- ^ Shift Register8 right logical.
  | SRLHL                    -- ^ Shift right logical in HL pointer.

  -- CPU control instructions
  | CCF                      -- ^ Complement carry flag.
  | SCF                      -- ^ Set carry flag.
  | NOP                      -- ^ No operation.
  | HALT                     -- ^ Halt until interrupt.
  | STOP                     -- ^ Standby mode.
  | DI                       -- ^ Disable interrupts.
  | EI                       -- ^ Enable interrupts.

  -- RGBASM-specific convenience stuff.
  -- these would need revamping if we were to start generating native machine code
  | LABEL Label               -- ^ Create a numbered label.
  | INCLUDE FilePath          -- ^ Include the file at FilePath.
  | BYTES [Word8]             -- ^ Define some bytes in the form of a Word8 list with a global label.

  deriving (Eq)