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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
|
#include <algorithm>
#include <iostream>
#include "spasm.hpp"
namespace SpasmImpl
{
PC_t Spasm::op_size = 21;
// Be sure the same number is on the previous and next lines
Operation Spasm::operations[] = {NULL,
&Spasm::push,
&Spasm::pop,
&Spasm::dup,
&Spasm::print,
&Spasm::read,
&Spasm::plus,
&Spasm::minus,
&Spasm::multiply,
&Spasm::divide,
&Spasm::modulus,
&Spasm::gotrue,
&Spasm::gofalse,
&Spasm::go,
&Spasm::call,
&Spasm::ret,
&Spasm::load,
&Spasm::store,
&Spasm::less,
&Spasm::lesseq};
Spasm::Spasm()
: pc(0), bc_size(0), bytecode(NULL), istr(&std::cin), ostr(&std::cout)
{
}
/*!
** Constructs new Spasm object
**
** \param _bc_size - size/length of the bytecode
** \param _bytecode - the bytecode of the program
** \param _istr - input stream for the machine
** \param _ostr - output stream for the machine
*/
Spasm::Spasm(PC_t _bc_size,
const byte* _bytecode,
std::istream& _istr,
std::ostream& _ostr)
: pc(0), bc_size(_bc_size), istr(&_istr), ostr(&_ostr)
{
bytecode = new byte[bc_size];
std::copy(_bytecode, _bytecode + bc_size, bytecode);
}
Spasm::Spasm(const Spasm& m)
: pc(m.pc),
bc_size(m.bc_size),
data_stack(m.data_stack),
return_stack(m.return_stack),
frame(m.frame),
istr(m.istr),
ostr(m.ostr)
{
copybc(m);
}
Spasm::~Spasm()
{
deleteobj();
}
Spasm& Spasm::operator=(const Spasm& m)
{
if (this != &m)
{
deleteobj();
data_stack = m.data_stack;
return_stack = m.return_stack;
frame = m.frame;
istr = m.istr;
ostr = m.ostr;
copybc(m);
}
return *this;
}
/*!
** Copies the bytecode of another machine m
**
** \param m - the machine from the bytecode is copied
*/
void Spasm::copybc(const Spasm& m)
{
bytecode = new byte[bc_size];
std::copy(m.bytecode, m.bytecode + bc_size, bytecode);
}
/*!
** Deletes the machine
*/
void Spasm::deleteobj()
{
if (bytecode)
delete[] bytecode;
}
/*!
** Returns the dstack, so that it can be examined after execution
**
** \return const reference to the data stack
*/
const Dstack& Spasm::get_dstack() const
{
return data_stack;
}
/*!
** Starts the machine. The machine stops if it reaches an invalid opcode
** or opcode 0 or the pc reaches beyond the end of the bytecode.
*/
void Spasm::run()
{
while (pc < bc_size)
{
if (bytecode[pc] > 0 && bytecode[pc] < op_size)
(this->*operations[bytecode[pc]])();
else
{
std::cerr << "opcode got to " << int(bytecode[pc]) << std::endl;
break;
}
++pc;
}
}
/*!
** Pushes the next data_t object on the data stack
*/
void Spasm::push()
{
++pc;
data_stack.push(*reinterpret_cast<data_t*>(bytecode + pc));
pc += sizeof(data_t) - 1;
}
/*!
** Pops a data_t object from the data stack.
*/
void Spasm::pop()
{
data_stack.pop();
}
/*!
** Dups the data_t object on top of the data stack.
*/
void Spasm::dup()
{
data_stack.push(data_stack.top());
}
/*!
** Reads a data_t object from the input stream and pushes it on the data
** stack.
*/
void Spasm::read()
{
data_t x;
*istr >> x;
data_stack.push(x);
}
/*!
** Pops a data_t object from the data stack and prints it on the output
** stream.
*/
void Spasm::print()
{
*ostr << data_stack.pop();
}
/*!
** Pops two data objects from the data stack and pushes their sum on the
** data stack.
*/
void Spasm::plus()
{
data_t x, y = data_stack.pop();
x = data_stack.pop();
data_stack.push(x + y);
}
/*!
** Pops two data objects from the data stack and pushes their difference
** on the data stack.
*/
void Spasm::minus()
{
data_t x, y = data_stack.pop();
x = data_stack.pop();
data_stack.push(x - y);
}
/*!
** Pops two data objects from the data stack and pushes their product
** on the data stack.
*/
void Spasm::multiply()
{
data_t x, y = data_stack.pop();
x = data_stack.pop();
data_stack.push(x * y);
}
/*!
** Pops two data objects from the data stack and pushes their division
** on the data stack.
*/
void Spasm::divide()
{
data_t x, y = data_stack.pop();
x = data_stack.pop();
data_stack.push(x / y);
}
/*!
** Pops two data objects from the data stack and pushes their modulus
** on the data stack.
*/
void Spasm::modulus()
{
data_t x, y = data_stack.pop();
x = data_stack.pop();
data_stack.push(x % y);
}
/*!
** Conditional execution.
** If the data_t object on top of the data stack evaluates to true
** execution continues from the place pointed by number in the next bytes
*/
void Spasm::gotrue()
{
++pc;
if (data_stack.pop())
pc = *(reinterpret_cast<PC_t*>(bytecode + pc)) - 1;
else
pc += sizeof(size_t) - 1;
}
/*!
** Conditional execution.
** If the data_t object on top of the data stack evaluates to false
** execution continues from the place pointed by number in the next bytes
*/
void Spasm::gofalse()
{
++pc;
if (!data_stack.pop())
pc = *(reinterpret_cast<PC_t*>(bytecode + pc)) - 1;
else
pc += sizeof(size_t) - 1;
}
/*!
** Unconditional execution.
** Execution continues from the opcode pointed by the next bytes
*/
void Spasm::go()
{
++pc;
pc = *(reinterpret_cast<PC_t*>(bytecode + pc)) - 1;
}
/*!
** Function call. A new frame with the specified size is created, the
** return address is saved in the return stack and the new pc is loaded.
*/
void Spasm::call()
{
size_t fs; // new frame size
return_stack.push(pc); // using the ++pc in run()
data_stack.pop(&fs, sizeof(fs));
data_stack.pop(&pc, sizeof(pc));
frame.new_frame(fs);
--pc; // the ++pc in run()
}
/*!
** Function return. The frame of the current function is destroyed and the
** saved return address is loaded in the pc
*/
void Spasm::ret()
{
pc = return_stack.top();
return_stack.pop();
frame.pop_frame();
}
/*!
** Local variable with index top of the data stack is pushed on top of the
** data stack.
*/
void Spasm::load()
{
size_t offset;
data_stack.pop(&offset, sizeof(offset));
data_stack.push(frame[offset]);
}
/*!
** Local variable is assigned value
*/
void Spasm::store()
{
data_t value = data_stack.pop();
size_t offset;
data_stack.pop(&offset, sizeof(offset));
frame[offset] = value;
}
/*!
** Compares the values on top of the stack. Pushes 1 if the value before
** the top of the stack is less than that on top of the stack.
*/
void Spasm::less()
{
data_t y = data_stack.pop();
data_t x = data_stack.pop();
data_stack.push(x < y);
}
/*!
** Compares the values on top of the stack. Pushes 1 if the value before
** the top of the stack is less than or equal to that on top of the stack.
*/
void Spasm::lesseq()
{
data_t y = data_stack.pop();
data_t x = data_stack.pop();
data_stack.push(x <= y);
}
} // namespace SpasmImpl
|