added cleanup callback to room scrips (remove code patching, etc)
[awish.git] / src / vm.c
blobd82c35b9c627a955a6689ca34ec5d61cf410ab40
1 /*
2 * This program is free software: you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation, either version 3 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 #include <stdarg.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
20 #include "vm.h"
23 //#define VM_DEBUG
26 #ifndef VM_FATAL_DEFINED
27 static __attribute__((__noreturn__)) __attribute__((format(printf, 3, 4))) void vmfatal (int tid, int pc, const char *fmt, ...) {
28 va_list ap;
30 fprintf(stderr, "VM FATAL (thread #%d, pc=%04x): ", tid, (unsigned int)pc);
31 va_start(ap, fmt);
32 vfprintf(stderr, fmt, ap);
33 va_end(ap);
34 fprintf(stderr, "\n");
35 exit(1);
37 #endif
40 const char *vmOpNames[] = {
41 "add",
42 "sub",
43 "mul",
44 "div",
45 "mod",
46 "bor",
47 "xor",
48 "and",
50 "jeq",
51 "jne",
52 "jlt",
53 "jle",
54 "jgt",
55 "jge",
57 "jmp",
59 "bsr",
61 "brk",
62 "end",
63 "new",
65 "set",
66 "get",
68 "psh",
69 "pop",
70 "swp",
71 "pck",
72 "rol",
73 "dpt",
75 "tid",
76 "kil",
77 "sus",
78 "res",
79 "sta",
81 "rxc",
82 "wxc",
84 "ret",
86 "rst",
88 "mgf",
89 "mgb",
90 "msf",
91 "msb",
93 NULL
97 VMRSTCB vmRSTCB = NULL;
98 VMMapGetCB vmMapGetCB = NULL;
99 VMMapSetCB vmMapSetCB = NULL;
102 typedef struct {
103 int pc;
104 int suspended;
105 int sp;
106 int *stack; // internal
107 int *tvars; // internal
108 } VMThread;
111 static VMThread vmThreads[VM_MAX_THREADS];
112 static int vmExecuted[VM_MAX_THREADS];
113 static int vmLastThreadId = 0;
115 unsigned char vmCode[65536];
116 int vmCodeSize = 0;
117 int vmGVars[VM_VARS_SIZE];
120 static void fixLastThreadId (void) {
121 for (; vmLastThreadId > 0; --vmLastThreadId) if (vmThreads[vmLastThreadId].stack) break;
125 static int vmFindFreeThread (void) {
126 for (int f = 0; f < VM_MAX_THREADS; ++f) if (vmThreads[f].stack == NULL) return f;
127 return -1;
131 static int vmInitThread (VMThread *trd) {
132 trd->pc = 0;
133 trd->sp = 0;
134 trd->suspended = 0;
135 trd->stack = calloc(VM_STACK_SIZE, sizeof(int));
136 if (trd->stack == NULL) return -1;
137 trd->tvars = calloc(VM_VARS_SIZE, sizeof(int));
138 if (trd->tvars == NULL) { free(trd->stack); return -1; }
139 return 0;
143 static void vmFreeThread (VMThread *trd) {
144 if (trd->stack) free(trd->stack);
145 if (trd->tvars) free(trd->tvars);
146 trd->stack = NULL;
147 trd->tvars = NULL;
148 trd->pc = 0;
149 trd->sp = 0;
150 trd->suspended = 0;
154 int vmInitialize (void) {
155 memset(vmThreads, 0, sizeof(vmThreads));
156 // setup main thread
157 vmLastThreadId = 0;
158 if (vmInitThread(vmThreads)) return -1; // alas
159 return 0;
163 void vmDeinitialize (void) {
164 for (int f = 0; f < VM_MAX_THREADS; ++f) {
165 if (vmThreads[f].stack) free(vmThreads[f].stack);
166 if (vmThreads[f].tvars) free(vmThreads[f].tvars);
168 memset(vmThreads, 0, sizeof(vmThreads));
169 vmLastThreadId = 0;
173 static inline int vmGetByte (int tid, int opc, VMThread *trd) {
174 if (trd->pc < 0 || trd->pc >= vmCodeSize) vmfatal(tid, opc, "out of code");
175 return vmCode[trd->pc++];
179 #define STACK_WANT(n) do { if (trd->sp < (n)) vmfatal(tid, opc, "stack underflow"); } while (0)
180 #define STACK_FSPC(n) do { if (trd->sp+(n) > VM_STACK_SIZE) vmfatal(tid, opc, "stack overflow"); } while (0)
182 #define STACK_TOP() (trd->stack[trd->sp-1])
183 #define STACK_POP() (trd->stack[--(trd->sp)])
184 #define STACK_PUSH(n) trd->stack[(trd->sp)++] = (n)
187 #define MATH(op) do { \
188 switch (argc) { \
189 case 0: \
190 STACK_WANT(2); \
191 trd->stack[trd->sp-2] = (trd->stack[trd->sp-2]) op (trd->stack[trd->sp-1]); \
192 --(trd->sp); \
193 break; \
194 case 1: \
195 STACK_WANT(1); \
196 trd->stack[trd->sp-1] = (trd->stack[trd->sp-1]) op (argv[0]); \
197 break; \
198 case 2: if (argp[0] != NULL) *(argp[0]) = (argv[0]) op (argv[1]); break; \
199 case 3: if (argp[2] != NULL) *(argp[2]) = (argv[0]) op (argv[1]); break; \
201 } while(0)
204 #define CHECK_DIV_ZERO \
205 switch (argc) { \
206 case 0: \
207 if (trd->sp > 0 && trd->stack[trd->sp-1] == 0) { fprintf(stderr, "VM FATAL: division by zero!\n"); exit(1); } \
208 break; \
209 case 1: \
210 if (argv[0] == 0) { fprintf(stderr, "VM FATAL: division by zero!\n"); exit(1); } \
211 break; \
212 default: \
213 if (argv[1] == 0) { fprintf(stderr, "VM FATAL: division by zero!\n"); exit(1); } \
214 break; \
218 #define JXX(op) do { \
219 switch (argc) { \
220 case 0: \
221 STACK_WANT(3); \
222 argv[0] = STACK_POP(); \
223 case 1: \
224 STACK_WANT(2); \
225 argv[2] = STACK_POP(); \
226 argv[1] = STACK_POP(); \
227 break; \
228 case 2: \
229 STACK_WANT(1); \
230 argv[2] = argv[1]; \
231 argv[1] = STACK_POP(); \
232 break; \
234 if (argv[1] op argv[2]) trd->pc = argv[0]; \
235 } while (0)
238 static inline int isBranch (int opcode) {
239 return
240 opcode == VM_JMP ||
241 opcode == VM_BSR ||
242 opcode == VM_JEQ ||
243 opcode == VM_JNE ||
244 opcode == VM_JLT ||
245 opcode == VM_JLE ||
246 opcode == VM_JGT ||
247 opcode == VM_JGE;
251 // <0: BRK; >0: END
252 int vmExecuteOne (int tid) {
253 VMThread *trd = &vmThreads[tid];
254 int opcode;
255 int argc;
256 int argv[3]; // argument values
257 int *argp[3]; // pointer to vars for each arg
258 int opc = trd->pc;
260 // decode instruction
261 opcode = vmGetByte(tid, opc, trd);
262 argc = (opcode>>6)&0x03;
263 opcode &= 0x3f;
264 argv[0] = argv[1] = argv[2] = 0;
265 argp[0] = argp[1] = argp[2] = NULL;
266 // read operands
267 if (tid > vmLastThreadId) vmLastThreadId = tid;
268 for (int f = 0; f < argc; ++f) {
269 int vn = vmGetByte(tid, opc, trd);
271 if (vn == 255) {
272 // immediate
273 argv[f] = vmGetByte(tid, opc, trd);
274 argv[f] |= vmGetByte(tid, opc, trd)<<8;
275 if (argv[f] >= 32768 && (f != 0 || !isBranch(opcode))) argv[f] -= 65536;
276 } else {
277 if (vn == 127) {
278 // stack var; <0: from stack top
279 argv[f] = vmGetByte(tid, opc, trd);
280 argv[f] |= (vmGetByte(tid, opc, trd)<<8);
281 if (argv[f] >= 32768) argv[f] -= 65536;
282 if (argv[f] < 0) argv[f] += trd->sp;
283 if (argv[f] < 0 || argv[f] >= VM_STACK_SIZE) vmfatal(tid, opc, "invalid stack offset");
284 argp[f] = trd->stack+argv[f];
285 } else {
286 // variable
287 argp[f] = vn&0x80 ? vmGVars : trd->tvars;
288 argp[f] += vn&0x7f;
290 argv[f] = *(argp[f]);
294 #ifdef VM_DEBUG
295 fprintf(stderr, "%04x: ", (unsigned int)opc);
296 if (opcode < sizeof(vmOpNames)/sizeof(char *)-1) fprintf(stderr, "%s", vmOpNames[opcode]); else fprintf(stderr, "BAD");
297 for (int f = 0; f < argc; ++f) {
298 fputc(' ', stderr);
299 if (argp[f]) {
300 unsigned int ofs;
302 fputc('[', stderr);
303 if (argp[f] >= trd->tvars && argp[f] < trd->tvars+VM_VARS_SIZE) {
304 ofs = (unsigned int)(argp[f]-trd->tvars);
305 } else if (argp[f] >= trd->stack && argp[f] < trd->stack+VM_VARS_SIZE) {
306 ofs = (unsigned int)(argp[f]-trd->stack);
307 fputc('.', stderr);
308 } else if (argp[f] >= vmGVars && argp[f] < vmGVars+VM_VARS_SIZE) {
309 ofs = (unsigned int)(argp[f]-vmGVars);
310 fputc('@', stderr);
312 fprintf(stderr, "%u]", ofs);
314 if (f == 0 && isBranch(opcode)) {
315 fprintf(stderr, "(0x%04x)", argv[f]);
316 } else {
317 fprintf(stderr, "(%d)", argv[f]);
320 fputc('\n', stderr);
321 #endif
323 switch (opcode) {
324 case VM_ADD: MATH(+); break;
325 case VM_SUB: MATH(-); break;
326 case VM_MUL: MATH(*); break;
327 case VM_DIV:
328 CHECK_DIV_ZERO
329 MATH(/);
330 break;
331 case VM_MOD:
332 CHECK_DIV_ZERO
333 MATH(%);
334 break;
335 case VM_BOR: MATH(|); break;
336 case VM_XOR: MATH(^); break;
337 case VM_AND: MATH(&); break;
339 case VM_JEQ: JXX(==); break;
340 case VM_JNE: JXX(!=); break;
341 case VM_JLT: JXX(<); break;
342 case VM_JLE: JXX(<=); break;
343 case VM_JGT: JXX(>); break;
344 case VM_JGE: JXX(>=); break;
346 case VM_JMP:
347 if (argc == 0) {
348 STACK_WANT(1);
349 argv[0] = STACK_POP();
351 trd->pc = argv[0];
352 break;
354 case VM_BSR:
355 switch (argc) {
356 case 0:
357 STACK_WANT(1);
358 argv[0] = STACK_POP();
359 break;
360 case 1:
361 break;
362 case 2:
363 STACK_FSPC(1);
364 STACK_PUSH(argv[1]);
365 break;
366 case 3:
367 STACK_FSPC(2);
368 STACK_PUSH(argv[1]);
369 STACK_PUSH(argv[2]);
370 break;
372 STACK_FSPC(1);
373 STACK_PUSH(trd->pc);
374 trd->pc = argv[0];
375 break;
377 case VM_END: return 1;
378 case VM_BRK: return -1;
380 case VM_NEW: { // new thread
381 int xtid = vmFindFreeThread();
383 if (argc == 0) {
384 STACK_WANT(1);
385 argv[0] = STACK_POP();
387 if (xtid >= 0) {
388 if (vmInitThread(&vmThreads[xtid]) == 0) {
389 vmThreads[xtid].pc = argv[0];
390 } else {
391 xtid = -1;
394 if (xtid < 0) vmfatal(tid, opc, "too many threads");
395 if (xtid > vmLastThreadId) vmLastThreadId = xtid;
396 if (argc > 1) {
397 if (argp[1]) *(argp[1]) = xtid;
398 } else {
399 STACK_FSPC(1);
400 STACK_PUSH(xtid);
402 break; }
404 case VM_SET:
405 switch (argc) {
406 case 0:
407 STACK_WANT(2);
408 argv[0] = STACK_POP(); // varid
409 argv[1] = STACK_POP(); // value
410 if (argv[0] < 0) {
411 argv[0] = -argv[0];
412 if (argv[0] < VM_VARS_SIZE) vmGVars[argv[0]] = argv[1];
413 } else if (argv[0] < VM_VARS_SIZE) {
414 trd->tvars[argv[0]] = argv[1];
416 break;
417 case 1:
418 STACK_WANT(1);
419 argv[1] = STACK_POP(); // value
420 // fallthru
421 case 2:
422 if (argp[0]) *(argp[0]) = argv[1];
423 break;
424 case 3: {
425 int xtid = argv[2], vid = argv[0];
427 if (xtid < 0 || xtid >= VM_MAX_THREADS || vmThreads[xtid].stack == NULL || vid < 0 || vid >= VM_VARS_SIZE) break;
428 vmThreads[xtid].tvars[vid] = argv[1];
429 break; }
431 break;
433 case VM_GET: {
434 int xtid, vid, pushit;
436 switch (argc) {
437 case 0:
438 STACK_WANT(2);
439 vid = STACK_POP();
440 xtid = STACK_POP();
441 pushit = 1;
442 break;
443 case 1:
444 STACK_WANT(1);
445 vid = argv[0];
446 xtid = STACK_POP();
447 pushit = 1;
448 break;
449 case 2:
450 xtid = argv[0];
451 vid = argv[1];
452 pushit = 1;
453 break;
454 default:
455 xtid = argv[0];
456 vid = argv[1];
457 pushit = 0;
458 break;
460 if (xtid >= 0 && xtid < VM_MAX_THREADS && vmThreads[xtid].stack != NULL && vid >= 0 && vid < VM_VARS_SIZE) {
461 argv[0] = vmThreads[xtid].tvars[vid];
462 } else {
463 argv[0] = 0;
465 if (!pushit) {
466 if (argp[2]) *(argp[2]) = argv[0];
467 } else {
468 STACK_FSPC(1);
469 STACK_PUSH(argv[0]);
471 break;
473 case VM_PSH:
474 if (argc > 0) {
475 STACK_FSPC(argc);
476 for (int f = 0; f < argc; ++f) STACK_PUSH(argv[f]);
477 } else {
478 // dup
479 STACK_WANT(1);
480 STACK_FSPC(1);
481 trd->stack[trd->sp] = trd->stack[trd->sp-1];
482 ++(trd->sp);
484 break;
485 case VM_POP:
486 switch (argc) {
487 case 0: // drop one
488 STACK_WANT(1);
489 --(trd->sp);
490 break;
491 case 1:
492 if (argp[0] == NULL && argv[0] < 0) {
493 // allocate stack space
494 argc = -argv[0];
495 STACK_FSPC(argc);
496 for (; argc > 0; --argc) STACK_PUSH(0);
497 break;
499 // fallthru
500 default:
501 for (int f = 0; f < argc; ++f) {
502 if (argp[f]) {
503 // pop
504 STACK_WANT(1);
505 *(argp[f]) = STACK_POP();
506 } else if (argv[f] > 0) {
507 // drop
508 STACK_WANT(argv[f]);
509 trd->sp -= argv[f];
512 break;
515 break;
516 case VM_SWP:
517 switch (argc) {
518 case 0:
519 STACK_WANT(2);
520 argc = trd->stack[trd->sp-2];
521 trd->stack[trd->sp-2] = trd->stack[trd->sp-1];
522 trd->stack[trd->sp-1] = argc;
523 break;
524 case 1:
525 STACK_WANT(1);
526 argc = trd->stack[trd->sp-1];
527 trd->stack[trd->sp-1] = argv[0];
528 if (argp[0]) *(argp[0]) = argc;
529 break;
530 default: // 2, 3
531 if (argp[0]) *(argp[0]) = argv[1];
532 if (argp[1]) *(argp[1]) = argv[0];
533 break;
535 break;
536 case VM_PCK:
537 case VM_ROL:
538 if (argc < 1) {
539 STACK_WANT(1);
540 argv[0] = STACK_POP();
542 // get item
543 if (argv[0] < 0) argv[0] += trd->sp;
544 if (argv[0] < 0 || argv[0] >= trd->sp) vmfatal(tid, opc, "invalid stack index (%d)", argv[0]);
545 argv[2] = trd->stack[argv[0]];
547 if (opcode == VM_ROL) {
548 for (int f = argv[0]+1; f < trd->sp; ++f) trd->stack[f-1] = trd->stack[f];
549 --(trd->sp);
552 switch (argc) {
553 case 0:
554 case 1:
555 STACK_FSPC(1);
556 STACK_PUSH(argv[2]);
557 break;
558 default:
559 if (argp[1]) *(argp[1]) = argv[2];
560 break;
562 break;
563 case VM_DPT:
564 if (argc > 0) {
565 if (argp[0]) *(argp[0]) = trd->sp;
566 } else {
567 STACK_FSPC(1);
568 argc = trd->sp;
569 STACK_PUSH(argc);
571 break;
573 case VM_TID:
574 if (argc > 0) {
575 if (argp[0]) *(argp[0]) = tid;
576 } else {
577 STACK_FSPC(1);
578 STACK_PUSH(tid);
580 break;
582 case VM_KIL:
583 case VM_SUS:
584 case VM_RES:
585 if (argc == 0) {
586 STACK_WANT(1);
587 argv[0] = STACK_POP();
589 if (argv[0] >= 0 && argv[0] < VM_MAX_THREADS && vmThreads[argv[0]].stack != NULL) {
590 switch (opcode) {
591 case VM_KIL:
592 if (argv[0] == tid) return 1;
593 vmFreeThread(&vmThreads[argv[0]]);
594 fixLastThreadId();
595 break;
596 case VM_SUS:
597 if (argv[0] == tid) return -1;
598 vmThreads[argv[0]].suspended = 1;
599 break;
600 case VM_RES:
601 vmThreads[argv[0]].suspended = 0;
602 break;
605 break;
607 case VM_STA:
608 if (argc == 0) {
609 STACK_WANT(1);
610 argv[0] = STACK_POP();
612 if (argv[0] >= 0 && argv[0] < VM_MAX_THREADS && vmThreads[argv[0]].stack != NULL) {
613 argv[0] = vmThreads[argv[0]].suspended;
614 } else {
615 argv[0] = -1;
617 if (argc > 1) {
618 if (argp[1]) *(argp[1]) = argv[0];
619 } else {
620 STACK_FSPC(1);
621 STACK_PUSH(argv[0]);
623 break;
625 case VM_RXC:
626 if (argc == 0) {
627 STACK_WANT(1);
628 argv[0] = STACK_POP();
630 if (argv[0] < 0 || argv[0] >= vmCodeSize) vmfatal(tid, opc, "invalid address in RXC (%d)", argv[0]);
631 if (argc < 2) {
632 STACK_FSPC(1);
633 STACK_PUSH(vmCode[argv[0]]);
634 } else if (argp[1]) {
635 *(argp[1]) = vmCode[argv[0]];
637 break;
639 case VM_WXC:
640 switch (argc) {
641 case 0:
642 STACK_WANT(2);
643 argv[1] = STACK_POP(); // byte
644 argv[0] = STACK_POP(); // address
645 break;
646 case 1:
647 STACK_WANT(1);
648 argv[1] = STACK_POP(); // byte
649 break;
651 if (argv[0] < 0 || argv[0] >= vmCodeSize) vmfatal(tid, opc, "invalid address in WXC (%d)", argv[0]);
652 if (argv[1] < -128 || argv[1] > 255) vmfatal(tid, opc, "invalid value in WXC (%d)", argv[1]);
653 if (argv[1] < 0) argv[1] &= 0xff;
654 vmCode[argv[0]] = argv[1];
655 break;
657 case VM_RET:
658 switch (argc) {
659 case 0: // simple
660 STACK_WANT(1);
661 trd->pc = STACK_POP();
662 break;
663 case 1:
664 vmfatal(tid, opc, "invalid RET");
665 case 2: // # of locals to pop, # of arguments to pop
666 case 3: // # of locals to pop, # of arguments to pop, retvalue (taken before all pops, pushes after returning)
667 if (argv[0] < 0) argv[0] = 0;
668 if (argv[1] < 0) argv[1] = 0;
669 STACK_WANT(argv[0]+argv[1]+1);
670 trd->sp -= argv[0]; // drop locals
671 trd->pc = STACK_POP();
672 trd->sp -= argv[1]; // drop arguments
673 if (argc == 3) {
674 // push result
675 STACK_FSPC(1);
676 STACK_PUSH(argv[2]);
678 break;
680 break;
682 case VM_RST:
683 if (vmRSTCB) {
684 if (argc == 0) {
685 STACK_WANT(1);
686 argv[0] = STACK_POP();
687 argc = 1;
689 vmRSTCB(tid, opcode, argc, argv, argp);
690 } else if (argc == 0) {
691 STACK_WANT(1);
692 --(trd->sp);
694 break;
696 case VM_MGF:
697 case VM_MGB:
698 switch (argc) {
699 case 0: // nothing
700 case 1: // only dest
701 STACK_WANT(2);
702 argv[1] = STACK_POP(); // y
703 argv[0] = STACK_POP(); // x
704 break;
706 if (vmMapGetCB) argv[0] = vmMapGetCB(tid, opcode==VM_MGF, argv[0], argv[1]); else argv[0] = 0;
707 if (argc == 3) {
708 if (argp[2]) *(argp[2]) = argv[0];
709 } else if (argc == 1) {
710 if (argp[0]) *(argp[0]) = argv[0];
711 } else {
712 STACK_FSPC(1);
713 STACK_PUSH(argv[0]);
715 break;
716 case VM_MSF:
717 case VM_MSB:
718 switch (argc) {
719 case 0:
720 STACK_WANT(2);
721 argv[2] = STACK_POP(); // tile
722 argv[1] = STACK_POP(); // y
723 argv[0] = STACK_POP(); // x
724 break;
725 case 1:
726 STACK_WANT(2);
727 argv[2] = argv[0]; // tile
728 argv[1] = STACK_POP(); // y
729 argv[0] = STACK_POP(); // x
730 break;
731 case 2:
732 STACK_WANT(1);
733 argv[2] = STACK_POP(); // tile
734 break;
736 if (vmMapSetCB) vmMapSetCB(tid, opcode==VM_MSF, argv[0], argv[1], argv[2]);
737 break;
739 default:
740 vmfatal(tid, opc, "invalid opcode (%d)", opcode);
742 return 0;
746 void vmExecuteAll (void) {
747 int runned;
749 memset(vmExecuted, 0, sizeof(vmExecuted));
750 do {
751 runned = 0;
752 for (int t = 0; t < VM_MAX_THREADS; ++t) {
753 if (!vmExecuted[t] && vmThreads[t].stack != NULL && !vmThreads[t].suspended) {
754 int f;
756 vmExecuted[t] = 1;
757 runned = 1;
758 for (f = 1000000; f >= 0; --f) {
759 int res = vmExecuteOne(t);
761 if (res < 0) {
762 // BRK
763 //fprintf(stderr, "!!!\n");
764 break;
766 if (res > 0) {
767 // END
768 vmFreeThread(&vmThreads[t]);
769 fixLastThreadId();
770 break;
773 if (f < 0) vmfatal(t, vmThreads[t].pc, "too many instructions in frame");
776 } while (runned);
780 // <0: BRK; >0: END; 0: ok
781 // maxinst<0: any number of instructions
782 int vmExecuteBSR (int tid, int pc, int maxinst) {
783 int opc;
785 if (tid < 0 || tid >= VM_MAX_THREADS || vmThreads[tid].stack == NULL || vmThreads[tid].suspended) return 2; // END
786 opc = vmThreads[tid].pc;
787 vmPush(tid, -666666);
788 vmThreads[tid].pc = pc;
789 if (maxinst == 0) maxinst = 1000000;
790 for (;;) {
791 int res = vmExecuteOne(tid);
793 if (!vmThreads[tid].stack) return 1;
794 if (vmThreads[tid].suspended) return -2;
795 if (res > 0) vmFreeThread(&vmThreads[tid]);
796 if (res != 0) return res;
797 if (vmThreads[tid].pc == -666666) {
798 vmThreads[tid].pc = opc;
799 return 0;
801 if (maxinst == 1) vmfatal(tid, vmThreads[tid].pc, "too many instructions in vmExecuteBSR");
802 if (maxinst > 0) --maxinst;
807 int vmIsThreadAlive (int tid) {
808 if (tid >= 0 && tid < VM_MAX_THREADS) return vmThreads[tid].stack != NULL;
809 return 0;
813 int vmNewThread (int pc) {
814 int tid = vmFindFreeThread();
816 if (tid >= 0) {
817 if (vmInitThread(&vmThreads[tid]) != 0) return -1;
818 vmThreads[tid].pc = pc;
819 if (tid > vmLastThreadId) vmLastThreadId = tid;
821 return tid;
825 int vmKillThread (int tid) {
826 if (vmIsThreadAlive(tid)) {
827 vmFreeThread(&vmThreads[tid]);
828 fixLastThreadId();
829 return 1;
831 return 0;
835 int vmIsSuspendedThread (int tid) {
836 if (vmIsThreadAlive(tid)) return vmThreads[tid].suspended;
837 return 0;
841 int vmSuspendThread (int tid) {
842 if (vmIsThreadAlive(tid)) { vmThreads[tid].suspended = 1; return 0; }
843 return -1;
847 int vmResumeThread (int tid) {
848 if (vmIsThreadAlive(tid)) { vmThreads[tid].suspended = 0; return 0; }
849 return -1;
853 int vmGetTVar (int tid, int idx) {
854 if (idx >= 0 && idx < VM_VARS_SIZE && vmIsThreadAlive(tid)) return vmThreads[tid].tvars[idx];
855 return 0;
859 int vmSetTVar (int tid, int idx, int value) {
860 if (idx >= 0 && idx < VM_VARS_SIZE && vmIsThreadAlive(tid)) { vmThreads[tid].tvars[idx] = value; return 0; }
861 return -1;
865 int vmGetSP (int tid) {
866 if (vmIsThreadAlive(tid)) return vmThreads[tid].sp;
867 return -1;
871 int vmGetPC (int tid) {
872 if (vmIsThreadAlive(tid)) return vmThreads[tid].pc;
873 return -1;
877 int vmSetPC (int tid, int pc) {
878 if (vmIsThreadAlive(tid) && pc >= 0 && pc < vmCodeSize) { vmThreads[tid].pc = pc; return 0; }
879 return -1;
883 int vmSetSP (int tid, int value) {
884 if (vmIsThreadAlive(tid)) { vmThreads[tid].sp = value; return 0; }
885 return -1;
889 int vmGetStack (int tid, int idx) {
890 if (vmIsThreadAlive(tid)) {
891 if (idx < 0) idx += vmThreads[tid].sp;
892 if (idx >= 0 && idx < VM_STACK_SIZE) return vmThreads[tid].stack[idx];
894 return -1;
898 int vmSetStack (int tid, int idx, int value) {
899 if (vmIsThreadAlive(tid)) {
900 if (idx < 0) idx += vmThreads[tid].sp;
901 if (idx >= 0 && idx < VM_STACK_SIZE) { vmThreads[tid].stack[idx] = value; return 0; }
903 return -1;
907 int vmPush (int tid, int value) {
908 if (vmIsThreadAlive(tid) && vmThreads[tid].sp < VM_STACK_SIZE) {
909 vmThreads[tid].stack[vmThreads[tid].sp++] = value;
910 return 0;
912 return -1;
916 int vmPop (int tid) {
917 if (vmIsThreadAlive(tid) && vmThreads[tid].sp > 0) return vmThreads[tid].stack[--vmThreads[tid].sp];
918 return 0;
922 int vmLoadArgs (int tid, int argc, int argv[], int *argp[], int aargc, int aargv[], int *aargp[]) {
923 if (!vmIsThreadAlive(tid)) return -1;
924 if (argc > 0) {
925 int e = argc;
927 if (e > aargc) e = aargc;
928 for (int f = 0; f < e; ++f) { argv[f] = aargv[f]; argp[f] = aargp[f]; }
929 for (int f = e; f < argc; ++f) {
930 if (vmThreads[tid].sp <= 0) return -1;
931 argp[argc-f] = NULL;
932 argv[argc-f] = vmThreads[tid].stack[--vmThreads[tid].sp];
935 return 0;
939 int vmLastThread (void) {
940 return vmLastThreadId;
944 static int readBuf (FILE *fl, void *buf, int len) {
945 unsigned char *c = (unsigned char *)buf;
947 while (len-- > 0) {
948 unsigned char b;
950 if (fread(&b, 1, 1, fl) != 1) return -1;
951 b ^= 42;
952 *c++ = b;
954 return 0;
958 static int writeBuf (FILE *fl, const void *buf, int len) {
959 const unsigned char *c = (const unsigned char *)buf;
961 while (len-- > 0) {
962 unsigned char b = *c++;
964 b ^= 42;
965 if (fwrite(&b, 1, 1, fl) != 1) return -1;
967 return 0;
971 static int readDW (FILE *fl, int *v) {
972 int t[4];
974 for (int f = 0; f < 4; ++f) {
975 unsigned char b;
977 if (fread(&b, 1, 1, fl) != 1) return -1;
978 t[f] = b^42;
981 if (v) {
982 *v = 0;
983 for (int f = 0; f < 4; ++f) *v |= t[f]<<(8*f);
985 return 0;
989 static int writeDW (FILE *fl, int v) {
990 for (int f = 0; f < 4; ++f) {
991 unsigned char b;
993 b = v&0xff;
994 b ^= 42;
995 if (fwrite(&b, 1, 1, fl) != 1) return -1;
996 v >>= 8;
998 return 0;
1002 int vmSaveState (FILE *fl) {
1003 fixLastThreadId();
1005 if (writeDW(fl, VM_VARS_SIZE) != 0) return -1;
1006 //if (writeDW(fl, VM_STACK_SIZE) != 0) return -1;
1007 if (writeDW(fl, vmLastThreadId) != 0) return -1;
1008 if (writeDW(fl, vmCodeSize) != 0) return -1;
1009 if (writeBuf(fl, vmCode, vmCodeSize) != 0) return -1;
1010 for (int f = 0; f < VM_VARS_SIZE; ++f) if (writeDW(fl, vmGVars[f]) != 0) return -1;
1011 for (int f = 0; f <= vmLastThreadId; ++f) {
1012 if (vmThreads[f].stack != NULL) {
1013 if (writeDW(fl, f) != 0) return -1;
1014 if (writeDW(fl, vmThreads[f].pc) != 0) return -1;
1015 if (writeDW(fl, vmThreads[f].suspended) != 0) return -1;
1016 if (writeDW(fl, vmThreads[f].sp) != 0) return -1;
1017 for (int c = 0; c < vmThreads[f].sp; ++c) if (writeDW(fl, vmThreads[f].stack[c]) != 0) return -1;
1018 for (int c = 0; c < VM_VARS_SIZE; ++c) if (writeDW(fl, vmThreads[f].tvars[c]) != 0) return -1;
1019 } else {
1020 if (writeDW(fl, -1) != 0) return -1;
1023 return 0;
1027 int vmLoadState (FILE *fl) {
1028 int v;
1029 //int ssz;
1031 vmDeinitialize();
1032 if (vmInitialize() != 0) goto fail;
1034 if (readDW(fl, &v) != 0 || v != VM_VARS_SIZE) goto fail;
1035 //if (readDW(fl, &ssz) != 0) goto fail;
1036 if (readDW(fl, &vmLastThreadId) != 0 || vmLastThreadId < 0 || vmLastThreadId > VM_MAX_THREADS) goto fail;
1037 if (readDW(fl, &vmCodeSize) != 0 || vmCodeSize < 1 || vmCodeSize > 65536) goto fail;
1038 if (readBuf(fl, vmCode, vmCodeSize) != 0) goto fail;
1039 for (int f = 0; f < VM_VARS_SIZE; ++f) if (readDW(fl, &vmGVars[f]) != 0) goto fail;
1040 for (int f = 0; f <= vmLastThreadId; ++f) {
1041 int flag;
1043 if (readDW(fl, &flag) != 0) goto fail;
1044 if (flag == -1) continue;
1045 if (flag != f) goto fail;
1046 if (vmInitThread(&vmThreads[f]) != 0) goto fail;
1047 if (readDW(fl, &vmThreads[f].pc) != 0) goto fail;
1048 if (readDW(fl, &vmThreads[f].suspended) != 0) goto fail;
1049 if (readDW(fl, &vmThreads[f].sp) != 0) goto fail;
1050 if (!vmThreads[f].suspended) {
1051 if (vmThreads[f].pc < 0 || vmThreads[f].pc >= vmCodeSize) goto fail;
1052 if (vmThreads[f].sp < 0 || vmThreads[f].sp > VM_STACK_SIZE) goto fail;
1054 for (int c = 0; c < vmThreads[f].sp; ++c) if (readDW(fl, &vmThreads[f].stack[c]) != 0) goto fail;
1055 for (int c = 0; c < VM_VARS_SIZE; ++c) if (readDW(fl, &vmThreads[f].tvars[c]) != 0) goto fail;
1057 return 0;
1058 fail:
1059 vmDeinitialize();
1060 return -1;