fix failures to byteswap images (in imageManager and imagepreview)
[openc2e.git] / caosVM.cpp
blob237e2e036bf33937c2b34852b05349f233f6c587
1 /*
2 * caosVM.cpp
3 * openc2e
5 * Created by Alyssa Milburn on Tue May 25 2004.
6 * Copyright (c) 2004 Alyssa Milburn. All rights reserved.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
20 #include "caosVM.h"
21 #include "openc2e.h"
22 #include "World.h"
23 #include "bytecode.h"
24 #include "caosScript.h"
25 #include <iostream>
27 #include <boost/format.hpp>
29 using std::cout;
30 using std::cerr;
31 using std::endl;
33 void dumpStack(caosVM *vm) {
34 std::cerr << "\tvalueStack: ";
35 int i, c = 0;
36 for (i = vm->valueStack.size() - 1; i >= 0 && c++ < 5; i--)
37 std::cerr << vm->valueStack[i].dump() << " | ";
38 if (i >= 0)
39 std::cerr << "...";
40 else
41 std::cerr << "END";
42 std::cerr << std::endl;
45 caosVM::caosVM(const AgentRef &o)
46 : vm(this)
48 owner = o;
49 currentscript.reset();
50 cip = nip = 0;
51 blocking = NULL;
52 inputstream = 0; outputstream = 0;
53 resetCore();
54 trace = false;
57 caosVM::~caosVM() {
58 resetCore(); // delete blocking, close streams
61 bool caosVM::isBlocking() {
62 if (!blocking) return false;
63 bool bl = (*blocking)();
64 if (!bl) {
65 delete blocking;
66 blocking = NULL;
68 return bl;
71 void caosVM::startBlocking(blockCond *whileWhat) {
72 if (!owner)
73 // TODO: should this just fail to block?
74 throw creaturesException("trying to block in a non-blockable script");
75 inst = false;
76 if (blocking)
77 throw creaturesException("trying to block with a block condition in-place");
78 blocking = whileWhat;
81 inline void caosVM::safeJMP(int dest) {
82 // std::cerr << "jmp from " << cip << " to " << dest << "(old nip = " << nip << ") in script of length " << currentscript->scriptLength() << std::endl;
83 if (dest < 0) {
84 std::cerr << currentscript->dump();
85 throw caosException(boost::str(boost::format("Internal error: Unrelocated jump at %08x") % cip));
87 if (dest >= currentscript->scriptLength()) {
88 std::cerr << currentscript->dump();
89 throw creaturesException(boost::str(boost::format("Internal error: Jump out of bounds at %08x") % cip));
91 nip = dest;
94 inline void caosVM::invoke_cmd(script *s, bool is_saver, int opidx) {
95 const cmdinfo *ci = s->dialect->getcmd(opidx);
96 // We subtract two here to account for a) the missing return, and b)
97 // consuming the new value.
98 int stackdelta = ci->stackdelta - (is_saver ? 2 : 0);
99 unsigned int stackstart = valueStack.size();
100 assert(result.isNull());
101 #ifndef VCPP_BROKENNESS
102 if (is_saver)
103 (this->*(ci->savehandler))();
104 else
105 (this->*(ci->handler))();
106 #else
107 if (is_saver)
108 dispatchCAOS(this, ci->savehandler_idx);
109 else
110 dispatchCAOS(this, ci->handler_idx);
111 #endif
112 if (!is_saver && !result.isNull()) {
113 valueStack.push_back(result);
114 result.reset();
115 } else {
116 assert(result.isNull());
118 if (stackdelta < INT_MAX - 1) {
119 if ((int)stackstart + stackdelta != (int)valueStack.size()) {
120 dumpStack(this);
121 throw caosException(boost::str(boost::format("Internal error: Stack imbalance detected: expected to be %d after start of %d, but stack size is now %d") % stackdelta % (int)stackstart % (int)valueStack.size()));
126 inline void caosVM::runOpCore(script *s, caosOp op) {
127 switch (op.opcode) {
128 case CAOS_NOP: break;
129 case CAOS_DIE:
131 int idx = op.argument;
132 std::string err = "aborted";
133 caosVar constVal = s->getConstant(idx);
134 if (constVal.hasString())
135 err = constVal.getString();
136 throw creaturesException(err);
138 case CAOS_STOP:
140 stop();
141 break;
143 case CAOS_SAVE_CMD:
145 invoke_cmd(s, true, op.argument);
146 break;
148 case CAOS_CMD:
150 invoke_cmd(s, false, op.argument);
151 break;
153 case CAOS_YIELD:
155 #ifndef NDEBUG
156 // This condition can arise as a result of bad save data,
157 // so an assert is not appropriate... or is it?
159 // In any case, it is mostly harmless but should never occur,
160 // as it indicates a bug in the CAOS compiler.
161 caos_assert(auxStack.size() == 0);
162 #endif
163 if (!inst)
164 timeslice -= op.argument;
165 break;
167 case CAOS_COND:
169 VM_PARAM_VALUE(v2);
170 VM_PARAM_VALUE(v1);
171 VM_PARAM_INTEGER(condaccum);
172 assert(!v1.isEmpty());
173 assert(!v2.isEmpty());
174 int condition = op.argument;
175 if (condition & CAND) condition -= CAND;
176 if (condition & COR) condition -= COR;
177 int result = 0;
178 if (condition == CEQ)
179 result = (v1 == v2);
180 if (condition == CNE)
181 result = !(v1 == v2);
183 if (condition == CLT)
184 result = (v1 < v2);
185 if (condition == CGE)
186 result = !(v1 < v2);
187 if (condition == CGT)
188 result = (v1 > v2);
189 if (condition == CLE)
190 result = !(v1 > v2);
192 if (condition == CBT) {
193 caos_assert(v1.hasInt() && v2.hasInt());
194 result = (v2.getInt() == v1.getInt() & v2.getInt());
196 if (condition == CBF) {
197 caos_assert(v1.hasInt() && v2.hasInt());
198 result = (0 == v1.getInt() & v2.getInt());
200 if (op.argument & CAND)
201 result = (condaccum && result);
202 else
203 result = (condaccum || result);
204 valueStack.push_back(caosVar(result));
205 break;
207 case CAOS_CONST:
209 valueStack.push_back(vmStackItem(s->getConstant(op.argument)));
210 break;
212 case CAOS_CONSTINT:
214 valueStack.push_back(vmStackItem(caosVar(op.argument)));
215 break;
217 case CAOS_BYTESTR:
219 valueStack.push_back(vmStackItem(s->getBytestr(op.argument)));
220 break;
222 case CAOS_PUSH_AUX:
224 caos_assert(op.argument >= 0);
225 caos_assert(op.argument < (int)valueStack.size());
226 auxStack.push_back(valueStack[valueStack.size() - op.argument - 1]);
227 break;
229 case CAOS_RESTORE_AUX:
231 caos_assert(op.argument >= 0);
232 caos_assert(op.argument <= (int)auxStack.size());
233 for (int i = 0; i < op.argument; i++) {
234 valueStack.push_back(auxStack.back());
235 auxStack.pop_back();
237 break;
239 case CAOS_STACK_ROT:
241 caos_assert(op.argument >= 0);
242 caos_assert(op.argument < (int)valueStack.size());
243 for (int i = 0; i < op.argument; i++) {
244 int top = valueStack.size() - 1;
245 std::swap(valueStack[top - i], valueStack[top - i - 1]);
247 break;
249 case CAOS_CJMP:
251 VM_PARAM_VALUE(v);
252 if (v.getInt() != 0)
253 safeJMP(op.argument);
254 break;
256 case CAOS_JMP:
258 safeJMP(op.argument);
259 break;
261 case CAOS_DECJNZ:
263 VM_PARAM_INTEGER(counter);
264 counter--;
265 if (counter) {
266 safeJMP(op.argument);
267 valueStack.push_back(caosVar(counter));
269 break;
271 case CAOS_GSUB:
273 callStack.push_back(callStackItem());
274 callStack.back().nip = nip;
275 callStack.back().valueStack.swap(valueStack);
276 safeJMP(op.argument);
277 break;
279 case CAOS_ENUMPOP:
281 VM_PARAM_VALUE(v);
282 if (v.isEmpty())
283 break;
284 if (!v.hasAgent()) {
285 dumpStack(this);
286 throw caosException(std::string("Stack item type mismatch: ") + v.dump());
288 targ = v.getAgentRef();
289 safeJMP(op.argument);
290 break;
292 default:
293 throw creaturesException(boost::str(boost::format("Illegal opcode %d") % (int)op.opcode));
297 inline void caosVM::runOp() {
298 cip = nip;
299 nip++;
301 runops++;
302 if (runops > 1000000) throw creaturesException("script exceeded 1m ops");
304 shared_ptr<script> scr = currentscript;
305 caosOp op = currentscript->getOp(cip);
307 try {
308 if (trace) {
309 std::cerr
310 << boost::str(boost::format("optrace(%s): INST=%d TS=%d %p @%08d top=%s depth=%d ") % scr->filename.c_str() % (int)inst % (int)timeslice % (void *)this % cip % (valueStack.empty() ? std::string("(empty)") : valueStack.back().dump()) % valueStack.size())
311 << dumpOp(currentscript->dialect, op) << std::endl;
312 if (trace >= 2) {
313 dumpStack(this);
316 runOpCore(scr.get(), op);
317 } catch (caosException &e) {
318 e.trace(currentscript, op.traceindex);
319 stop();
320 throw;
321 } catch (creaturesException &e) {
322 stop();
323 throw;
328 void caosVM::stop() {
329 lock = false;
330 currentscript.reset();
333 void caosVM::runEntirely(shared_ptr<script> s) {
334 // caller is responsible for resetting/setting *all* state!
335 cip = nip = runops = 0;
336 currentscript = s;
338 while (true) {
339 runOp();
340 if (!currentscript) break;
341 if (blocking) {
342 delete blocking;
343 blocking = NULL;
344 throw creaturesException("blocking in an installation script");
349 bool caosVM::fireScript(shared_ptr<script> s, bool nointerrupt, Agent *frm) {
350 assert(owner);
351 assert(s);
352 if (lock) return false; // can't interrupt scripts which called LOCK
353 if (currentscript && nointerrupt) return false; // don't interrupt scripts with a timer script
355 resetScriptState();
356 currentscript = s;
357 targ = owner;
358 from.setAgent(frm);
359 timeslice = 1;
360 return true;
363 void caosVM::resetScriptState() {
364 stop();
365 resetCore();
368 void caosVM::resetCore() {
369 if (blocking)
370 delete blocking;
371 blocking = NULL;
372 result.reset();
374 valueStack.clear();
375 auxStack.clear();
376 callStack.clear();
378 inst = lock = 0;
379 timeslice = 0;
381 c_FILE_ICLO(); // make sure input stream is freed
382 inputstream = 0; // .. possibly not strictly necessary, when all is bugfree
383 c_FILE_OCLO(); // make sure output stream is freed
384 outputstream = 0;
386 _it_ = NULL;
387 from.setAgent(NULL);
388 setTarg(owner);
389 part = 0;
391 _p_[0].reset(); _p_[0].setInt(0); _p_[1].reset(); _p_[1].setInt(0);
392 for (unsigned int i = 0; i < 100; i++) { var[i].reset(); var[i].setInt(0); }
394 camera.reset();
396 trace = 0;
397 cip = nip = runops = 0;
400 void caosVM::tick() {
401 stop_loop = false;
402 runops = 0;
403 while (currentscript && !stop_loop && (timeslice > 0 || inst)) {
404 if (isBlocking()) return;
405 runOp();
409 void caosVM::dummy_cmd() {
410 // no-op
412 /* vim: set noet: */