data Reg = Zero | At | V0 | V1
         | A0 | A1 | A2 | A3
         | T0 | T1 | T2 | T3 | T4 | T5 | T6 | T7
         | S0 | S1 | S2 | S3 | S4 | S5 | S6 | S7
         | T8 | T9
         | GP | SP | FP | RQ
         | PC
         deriving (Eq, Ord, Show, Enum, Bounded)

data Insn = Add Reg Reg Reg 
          | Sub Reg Reg Reg
          | J Word
          deriving (Eq, Ord, Show)
data PseudoInsn = Pseudo [Insn]
data Cell = W Word | I Insn deriving (Eq, Ord, Show)
data VM = VM (Map.Map Reg Cell) (Map.Map Word Cell) deriving (Eq, Show)

readReg r = gets $ \ (VM rs cs) -> fromMaybe err (Map.lookup r rs)
  where err = error $ "Invalid Register: " ++ show r
readMem a = gets $ \ (VM rs cs) -> fromMaybe err (Map.lookup a cs)
  where err = error $ "Invalid Address: " ++ show a
writeReg r x = modify $ \ (VM rs cs) -> VM (Map.insert r x rs) cs
writeMem a x = modify $ \ (VM rs cs) -> VM rs (Map.insert a x cs)

incr r = do
  W x <- readReg r
  writeReg r (W (x + 1))

fetch :: MonadState VM m => m Cell
fetch = do
  W pc <- readReg PC
  readMem pc

decode :: Cell -> Insn
decode (I x) = x
decode _ = error "Invalid Instruction"

exec :: MonadState VM m => m ()
exec = do
  op <- fetch
  case decode op of
    Add rs rt rd -> do
      W s <- readReg rs
      W t <- readReg rt
      writeReg rd (W (s + t))
      incr PC
      exec