Intel.hs 7.52 KB
{-# LANGUAGE UndecidableInstances, TypeSynonymInstances, FlexibleInstances,
      GeneralizedNewtypeDeriving #-}

-- Intel Machine Instructions 386 32bit

-- we use only
--   32Bit Registers, signed 32Bit values

module Intel where

import Data.Char -- toLower
import Data.Int
import Data.List -- elemIndex
import Text.PrettyPrint

import GenSym
import Util  -- class Pretty

class Register a where
  eax :: a
  ebx :: a
  ecx :: a
  edx :: a
  esi :: a
  edi :: a
  ebp :: a
  esp :: a

instance Register Int where
  eax = 0  -- accumulator for MUL and DIV
  ebx = 1
  ecx = 2
  edx = 3  -- holds upper half of MUL/DIV result
  esi = 4
  edi = 5
  ebp = 6  -- frame pointer
  esp = 7  -- stack pointer

newtype Reg32 = Reg32 Int
    deriving (Eq, Ord)

regNames =  ["eax", "ebx", "ecx", "edx", "esi", "edi", "ebp", "esp"]

instance Show Reg32 where
  show (Reg32 r) = regNames !! (fromIntegral r)

instance Read Reg32 where
  readsPrec n s = case (elemIndex name regNames) of
                    Just r -> [(Reg32 $ fromIntegral r,rest)]
                    Nothing -> []
                  where (name, rest) = splitAt 3 s

instance Register Reg32 where
  eax = Reg32 eax
  ebx = Reg32 ebx
  ecx = Reg32 ecx
  edx = Reg32 edx
  esi = Reg32 esi
  edi = Reg32 edi
  ebp = Reg32 ebp
  esp = Reg32 esp


-- Intel specific implementation

callerSave :: Register a => [a]
callerSave = [ eax , ecx , edx ]

calleeSave :: Register a => [a]
calleeSave = [ ebx , esi , edi ]



-- temporaries

data Reg = Fixed { unFixed :: Reg32 } -- fixed register, cannot be changed
         | Flex  { unFlex  :: Temp  } -- a register to be assigned later
           deriving (Eq, Ord)

isReg32 :: Reg -> Bool
isReg32 (Fixed _) = True
isReg32 (Flex _)  = False

isTemp :: Reg -> Bool
isTemp (Fixed _) = False
isTemp (Flex _)  = True

instance Show Reg where
  show (Fixed r) = show r
  show (Flex t)  = show t

instance Temporary Reg where
  temp = Flex

instance Register Reg where
  eax = Fixed eax
  ebx = Fixed ebx
  ecx = Fixed ecx
  edx = Fixed edx
  esi = Fixed esi
  edi = Fixed edi
  ebp = Fixed ebp
  esp = Fixed esp

data Scale = S1 | S2 | S4 | S8 -- possible scaling values for effective addressing
           deriving (Eq)

toScale :: (Eq a, Num a) => a -> Maybe Scale
toScale 1 = Just S1
toScale 2 = Just S2
toScale 4 = Just S4
toScale 8 = Just S8
toScale _ = Nothing

-- parts of e.a.

type EA = (Reg, Maybe Scale, Maybe Reg, Int32)
type BiLinEA = (Reg, Maybe Scale, Maybe Reg)
type AffineEA = (Maybe Reg, Maybe Scale, Int32)
type LinEA = (Reg, Maybe Scale)

data Dest = Reg Reg     -- a register

            -- full effective address
          | Mem Reg                    -- index
                (Maybe Scale)          -- * scale
                (Maybe Reg)            -- + base
                Int32                  -- + Displacement (8, 16, or) 32 bit
            deriving (Eq)

instance Temporary Dest where
  temp = Reg . temp

instance Register Dest where
  eax = Reg eax
  ebx = Reg ebx
  ecx = Reg ecx
  edx = Reg edx
  esi = Reg esi
  edi = Reg edi
  ebp = Reg ebp
  esp = Reg esp

data Src = Imm Int32      -- immediate value
         | Dest Dest      -- or register or eff.addr.
           deriving (Eq)

type Src' = Dest   -- src which cannot be immed.

instance Temporary Src where
  temp = Dest . temp

instance Register Src where
  eax = Dest eax
  ebx = Dest ebx
  ecx = Dest ecx
  edx = Dest edx
  esi = Dest esi
  edi = Dest edi
  ebp = Dest ebp
  esp = Dest esp

data Cond = E | NE | L | LE | G | GE
               deriving (Eq, Show)

-- destructive instruction with dest. and source
data DS = MOV      -- dest := src, at least one of them a register
        | ADD      -- dest += src, flags carry
        | SUB      -- dest -= src
        | IMUL2    -- dest *= src
        | SHL      -- dest <<= imm
        | SHR      -- dest >>= imm
        | SAL      -- dest <<= imm (synonym to shl)
        | SAR      -- dest >>= imm (arithmetic right shift)
        | AND      -- dest &= src
        | OR       -- dest |= src
        | XOR      -- dest xor= src
               deriving (Eq, Show)

isShift :: DS -> Bool
isShift i = i `elem` [SHL,SHR,SAL,SAR]

-- destructive instruction with dest only
data D  = POP      -- pop off stack into dest, adding 4 to ESP
        | NEG      -- dest = -dest
        | NOT      -- dest := !dest
        | INC      -- dest++
        | DEC      -- dest--
               deriving (Eq, Show)

data Instr
  = DS DS Dest Src -- dest ?= src, not two mems
  | D  D  Dest     -- modifies dest
  | LEA Reg EA     -- load effective address into register
  | CMP Dest Src   -- not two mems, non-destructive
  | PUSH Src       -- push src onto stack, subtracting 4 from ESP
  | IMUL Src'      -- (edx, eax) := eax * src
  | IDIV Src'      -- eax := (edx, eax) div src,  edx := (edx,eax) mod src
  | CDQ            -- convert between signed 32bit and signed 64bit
--  | CQD            -- Convert Quadword Doubleword
  | JMP Label      -- jump, resolved by the assembler
  | J Cond Label   -- conditional jump
  | CALL Label     -- function call
  | RET            -- return from function call
  | ENTER Int32    -- set up local variables
  | LEAVE          -- esp := ebp, pop ebp
  | NOP            -- do nothing
  | LABEL Label

instance Pretty Reg32 where
  ppr = text . ('%':) . map toLower . show

instance Pretty Reg where
  ppr (Fixed r) = ppr r
  ppr (Flex  t) = ppr t

instance Pretty Scale where
  ppr S1 = text "1"
  ppr S2 = text "2"
  ppr S4 = text "4"
  ppr S8 = text "8"

instance Pretty a => Pretty (Maybe a) where
  ppr Nothing  = empty
  ppr (Just a) = ppr a

ppr_displacement n
  | n == 0 = empty
  | n >  0 = text "+" <> text (show n)
  | n <  0 = text "-" <> text (show (-n))

sepBy :: Doc -> Doc -> Doc -> Doc
sepBy sep d1 d2 | isEmpty d2 = d1
                | isEmpty d1 = d2
                | otherwise  = d1 <> sep <> d2

instance Pretty EA where
  ppr (r, ms, mr, n) =
    (sepBy (text "+") (sepBy (text "*") (ppr r) (ppr ms)) (ppr mr))
      <> ppr_displacement n

instance Pretty Dest where
  ppr (Reg r)         = ppr r
  ppr (Mem r ms mr n) = text "DWORD PTR [" <> ppr (r,ms,mr,n) <> text "]"

instance Pretty Src where
  ppr (Dest d) = ppr d
  ppr (Imm i)  = text $ show i

instance Pretty Cond where
  ppr = text . map toLower . show

instance Pretty DS where
  ppr IMUL2 = text "imul"
  ppr ds    = text . map toLower . show $ ds

instance Pretty D where
  ppr = text . map toLower . show

instance Pretty Instr where
  ppr (DS c d s) = nest 8 $ ppr c $$ (nest 8 $ ppr d <> comma <+> ppr s)
  ppr (D c  d)   = nest 8 $ ppr c $$ (nest 8 $ ppr d)
  ppr (LEA r ea) = nest 8 $ text "lea"  $$ (nest 8 $ ppr r <> comma <+> (text "DWORD PTR [" <> ppr ea <> text "]"))
  ppr (CMP d s)  = nest 8 $ text "cmp"  $$ (nest 8 $ ppr d <> comma <+> ppr s)
  ppr (PUSH s)   = nest 8 $ text "push" $$ (nest 8 $ ppr s)
  ppr (IMUL s)   = nest 8 $ text "imul" $$ (nest 8 $ ppr s)
  ppr (IDIV s)   = nest 8 $ text "idiv" $$ (nest 8 $ ppr s)
  ppr (CDQ)      = nest 8 $ text "cdq"
--  ppr (CQD)      = nest 8 $ text "cqd"
  ppr (JMP l)    = nest 8 $ text "jmp"  $$ (nest 8 $ ppr l)
  ppr (J c l)    = nest 8 $ text "j" <> ppr c $$ (nest 8 $ ppr l)
  ppr (CALL l)   = nest 8 $ text "call" $$ (nest 8 $ ppr l)
  ppr (RET)      = nest 8 $ text "ret"
  ppr (ENTER i)  = nest 8 $ text "enter" $$ (nest 8 $ text (show i) <> comma <+> text "0")
  ppr (LEAVE)    = nest 8 $ text "leave"
  ppr (NOP)      = nest 8 $ text "nop"
  ppr (LABEL l)  = ppr l <> text ":"

instance Pretty [Instr] where
    ppr [] = empty
    ppr (s:ss) = (ppr s) $$ ppr ss


comment :: Doc -> Doc
comment d = text "/*" <+> d <+> text "*/"


instance Show Instr where
  show = render . ppr