module Language.ArrayForth.Opcode where
import           Data.List                 (elemIndex)
import           Data.Word.Odd             (Word18)
import           Language.ArrayForth.Parse (ParseError (..))
type F18Word = Word18
data Opcode = Ret                
            | Exec               
            | Jmp                
            | Call               
            | Unext              
            | Next               
            | If                 
            | MinusIf            
            | FetchP             
            | FetchPlus          
            | FetchB             
            | Fetch              
            | StoreP             
            | StorePlus          
            | StoreB             
            | Store              
            | MultiplyStep       
            | Times2             
            | Div2               
            | Not                
            | Plus               
            | And                
            | Or                 
            | Drop               
            | Dup                
            | Pop                
            | Over               
            | ReadA              
            | Nop                
            | Push               
            | SetB               
            | SetA               
            deriving (Eq, Bounded, Enum)
names :: [String]
names = [";", "ex", "jump", "call", "unext", "next", "if", "-if", "@p", "@+", "@b", "@",
         "!p", "!+", "!b", "!", "+*", "2*", "2/", "-", "+", "and", "or", "drop", "dup",
         "pop", "over", "a", ".", "push", "b!", "a!"]
opcodes :: [Opcode]
opcodes = [minBound..maxBound]
instance Show Opcode where show op = names !! fromEnum op
readOpcode :: String -> Either ParseError Opcode
readOpcode token = case elemIndex token names of
  Just res -> Right $ toEnum res
  Nothing  -> Left  $ BadOpcode token
instance Read Opcode where readsPrec _ str = case readOpcode str of
                             Left err -> error $ show err
                             Right r  -> [(r, "")]
toOpcode :: F18Word -> Opcode
toOpcode = toEnum . fromIntegral
fromOpcode :: Opcode -> F18Word
fromOpcode = fromIntegral . fromEnum
isJump :: Opcode -> Bool
isJump = (`elem` [Jmp, Call, Next, If, MinusIf])
slot3 :: Opcode -> Bool
slot3 = (`elem` [Ret, MultiplyStep, Unext, Plus, FetchP, Dup, StoreP, Nop])
opcodeTime :: Opcode -> Double
opcodeTime op = if memoryOp op then 5 else 1.5
  where memoryOp = (`elem` [FetchP, FetchPlus, FetchB, Fetch, StoreP,
                            StorePlus, StoreB, Store])