1 The
\gls{mTask
}-
\glspl{Task
} are sent to the device in bytecode and are saved
2 in the memory of the device. To compile the
\gls{EDSL
} code to bytecode, a view
3 is added to the
\gls{mTask
}-system encapsulated in the type
\CI{ByteCode
}. As
4 shown in Listing~
\ref{lst:bcview
}, the
\CI{ByteCode
} view is a boxed
\gls{RWST
}
5 that writes bytecode instructions (
\CI{BC
}, Subsection~
\ref{sec:instruction
})
6 while carrying around a
\CI{BCState
}. The state is kept between compilations
7 and is unique to a device. The state contains fresh variable names and a
8 register of
\glspl{SDS
} that are used.
10 Types implementing the
\gls{mTask
} classes must have two free type variables.
11 Therefore the
\gls{RWST
} is wrapped with a constructor and two phantom type
12 variables are added. This means that the programmer has to unbox the
13 \CI{ByteCode
} object to be able to make use of the
\gls{RWST
} functionality
14 such as return values. Tailor made access functions are used to achieve this
15 with ease. The fresh variable stream in a compiler using a
\gls{RWST
} is often
16 put into the
\emph{Reader
} part of the monad. However, not all code is compiled
17 immediately and later on the fresh variable stream cannot contain variables
18 that were used before. Therefore this information is put in the state which is
19 kept between compilations.
21 Not all types are suitable for usage in bytecode compiled programs. Every value
22 used in the bytecode view must fit in the
\CI{BCValue
} type which restricts
23 the content. Most notably, the type must be bytecode encodable. A
\CI{BCValue
}
24 must be encodable and decodable without losing type or value information. At
25 the moment a simple encoding scheme is used that uses single byte prefixes to
26 detect the type of the value. The devices know these prefixes and can apply the
27 same detection if necessary. Note that
\CI{BCValue
} uses existentially
28 quantified type variables and therefore it is not possible to derive class
29 instances such as
\CI{iTasks
}. Tailor-made instances for these functions have
32 \begin{lstlisting
}[label=
{lst:bcview
},caption=
{Bytecode view
}]
33 :: ByteCode a p = BC (RWS ()
[BC
] BCState ())
34 :: BCValue = E.e: BCValue e & mTaskType, TC e
46 class toByteCode a :: a -> String
47 class fromByteCode a :: String -> a
48 class mTaskType a | toByteCode, fromByteCode, iTask, TC a
50 instance toByteCode Int, ... , UserLED, BCValue
51 instance fromByteCode Int, ... , UserLED, BCValue
53 instance arith ByteCode
55 instance serial ByteCode
58 \subsection{Instruction Set
}\label{sec:instruction
}
59 The instruction set is given in Listing~
\ref{bc:instr
}. The instruction set is
60 kept large, but the number of instructions stays under $
255$ to get as much
61 expressive power while keeping all instruction within one byte.
63 The interpreter running in the client is a stack machine. The virtual
64 instruction
\CI{BCLab
} is added to allow for an easy implementation of jumping.
65 However, this is not a real instruction and the labels are resolved to actual
66 program memory addresses in the final step of compilation to save instructions
67 and avoid label lookups at runtime.
69 \begin{lstlisting
}[label=
{bc:instr
},
%
70 caption=
{Bytecode instruction set
}]
72 | BCLab Int | BCPush BCValue | BCPop
74 | BCSdsStore BCShare | BCSdsFetch BCShare | BCSdsPublish BCShare
78 | BCAdd | BCSub | BCMul
83 | BCEq | BCNeq | BCLes | BCGre
85 //Conditionals and jumping
86 | BCJmp Int | BCJmpT Int | BCJmpF Int
90 | BCAnalogRead Pin | BCAnalogWrite Pin | BCDigitalRead Pin | BCDigitalWrite Pin
95 All single byte instructions are converted automatically using a generic
96 function which returns the index of the constructor. The index of the
97 constructor is the byte value for all instructions. Added to this single byte
98 value are the encoded parameters of the instruction. The last step of the
99 compilation is transforming the list of bytecode instructions to actual bytes.
101 \subsection{Helper functions
}
102 Since the
\CI{ByteCode
} type is just a boxed
\gls{RWST
}, access to the whole
103 range of
\gls{RWST
} functions is available. However, to use this, the type must
104 be unboxed. After application the type must be boxed again. To achieve this,
105 several helper functions have been created. They are given in
106 Listing~
\ref{lst:helpers
}. The
\CI{op
} and
\CI{op2
} functions is hand-crafted
107 to make operators that pop one or two values off the stack respectively. The
108 \CI{tell`
} function is a wrapper around the
\gls{RWST
} function
\CI{tell
} that
109 appends the argument to the
\emph{Writer
} value.
111 \begin{lstlisting
}[label=
{lst:helpers
},caption=
{Some helper functions
}]
112 op2 :: (ByteCode a p1) (ByteCode a p2) BC -> ByteCode b Expr
113 op2 (BC x) (BC y) bc = BC (x >>| y >>| tell
[bc
])
115 op :: (ByteCode a p) BC -> ByteCode b c
116 op (BC x) bc = BC (x >>| tell
[bc
])
118 tell` ::
[BC
] -> (ByteCode a p)
119 tell` x = BC (tell x)
121 unBC :: (ByteCode a p) -> RWS ()
[BC
] BCState ()
125 \subsection{Arithmetics \& Peripherals
}
126 Almost all of the code from the simple classes exclusively use helper
127 functions. Listing~
\ref{lst:arithview
} shows some implementations. The
128 \CI{boolExpr
} class and the classes for the peripherals are implemented using
131 \begin{lstlisting
}[label=
{lst:arithview
},caption=
{%
132 Bytecode view implementation for arithmetic and peripheral classes
}]
133 instance arith ByteCode where
134 lit x = tell`
[BCPush (BCValue x)
]
135 (+.) x y = op2 x y BCDiv
138 instance userLed ByteCode where
139 ledOn l = op l BCLedOn
140 ledOff l = op l BCLedOff
143 \subsection{Control Flow
}
144 Implementing the sequence operator is very straightforward in the bytecode
145 view. The function just sequences the two
\glspl{RWST
}. The
146 implementation for the
\emph{If
} statement speaks for itself in
147 Listing~
\ref{lst:controlflow
}. First, all the labels are gathered after which
148 they are placed in the correct order in the bytecode sequence. It can happen
149 that multiple labels appear consecutively in the code. This is not a problem
150 since the labels are resolved to real addresses later on anyway.
152 \begin{lstlisting
}[label=
{lst:controlflow
},
%
153 caption=
{Bytecode view for
\texttt{arith
} class
}]
154 freshlabel = get >>=
\st=:
{freshl
}->put
{st & freshl=freshl+
1} >>| pure freshl
156 instance If ByteCode Stmt Stmt Stmt where If b t e = BCIfStmt b t e
157 instance If ByteCode e Stmt Stmt where If b t e = BCIfStmt b t e
158 instance If ByteCode Stmt e Stmt where If b t e = BCIfStmt b t e
159 instance If ByteCode x y Stmt where If b t e = BCIfStmt b t e
160 instance IF ByteCode where
161 IF b t e = BCIfStmt b t e
162 (?) b t = BCIfStmt b t (tell`
[])
163 BCIfStmt (BC b) (BC t) (BC e) = BC (
164 freshlabel >>=
\else->freshlabel >>=
\endif->
165 b >>| tell
[BCJmpF else
] >>|
166 t >>| tell
[BCJmp endif, BCLab else
] >>|
167 e >>| tell
[BCLab endif
]
170 instance noOp ByteCode where
174 The semantics for the
\gls{mTask
}-
\glspl{Task
} bytecode view are different from
175 the semantics of the
\gls{C
} view.
\glspl{Task
} in the
\gls{C
} view can start
176 new
\glspl{Task
} or even start themselves to continue, while in the bytecode
177 view,
\glspl{Task
} run indefinitely, one-shot or on interrupt. To allow
178 interval and interrupt
\glspl{Task
} to terminate, a return instruction is
179 added. This class was not available in the original system and is thus added.
180 It just writes a single instruction so that the interpreter knows to stop
181 execution. Listing~
\ref{lst:return
} shows the classes and implementation for
182 the return expression.
184 \begin{lstlisting
}[label=
{lst:return
},
%
185 caption=
{Bytecode view for the return instruction
}]
189 instance retrn ByteCode where
190 retrn = tell`
[BCReturn
]
193 \subsection{Shared Data Sources \& Assignment
}
194 Fresh
\gls{SDS
} are generated using the state and constructing one involves
195 multiple steps. First, a fresh identifier is grabbed from the state. Then a
196 \CI{BCShare
} record is created with that identifier. A
\CI{BCSdsFetch
}
197 instruction is written and the body is generated to finally add the
\gls{SDS
}
198 to the actual state with the value obtained from the function. The exact
199 implementation is shown in Listing~
\ref{lst:shareview
}. The implementation for
200 the
\CI{namedsds
} class is exactly the same other than that it stores the given
201 name in the
\CI{BCShare
} structure as well.
203 \begin{lstlisting
}[label=
{lst:shareview
},
%
204 caption=
{Bytecode view for
\texttt{arith
}}]
205 freshshare = get >>=
\st=:
{freshs
}->put
{st & freshs=freshs+
1} >>| pure freshs
207 instance sds ByteCode where
208 sds f =
{main = BC (freshshare
209 >>=
\sdsi->pure
{BCShare|sdsname="",sdsi=sdsi,sdsval=BCValue
0}
210 >>=
\sds->pure (f (tell`
[BCSdsFetch sds
]))
211 >>= \(v In bdy)->modify (addSDS sds v)
212 >>| unBC (unMain bdy))
214 instance sdspub ByteCode where
215 pub (BC x) = BC (censor (\
[BCSdsFetch s
]->
[BCSdsPublish s
]) x)
217 addSDS sds v s =
{s & sdss=
[{sds & sdsval=BCValue v
}:s.sdss
]}
220 All assignable types compile to an
\gls{RWST
} which writes the specific fetch
221 instruction(s). For example, using an
\gls{SDS
} always results in
% chktex 36
222 an expression of the form
\CI{sds
\x=
4 In ...
}. The actual
\CI{x
} is the
223 \gls{RWST
} that always writes one
\CI{BCSdsFetch
} instruction with the
224 correctly embedded
\gls{SDS
}. Assigning to an analog pin will result in the
225 \gls{RWST
} containing the
\CI{BCAnalogRead
} instruction. When the operation on
226 the assignable is not a read operation from but an assign operation, the
227 instruction(s) will be rewritten accordingly. This results in a
%chktex 36
228 \CI{BCSdsStore
} or
\CI{BCAnalogWrite
} instruction respectively. The
229 implementation for this is given in Listing~
\ref{lst:assignmentview
}.
231 \begin{lstlisting
}[label=
{lst:assignmentview
},
%
232 caption=
{Bytecode view implementation for assignment.
}]
233 instance assign ByteCode where
234 (=.) (BC v) (BC e) = BC (e >>| censor makeStore v)
236 makeStore
[BCSdsFetch i
] =
[BCSdsStore i
]
237 makeStore
[BCDigitalRead i
] =
[BCDigitalWrite i
]
238 makeStore
[...
] =
[...
]
241 \subsection{Actual Compilation
}
242 All the previous functions are tied together with the
\CI{toMessages
} function.
243 This function compiles the bytecode and transforms the
\gls{Task
} to a message.
244 The
\glspl{SDS
} that were not already sent to the device are also added as
245 messages to be sent to the device. This functionality is shown in
246 Listing~
\ref{lst:compilation
}. The compilation process consists of two steps.
247 First, the
\gls{RWST
} is executed. Then, the
\emph{Jump
} statements that
248 jump to labels are transformed to jump to program memory addresses. The
249 translation of labels is possible in one sweep because fresh labels are reused.
250 Reusing labels would not give a speed improvement since the labels are removed
253 \begin{lstlisting
}[label=
{lst:compilation
},
%
254 caption=
{Actual compilation.
}]
255 bclength :: BC -> Int
256 bclength (BCPush s) =
1 + size (toByteCode s)
258 bclength x =
1 + consNum
{|*|
} x
260 computeGotos ::
[BC
] Int -> (
[BC
], Map Int Int)
261 computeGotos
[] _ = (
[], newMap)
262 computeGotos
[BCLab l:xs
] i = appSnd ('DM'.put l i) (computeGotos xs i)
263 computeGotos
[x:xs
] i = appFst (
\bc->
[x:bc
]) (computeGotos xs (i + bclength x))
265 toRealByteCode :: (ByteCode a b) BCState -> (String, BCState)
267 # (s, bc) = runBC x s
268 # (bc, gtmap) = computeGotos bc
1
269 = (concat (map (toString o toByteVal) (map (implGotos gtmap) bc)), s)
271 toMessages :: MTaskInterval (Main (ByteCode a b)) BCState -> (
[MTaskMSGSend
], BCState)
272 toMessages interval x oldstate
273 # (bc, newstate) = toRealByteCode (unMain x) oldstate
274 # newsdss = difference newstate.sdss oldstate.sdss
275 = (
[MTSds sdsi e\\
{sdsi,sdsval=e
}<-newsdss
] ++
[MTTask interval bc
], newstate)