module Language.ArrayForth.State where
import Data.Functor ((<$>))
import Data.Vector.Unboxed (Vector, (//))
import qualified Data.Vector.Unboxed as V
import Text.Printf (printf)
import Language.ArrayForth.Channel
import Language.ArrayForth.NativeProgram
import Language.ArrayForth.Opcode (F18Word)
import Language.ArrayForth.Stack
data Memory = Memory { ram :: Vector Int
, rom :: Vector Int
, input :: Channel
, output :: Channel } deriving (Show, Eq)
emptyMem :: Memory
emptyMem = Memory { ram = V.replicate 64 0
, rom = V.replicate 64 0
, input = emptyChannel
, output = emptyChannel }
memSize :: Num a => a
memSize = 0x03F
data State =
State { a, b, p, r, s, t :: !F18Word
, i :: !(Maybe F18Word)
, dataStack, returnStack :: !Stack
, memory :: !Memory }
instance Show State where
show State {p, a, b, r, s, t, dataStack} =
printf "p:%s a:%s b:%s r:%s\n %s %s %s" p' a' b' r' t' s' (show dataStack)
where [p', a', b', r', s', t'] = map show [p, a, b, r, s, t]
startState :: State
startState = State 0 0 0 0 0 0 (Just 0) empty empty emptyMem
incrP :: State -> State
incrP state@State { p } = state { p = nextP }
where nextP | p < 2 * memSize = succ p `mod` (2 * memSize)
| p < 4 * memSize = (succ p `mod` (2 * memSize)) + 2 * memSize
| otherwise = p
next :: State -> Maybe Instrs
next State { memory, p } = fromBits <$> memory ! p
dpop :: State -> (State, F18Word)
dpop state@State {s, t, dataStack} =
let (ds', res) = pop dataStack in (state {t = s, s = res, dataStack = ds'}, t)
dpush :: State -> F18Word -> State
dpush state@State {s, t, dataStack} word =
state {t = word, s = t, dataStack = push dataStack s}
rpop :: State -> (State, F18Word)
rpop state@State {r, returnStack} =
let (rs', res) = pop returnStack in (state {r = res, returnStack = rs'}, r)
rpush :: State -> F18Word -> State
rpush state@State {r, returnStack} word =
state {r = word, returnStack = push returnStack r}
toMem :: (Integral a, Integral b) => a -> b
toMem = fromIntegral . (`mod` 64)
(!) :: Memory -> F18Word -> Maybe F18Word
Memory {..} ! i | i < 2 * memSize = Just . fromIntegral $ ram V.! toMem i
| i < 4 * memSize = Just . fromIntegral $ rom V.! toMem i
| otherwise = readPort i input
set :: State -> F18Word -> F18Word -> State
set state@State {memory = memory@Memory {..}} i value
| i < 2 * memSize = state { memory = updatedRam }
| i < 4 * memSize = error "Cannot set memory in the ROM!"
| otherwise = state { memory = updatedOutput }
where updatedRam = memory { ram = ram // [(toMem i, fromIntegral value)] }
updatedOutput = memory { output = writePort i value }
blocked :: State -> Bool
blocked State { memory = Memory { output } } = output /= emptyChannel
setProgram :: F18Word -> NativeProgram -> State -> State
setProgram start program state = state' { i = toBits <$> next state' }
where state' = loadMemory start (fromIntegral . toBits <$> program) state
loadMemory :: F18Word -> [F18Word] -> State -> State
loadMemory start values state@State {memory = memory@Memory {..}} =
state { memory = memory {
ram = ram // zip [toMem start..] (fromIntegral <$> values) } }
sendInput :: Port -> F18Word -> State -> State
sendInput port value state@(State { memory = memory@Memory {..} }) = updated
where updated = state {
memory = case port of
R -> memory { input = input { right = Just value } }
D -> memory { input = input { down = Just value } }
L -> memory { input = input { left = Just value } }
U -> memory { input = input { up = Just value } }
}