module Language.ArrayForth.Opcode where import Data.List (elemIndex) import Data.Word.Odd (Word18) import Language.ArrayForth.Parse (ParseError (..)) -- | The 18-bit word type used by Greenarrays chips. type F18Word = Word18 -- | Each F18A instruction, ordered by opcode. data Opcode = Ret -- ; | Exec -- ex | Jmp -- name ; | Call -- name | Unext -- unext | Next -- next | If -- if | MinusIf -- -if | FetchP -- @p | FetchPlus -- @+ | FetchB -- @b | Fetch -- @ | StoreP -- !p | StorePlus -- !+ | StoreB -- !b | Store -- ! | MultiplyStep -- +* | Times2 -- 2* | Div2 -- 2/ | Not -- - | Plus -- + | And -- and | Or -- or | Drop -- drop | Dup -- dup | Pop -- pop | Over -- over | ReadA -- a | Nop -- . | Push -- push | SetB -- b! | SetA -- a! deriving (Eq, Bounded, Enum) -- | The names of the different instructions, ordered by opcode. 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!"] -- | All of the opcodes, in order. opcodes :: [Opcode] opcodes = [minBound..maxBound] instance Show Opcode where show op = names !! fromEnum op -- | Tries to read a given string as an opcode from the list of names. 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, "")] -- | Converts a word to an opcode. The word has to be < 32. toOpcode :: F18Word -> Opcode toOpcode = toEnum . fromIntegral -- | Converts an Opcode to its 18-bit word representation. fromOpcode :: Opcode -> F18Word fromOpcode = fromIntegral . fromEnum -- | Returns whether the given opcode is a jump instruction expecting -- an address. isJump :: Opcode -> Bool isJump = (`elem` [Jmp, Call, Next, If, MinusIf]) -- | Can the given opcode go in the last slot? slot3 :: Opcode -> Bool slot3 = (`elem` [Ret, MultiplyStep, Unext, Plus, FetchP, Dup, StoreP, Nop]) -- | Estimates how long a given opcode will take to execute. Normal -- opcodes take 1.5 nanoseconds where ones that access the memory take -- 5 nanoseconds. opcodeTime :: Opcode -> Double opcodeTime op = if memoryOp op then 5 else 1.5 where memoryOp = (`elem` [FetchP, FetchPlus, FetchB, Fetch, StoreP, StorePlus, StoreB, Store])