-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathvm-IO-Array.hs
128 lines (112 loc) · 4.45 KB
/
vm-IO-Array.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE FlexibleContexts #-}
import Control.Applicative
import Control.Monad
import Control.Monad.Reader
import Data.Array.MArray
import Data.Array.IO
import Data.Binary.Get
import Data.Bits
import qualified Data.ByteString.Lazy as BS
import Data.IORef
import Data.Word
import System.Environment
data VM = VM { ram :: IOUArray Word16 Word16
, stack :: IORef [Word16]
, registers :: IOUArray Word16 Word16
, pc :: IORef Word16
, halt :: IORef Bool
, input :: IORef String
}
io = liftIO
readRam a = do ram <- asks ram; io $ readArray ram a
writeRam a v = do ram <- asks ram; io $ writeArray ram a v
pop = do
stack <- asks stack
vs <- io $ readIORef stack
case vs of
v:vs' -> io $ v <$ writeIORef stack vs'
[] -> fail "empty stack"
push v = do stack <- asks stack; io $ modifyIORef' stack (v:)
getRegister r = do registers <- asks registers; io $ readArray registers r
setRegister r v = do registers <- asks registers; io $ writeArray registers r v
getPc = do pc <- asks pc; io $ readIORef pc
jump v = v `seq` do pc <- asks pc; io $ writeIORef pc v
getHalt = do halt <- asks halt; io $ readIORef halt
doHalt = do halt <- asks halt; io $ writeIORef halt True
next = do
pc <- getPc
readRam pc <* jump (succ pc)
operand = do
w <- next
if w < 32768 then
return w
else if w < 32768 + 8 then
getRegister (w - 32768)
else
fail $ "invalid operand " ++ show w
register = do
w <- next
if w < 32768 || w >= 32768 + 8 then
fail $ "invalid register " ++ show w
else
return (w - 32768)
getInput = do
input <- asks input
l <- io getLine
case l of
':':command -> do
case words command of
["save"] -> return ()
["restore"] -> return ()
_ -> io $ putStrLn "invalid command"
getInput
_ -> io $ writeIORef input (l ++ "\n")
getCharacter = do
input <- asks input
empty <- io $ null <$> readIORef input
when empty getInput
c:cs <- io $ readIORef input
io $ writeIORef input cs
return c
data Opcode = Halt | Set | Push | Pop | Eq | Gt | Jmp | Jt | Jf | Add | Mult | Mod | And | Or | Not | Rmem | Wmem | Call | Ret | Out | In | Noop
deriving (Show, Enum, Bounded)
execute Halt = doHalt
execute Set = do a <- register; b <- operand; setRegister a b
execute Push = do a <- operand; push a
execute Pop = do a <- register; v <- pop; setRegister a v
execute Eq = do a <- register; b <- operand; c <- operand; setRegister a $ if b == c then 1 else 0
execute Gt = do a <- register; b <- operand; c <- operand; setRegister a $ if b > c then 1 else 0
execute Jmp = do a <- operand; jump a
execute Jt = do a <- operand; b <- operand; when (a > 0) $ jump b
execute Jf = do a <- operand; b <- operand; when (a == 0) $ jump b
execute Add = do a <- register; b <- operand; c <- operand; setRegister a $ (b + c) `clearBit` 15
execute Mult = do a <- register; b <- operand; c <- operand; setRegister a $ (b * c) `clearBit` 15
execute Mod = do a <- register; b <- operand; c <- operand; setRegister a $ b `mod` c
execute And = do a <- register; b <- operand; c <- operand; setRegister a $ b .&. c
execute Or = do a <- register; b <- operand; c <- operand; setRegister a $ b .|. c
execute Not = do a <- register; b <- operand; setRegister a $ complement b `clearBit` 15
execute Rmem = do a <- register; b <- operand; v <- readRam b; setRegister a v
execute Wmem = do a <- operand; b <- operand; writeRam a b
execute Call = do a <- operand; ret <- getPc; push ret; jump a
execute Ret = do ret <- pop; jump ret
execute Out = do a <- operand; io $ putChar $ toEnum $ fromIntegral a
execute In = do a <- register; c <- getCharacter; setRegister a $ fromIntegral $ fromEnum c
execute Noop = return ()
loop = do
op <- next
when (fromIntegral op > fromEnum (maxBound :: Opcode)) $ fail $ "invalid opcode " ++ show op
execute (toEnum (fromIntegral op))
halt <- getHalt
unless halt loop
main = do
[ramFile] <- getArgs
ramContents <- BS.readFile ramFile
ram <- newListArray (0, 32767) $ runGet (many getWord16le) ramContents
stack <- newIORef []
registers <- newArray (0, 7) 0
pc <- newIORef 0
halt <- newIORef False
input <- newIORef ""
runReaderT loop VM {..}