1 module SynacorEngine where
5 -- import System.Environment
8 -- import Data.Binary.Get
9 -- import qualified Data.ByteString.Lazy as BL
10 import qualified Data.Map.Strict as M
11 import Data.Map.Strict ((!))
17 import Control.Monad.State.Strict
18 import Control.Monad.Reader
19 import Control.Monad.Writer
20 import Control.Monad.RWS.Strict
22 type Memory = M.Map Word16 Word16
26 data Machine = Machine { _memory :: Memory
28 , _registers :: Memory
33 deriving (Show, Eq, Ord)
36 type ProgrammedMachine = RWS [Word16] [Word16] Machine
38 data ExecutionState = Runnable | Blocked | Terminated deriving (Ord, Eq, Show)
40 -- returns (returnValue, finalMachine, outputs)
42 runMachine :: [Word16] -> Machine -> (ExecutionState, Machine, [Word16])
43 runMachine inputs machine = runRWS (runAll (10 ^ 6)) inputs machine
46 makeMachine :: Memory -> Machine
47 makeMachine mem = Machine
50 , _registers = M.fromList [ (r, 0) | r <- [0..7] ]
58 -- opcode <- getLocation cip
59 -- arg1 <- getValue (cip + 1)
60 -- arg2 <- getValue (cip + 2)
61 -- arg3 <- getValue (cip + 3)
62 -- mem <- gets _memory
63 -- regs <- gets _registers
64 -- let raw1 = mem ! (cip + 1)
65 -- let raw2 = mem ! (cip + 2)
66 -- let raw3 = mem ! (cip + 3)
70 -- , show (showHex (cip * 2) "")
72 -- , (show (showHex cip ""))
76 -- , show (showHex arg1 "")
77 -- , show (showHex arg2 "")
78 -- , show (showHex arg3 "")
80 -- , show (showHex raw1 "")
81 -- , show (showHex raw2 "")
82 -- , show (showHex raw3 "")
83 -- , show (M.elems regs)
88 runAll :: Int -> ProgrammedMachine ExecutionState
90 | executionLimit == 0 = return Terminated
93 opcode <- getLocation cip
95 -- opcode' <- traceMachine opcode
96 -- exState <- runStep opcode'
97 exState <- runStep opcode
99 Terminated -> return Terminated
100 Blocked -> return Blocked
101 _ -> runAll (executionLimit - 1)
104 runStep :: Word16 -> ProgrammedMachine ExecutionState
105 -- runStep n | trace (show n) False = undefined
106 runStep 0 = return Terminated
109 regR <- getLocation (cip + 1)
111 value <- getValue (cip + 2)
113 modify (\m -> m & registers . ix reg .~ value)
117 value <- getValue (cip + 1)
119 modify (\m -> m & stack %~ (value :) )
123 tgt <- getLocation (cip + 1)
124 val <- gets (\m -> head $ m ^. stack)
125 modify (\m -> m & stack %~ tail )
131 tgt <- getLocation (cip + 1)
132 b <- getValue (cip + 2)
133 c <- getValue (cip + 3)
134 putValue tgt (if b == c then 1 else 0)
135 modify (\m -> m & ip %~ (+ 4))
139 tgt <- getLocation (cip + 1)
140 b <- getValue (cip + 2)
141 c <- getValue (cip + 3)
142 putValue tgt (if b > c then 1 else 0)
147 tgt <- getLocation (cip + 1)
148 modify (\m -> m & ip .~ tgt)
152 a <- getValue (cip + 1)
153 tgt <- getLocation (cip + 2)
155 then modify (\m -> m & ip .~ tgt)
160 a <- getValue (cip + 1)
161 tgt <- getLocation (cip + 2)
163 then modify (\m -> m & ip .~ tgt)
169 a <- getLocation (cip + 1)
170 b <- getValue (cip + 2)
171 c <- getValue (cip + 3)
177 a <- getLocation (cip + 1)
178 b <- getValue (cip + 2)
179 c <- getValue (cip + 3)
185 a <- getLocation (cip + 1)
186 b <- getValue (cip + 2)
187 c <- getValue (cip + 3)
188 putValue a (b `mod` c)
193 a <- getLocation (cip + 1)
194 b <- getValue (cip + 2)
195 c <- getValue (cip + 3)
201 a <- getLocation (cip + 1)
202 b <- getValue (cip + 2)
203 c <- getValue (cip + 3)
209 a <- getLocation (cip + 1)
210 b <- getValue (cip + 2)
211 putValue a (complement b)
217 a <- getLocation (cip + 1)
218 b <- getValue (cip + 2)
225 a <- getValue (cip + 1)
226 b <- getValue (cip + 2)
233 a <- getValue (cip + 1)
234 modify (\m -> m & stack %~ ((cip + 2) :)
239 do val <- gets (\m -> head $ m ^. stack)
240 modify (\m -> m & stack %~ tail
246 v <- getValue (cip + 1)
252 do iIndex <- gets _inputIndex
255 tgt <- getLocation (cip + 1)
256 if (iIndex + 1) > (length inputs)
258 else do let char = (inputs!!iIndex)
260 modify (\m -> m & inputIndex %~ (+ 1))
272 traceMachine :: ProgrammedMachine ()
274 isTracing <- gets _tracing
277 (l, _) <- dissembleInstruction cip
278 regs <- gets _registers
279 let regVals = intercalate "; " $ fmap show $ M.elems regs
281 let stackVals = intercalate "; " $ fmap show $ take 10 stk
282 tell $ fmap (fromIntegral . ord) (">> " ++ l ++ " : r> " ++ regVals ++ " : s> " ++ stackVals ++ "<<")
284 runDissemble :: Word16 -> Int -> Machine -> [String]
285 runDissemble startAt num machine =fst $ evalRWS (dissemble startAt num) [] machine
288 dissemble :: Word16 -> Int -> ProgrammedMachine [String]
289 dissemble startAt num = go startAt num []
290 where go _ 0 ls = return $ reverse ls
292 do (line, step) <- dissembleInstruction here
293 go (here + step) (n - 1) (line : ls)
295 dissembleInstruction :: Word16 -> ProgrammedMachine (String, Word16)
296 dissembleInstruction cip =
297 do -- cip <- gets _ip
298 opcode <- getLocation cip
300 let a = mem ! (cip + 1)
301 let b = mem ! (cip + 2)
302 let c = mem ! (cip + 3)
306 va <- getValue (cip + 1)
307 vb <- getValue (cip + 2)
308 vc <- getValue (cip + 3)
315 1 -> ["set", sa, sb, "*", sva, svb]
316 2 -> ["push", sa, "*", sva]
317 3 -> ["pop", sa, "*", sva]
318 4 -> ["eq", sa, sb, sc, "*", sva, svb, svc]
319 5 -> ["gt", sa, sb, sc, "*", sva, svb, svc]
320 6 -> ["jmp", sa, "*", sva]
321 7 -> ["jt", sa, sb, "*", sva, svb]
322 8 -> ["jf", sa, sb, "*", sva, svb]
323 9 -> ["add", sa, sb, sc, "*", sva, svb, svc]
324 10 -> ["mul", sa, sb, sc, "*", sva, svb, svc]
325 11 -> ["mod", sa, sb, sc, "*", sva, svb, svc]
326 12 -> ["and", sa, sb, sc, "*", sva, svb, svc]
327 13 -> ["or", sa, sb, sc, "*", sva, svb, svc]
328 14 -> ["not", sa, sb, "*", sva, svb]
329 15 -> ["rmem", sa, sb, "*", sva, svb]
330 16 -> ["wmem", sa, sb, "*", sva, svb]
331 17 -> ["call", sa, "*", sva]
333 19 -> ["out", sa, "*", sva]
334 20 -> ["in", sa, "*", sva]
336 _ -> ["illegal", sa, sb, sc, "*", sva, svb, svc]
338 if | opcode `elem` [0, 18, 21] -> 1
339 | opcode `elem` [2, 3, 6, 17, 19, 20] -> 2
340 | opcode `elem` [1, 7, 8, 14, 15, 16] -> 3
341 | opcode `elem` [4, 5, 9, 10, 11, 12, 13] -> 4
343 return ( ((show cip) ++ ": " ++ (intercalate " " traceText))
347 getValue :: Word16 -> ProgrammedMachine Word16
349 do mem <- gets _memory
350 regs <- gets _registers
354 else return (regs ! (val .&. 7))
359 -- do mem <- gets _memory
360 -- return $ mem ! loc
362 -- do regs <- gets _registers
363 -- return $ regs ! (loc `shiftR` 15)
365 getLocation :: Word16 -> ProgrammedMachine Word16
367 do mem <- gets _memory
370 putValue :: Word16 -> Word16 -> ProgrammedMachine ()
372 | loc < (2 ^ 15) = modify (\m -> m & memory . ix loc .~ v)
373 | otherwise = modify (\m -> m & registers . ix (loc .&. 7) .~ v)
374 where v = value `mod` (2 ^ 15)
376 advanceIP :: Word16 -> ProgrammedMachine ()
377 advanceIP delta = modify (\m -> m & ip %~ (+ delta))