alot of code changed; first try to move level loader out of C engine; not working yet
[awish.git] / src / vm.c
blobe6f37cd908eecc0d8047bb750788187430e77af7
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 <stdint.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
21 #include "vm.h"
24 //#define VM_DEBUG
26 #define SECRET (42)
29 // should be enough for everyone!
30 #define MAX_INST_COUNT (1000000)
32 //extern int goobers;
35 //extern void fatal (const char *fmt, ...) __attribute__((__noreturn__)) __attribute__((format(printf, 1, 2)));
36 static __attribute__((__noreturn__)) __attribute__((format(printf, 1, 2))) void fatalis (const char *fmt, ...) {
37 va_list ap;
39 fprintf(stderr, "AWISH FATAL: ");
40 va_start(ap, fmt);
41 vfprintf(stderr, fmt, ap);
42 va_end(ap);
43 fprintf(stderr, "\n");
44 exit(1);
48 #ifndef VM_FATAL_DEFINED
49 static __attribute__((__noreturn__)) __attribute__((format(printf, 3, 4))) void vmfatal (int tid, int pc, const char *fmt, ...) {
50 va_list ap;
52 fprintf(stderr, "VM FATAL (thread #%d, pc=%04x): ", tid, (unsigned int)pc);
53 va_start(ap, fmt);
54 vfprintf(stderr, fmt, ap);
55 va_end(ap);
56 fprintf(stderr, "\n");
57 exit(1);
59 #endif
62 const char *vmOpNames[] = {
63 "add",
64 "sub",
65 "mul",
66 "div",
67 "mod",
68 "bor",
69 "xor",
70 "and",
72 "jeq",
73 "jne",
74 "jlt",
75 "jle",
76 "jgt",
77 "jge",
79 "jmp",
81 "bsr",
83 "brk",
84 "end",
85 "new",
87 "set",
88 "get",
90 "psh",
91 "pop",
92 "swp",
93 "pck",
94 "rol",
95 "dpt",
97 "tid",
98 "kil",
99 "sus",
100 "res",
101 "sta",
103 "rxc",
104 "wxc",
106 "ret",
108 "rst",
110 "mgf",
111 "mgb",
112 "msf",
113 "msb",
115 NULL
119 VMRSTCB vmRSTCB = NULL;
120 VMMapGetCB vmMapGetCB = NULL;
121 VMMapSetCB vmMapSetCB = NULL;
124 typedef struct {
125 int pc;
126 int suspended;
127 int sp;
128 int *stack; // internal
129 int *tvars; // internal
130 } VMThread;
133 static VMThread vmThreads[VM_MAX_THREADS];
134 static int vmExecuted[VM_MAX_THREADS];
135 static int vmLastThreadId = 0;
137 unsigned char vmCode[65536];
138 int vmCodeSize = 0;
139 int vmGVars[VM_VARS_SIZE];
142 static void fixLastThreadId (void) {
143 for (; vmLastThreadId > 0; --vmLastThreadId) if (vmThreads[vmLastThreadId].stack) break;
147 static int vmFindFreeThread (void) {
148 for (int f = 0; f < VM_MAX_THREADS; ++f) if (vmThreads[f].stack == NULL) return f;
149 return -1;
153 static int vmInitThread (VMThread *trd) {
154 trd->pc = 0;
155 trd->sp = 0;
156 trd->suspended = 0;
157 trd->stack = calloc(VM_STACK_SIZE, sizeof(int));
158 if (trd->stack == NULL) return -1;
159 trd->tvars = calloc(VM_VARS_SIZE, sizeof(int));
160 if (trd->tvars == NULL) { free(trd->stack); return -1; }
161 return 0;
165 static void vmFreeThread (VMThread *trd) {
166 if (trd->stack) free(trd->stack);
167 if (trd->tvars) free(trd->tvars);
168 trd->stack = NULL;
169 trd->tvars = NULL;
170 trd->pc = 0;
171 trd->sp = 0;
172 trd->suspended = 0;
176 int vmInitialize (void) {
177 vmFreeLabels();
178 memset(vmThreads, 0, sizeof(vmThreads));
179 // setup main thread
180 vmLastThreadId = 0;
181 if (vmInitThread(vmThreads)) return -1; // alas
182 return 0;
186 void vmDeinitialize (void) {
187 for (int f = 0; f < VM_MAX_THREADS; ++f) {
188 if (vmThreads[f].stack) free(vmThreads[f].stack);
189 if (vmThreads[f].tvars) free(vmThreads[f].tvars);
191 memset(vmThreads, 0, sizeof(vmThreads));
192 vmLastThreadId = 0;
193 vmFreeLabels();
197 static inline int vmGetByte (int tid, int opc, VMThread *trd) {
198 if (trd->pc < 0 || trd->pc >= vmCodeSize) vmfatal(tid, opc, "out of code");
199 return vmCode[trd->pc++];
203 #define STACK_WANT(n) do { if (trd->sp < (n)) vmfatal(tid, opc, "stack underflow"); } while (0)
204 #define STACK_FSPC(n) do { if (trd->sp+(n) > VM_STACK_SIZE) vmfatal(tid, opc, "stack overflow"); } while (0)
206 #define STACK_TOP() (trd->stack[trd->sp-1])
207 #define STACK_POP() (trd->stack[--(trd->sp)])
208 #define STACK_PUSH(n) trd->stack[(trd->sp)++] = (n)
211 #define MATH(op) do { \
212 switch (argc) { \
213 case 0: \
214 STACK_WANT(2); \
215 trd->stack[trd->sp-2] = (trd->stack[trd->sp-2]) op (trd->stack[trd->sp-1]); \
216 --(trd->sp); \
217 break; \
218 case 1: \
219 STACK_WANT(1); \
220 trd->stack[trd->sp-1] = (trd->stack[trd->sp-1]) op (argv[0]); \
221 break; \
222 case 2: if (argp[0] != NULL) *(argp[0]) = (argv[0]) op (argv[1]); break; \
223 case 3: if (argp[2] != NULL) *(argp[2]) = (argv[0]) op (argv[1]); break; \
225 } while(0)
228 #define CHECK_DIV_ZERO \
229 switch (argc) { \
230 case 0: \
231 if (trd->sp > 0 && trd->stack[trd->sp-1] == 0) { fprintf(stderr, "VM FATAL: division by zero!\n"); exit(1); } \
232 break; \
233 case 1: \
234 if (argv[0] == 0) { fprintf(stderr, "VM FATAL: division by zero!\n"); exit(1); } \
235 break; \
236 default: \
237 if (argv[1] == 0) { fprintf(stderr, "VM FATAL: division by zero!\n"); exit(1); } \
238 break; \
242 #define JXX(op) do { \
243 switch (argc) { \
244 case 0: \
245 STACK_WANT(3); \
246 argv[0] = STACK_POP(); \
247 case 1: \
248 STACK_WANT(2); \
249 argv[2] = STACK_POP(); \
250 argv[1] = STACK_POP(); \
251 break; \
252 case 2: \
253 STACK_WANT(1); \
254 argv[2] = argv[1]; \
255 argv[1] = STACK_POP(); \
256 break; \
258 if (argv[1] op argv[2]) trd->pc = argv[0]; \
259 } while (0)
262 static inline int isBranch (int opcode) {
263 return
264 opcode == VM_JMP ||
265 opcode == VM_BSR ||
266 opcode == VM_JEQ ||
267 opcode == VM_JNE ||
268 opcode == VM_JLT ||
269 opcode == VM_JLE ||
270 opcode == VM_JGT ||
271 opcode == VM_JGE;
275 // <0: BRK; >0: END
276 int vmExecuteOne (int tid) {
277 VMThread *trd = &vmThreads[tid];
278 int opcode;
279 int argc;
280 int argv[3]; // argument values
281 int *argp[3]; // pointer to vars for each arg
282 int opc = trd->pc;
284 // decode instruction
285 opcode = vmGetByte(tid, opc, trd);
286 argc = (opcode>>6)&0x03;
287 opcode &= 0x3f;
288 argv[0] = argv[1] = argv[2] = 0;
289 argp[0] = argp[1] = argp[2] = NULL;
290 // read operands
291 if (tid > vmLastThreadId) vmLastThreadId = tid;
292 for (int f = 0; f < argc; ++f) {
293 int vn = vmGetByte(tid, opc, trd);
295 if (vn == 255) {
296 // immediate
297 argv[f] = vmGetByte(tid, opc, trd);
298 argv[f] |= vmGetByte(tid, opc, trd)<<8;
299 if (argv[f] >= 32768 && (f != 0 || !isBranch(opcode))) argv[f] -= 65536;
300 } else {
301 if (vn == 127) {
302 // stack var; <0: from stack top
303 argv[f] = vmGetByte(tid, opc, trd);
304 argv[f] |= (vmGetByte(tid, opc, trd)<<8);
305 if (argv[f] >= 32768) argv[f] -= 65536;
306 if (argv[f] < 0) argv[f] += trd->sp;
307 if (argv[f] < 0 || argv[f] >= VM_STACK_SIZE) vmfatal(tid, opc, "invalid stack offset");
308 argp[f] = trd->stack+argv[f];
309 } else {
310 // variable
311 argp[f] = vn&0x80 ? vmGVars : trd->tvars;
312 argp[f] += vn&0x7f;
314 argv[f] = *(argp[f]);
318 #ifdef VM_DEBUG
319 fprintf(stderr, "%04x: ", (unsigned int)opc);
320 if (opcode < sizeof(vmOpNames)/sizeof(char *)-1) fprintf(stderr, "%s", vmOpNames[opcode]); else fprintf(stderr, "BAD");
321 for (int f = 0; f < argc; ++f) {
322 fputc(' ', stderr);
323 if (argp[f]) {
324 unsigned int ofs;
326 fputc('[', stderr);
327 if (argp[f] >= trd->tvars && argp[f] < trd->tvars+VM_VARS_SIZE) {
328 ofs = (unsigned int)(argp[f]-trd->tvars);
329 } else if (argp[f] >= trd->stack && argp[f] < trd->stack+VM_VARS_SIZE) {
330 ofs = (unsigned int)(argp[f]-trd->stack);
331 fputc('.', stderr);
332 } else if (argp[f] >= vmGVars && argp[f] < vmGVars+VM_VARS_SIZE) {
333 ofs = (unsigned int)(argp[f]-vmGVars);
334 fputc('@', stderr);
336 fprintf(stderr, "%u]", ofs);
338 if (f == 0 && isBranch(opcode)) {
339 fprintf(stderr, "(0x%04x)", argv[f]);
340 } else {
341 fprintf(stderr, "(%d)", argv[f]);
344 fputc('\n', stderr);
345 #endif
347 switch (opcode) {
348 case VM_ADD: MATH(+); break;
349 case VM_SUB: MATH(-); break;
350 case VM_MUL: MATH(*); break;
351 case VM_DIV:
352 CHECK_DIV_ZERO
353 MATH(/);
354 break;
355 case VM_MOD:
356 CHECK_DIV_ZERO
357 MATH(%);
358 break;
359 case VM_BOR: MATH(|); break;
360 case VM_XOR: MATH(^); break;
361 case VM_AND: MATH(&); break;
363 case VM_JEQ: JXX(==); break;
364 case VM_JNE: JXX(!=); break;
365 case VM_JLT: JXX(<); break;
366 case VM_JLE: JXX(<=); break;
367 case VM_JGT: JXX(>); break;
368 case VM_JGE: JXX(>=); break;
370 case VM_JMP:
371 if (argc == 0) {
372 STACK_WANT(1);
373 argv[0] = STACK_POP();
375 trd->pc = argv[0];
376 break;
378 case VM_BSR:
379 switch (argc) {
380 case 0:
381 STACK_WANT(1);
382 argv[0] = STACK_POP();
383 break;
384 case 1:
385 break;
386 case 2:
387 STACK_FSPC(1);
388 STACK_PUSH(argv[1]);
389 break;
390 case 3:
391 STACK_FSPC(2);
392 STACK_PUSH(argv[1]);
393 STACK_PUSH(argv[2]);
394 break;
396 STACK_FSPC(1);
397 STACK_PUSH(trd->pc);
398 trd->pc = argv[0];
399 break;
401 case VM_END: return 1;
402 case VM_BRK: return -1;
404 case VM_NEW: { // new thread
405 int xtid = vmFindFreeThread();
407 if (argc == 0) {
408 STACK_WANT(1);
409 argv[0] = STACK_POP();
411 if (xtid >= 0) {
412 if (vmInitThread(&vmThreads[xtid]) == 0) {
413 vmThreads[xtid].pc = argv[0];
414 } else {
415 xtid = -1;
418 if (xtid < 0) vmfatal(tid, opc, "too many threads");
419 if (xtid > vmLastThreadId) vmLastThreadId = xtid;
420 if (argc > 1) {
421 if (argp[1]) *(argp[1]) = xtid;
422 } else {
423 STACK_FSPC(1);
424 STACK_PUSH(xtid);
426 break; }
428 case VM_SET:
429 switch (argc) {
430 case 0:
431 STACK_WANT(2);
432 argv[0] = STACK_POP(); // varid
433 argv[1] = STACK_POP(); // value
434 if (argv[0] < 0) {
435 argv[0] = -argv[0];
436 if (argv[0] < VM_VARS_SIZE) vmGVars[argv[0]] = argv[1];
437 } else if (argv[0] < VM_VARS_SIZE) {
438 trd->tvars[argv[0]] = argv[1];
440 break;
441 case 1:
442 STACK_WANT(1);
443 argv[1] = STACK_POP(); // value
444 // fallthru
445 case 2:
446 if (argp[0]) *(argp[0]) = argv[1];
447 break;
448 case 3: {
449 int xtid = argv[2], vid = argv[0];
451 if (xtid < 0 || xtid >= VM_MAX_THREADS || vmThreads[xtid].stack == NULL || vid < 0 || vid >= VM_VARS_SIZE) break;
452 vmThreads[xtid].tvars[vid] = argv[1];
453 break; }
455 break;
457 case VM_GET: {
458 int xtid, vid, pushit;
460 switch (argc) {
461 case 0:
462 STACK_WANT(2);
463 vid = STACK_POP();
464 xtid = STACK_POP();
465 pushit = 1;
466 break;
467 case 1:
468 STACK_WANT(1);
469 vid = argv[0];
470 xtid = STACK_POP();
471 pushit = 1;
472 break;
473 case 2:
474 xtid = argv[0];
475 vid = argv[1];
476 pushit = 1;
477 break;
478 default:
479 xtid = argv[0];
480 vid = argv[1];
481 pushit = 0;
482 break;
484 if (xtid >= 0 && xtid < VM_MAX_THREADS && vmThreads[xtid].stack != NULL && vid >= 0 && vid < VM_VARS_SIZE) {
485 argv[0] = vmThreads[xtid].tvars[vid];
486 } else {
487 argv[0] = 0;
489 if (!pushit) {
490 if (argp[2]) *(argp[2]) = argv[0];
491 } else {
492 STACK_FSPC(1);
493 STACK_PUSH(argv[0]);
495 break;
497 case VM_PSH:
498 if (argc > 0) {
499 STACK_FSPC(argc);
500 for (int f = 0; f < argc; ++f) STACK_PUSH(argv[f]);
501 } else {
502 // dup
503 STACK_WANT(1);
504 STACK_FSPC(1);
505 trd->stack[trd->sp] = trd->stack[trd->sp-1];
506 ++(trd->sp);
508 break;
509 case VM_POP:
510 switch (argc) {
511 case 0: // drop one
512 STACK_WANT(1);
513 --(trd->sp);
514 break;
515 case 1:
516 if (argp[0] == NULL && argv[0] < 0) {
517 // allocate stack space
518 argc = -argv[0];
519 STACK_FSPC(argc);
520 for (; argc > 0; --argc) STACK_PUSH(0);
521 break;
523 // fallthru
524 default:
525 for (int f = 0; f < argc; ++f) {
526 if (argp[f]) {
527 // pop
528 STACK_WANT(1);
529 *(argp[f]) = STACK_POP();
530 } else if (argv[f] > 0) {
531 // drop
532 STACK_WANT(argv[f]);
533 trd->sp -= argv[f];
536 break;
539 break;
540 case VM_SWP:
541 switch (argc) {
542 case 0:
543 STACK_WANT(2);
544 argc = trd->stack[trd->sp-2];
545 trd->stack[trd->sp-2] = trd->stack[trd->sp-1];
546 trd->stack[trd->sp-1] = argc;
547 break;
548 case 1:
549 STACK_WANT(1);
550 argc = trd->stack[trd->sp-1];
551 trd->stack[trd->sp-1] = argv[0];
552 if (argp[0]) *(argp[0]) = argc;
553 break;
554 default: // 2, 3
555 if (argp[0]) *(argp[0]) = argv[1];
556 if (argp[1]) *(argp[1]) = argv[0];
557 break;
559 break;
560 case VM_PCK:
561 case VM_ROL:
562 if (argc < 1) {
563 STACK_WANT(1);
564 argv[0] = STACK_POP();
566 // get item
567 if (argv[0] < 0) argv[0] += trd->sp;
568 if (argv[0] < 0 || argv[0] >= trd->sp) vmfatal(tid, opc, "invalid stack index (%d)", argv[0]);
569 argv[2] = trd->stack[argv[0]];
571 if (opcode == VM_ROL) {
572 for (int f = argv[0]+1; f < trd->sp; ++f) trd->stack[f-1] = trd->stack[f];
573 --(trd->sp);
576 switch (argc) {
577 case 0:
578 case 1:
579 STACK_FSPC(1);
580 STACK_PUSH(argv[2]);
581 break;
582 default:
583 if (argp[1]) *(argp[1]) = argv[2];
584 break;
586 break;
587 case VM_DPT:
588 if (argc > 0) {
589 if (argp[0]) *(argp[0]) = trd->sp;
590 } else {
591 STACK_FSPC(1);
592 argc = trd->sp;
593 STACK_PUSH(argc);
595 break;
597 case VM_TID:
598 if (argc > 0) {
599 if (argp[0]) *(argp[0]) = tid;
600 } else {
601 STACK_FSPC(1);
602 STACK_PUSH(tid);
604 break;
606 case VM_KIL:
607 case VM_SUS:
608 case VM_RES:
609 if (argc == 0) {
610 STACK_WANT(1);
611 argv[0] = STACK_POP();
613 if (argv[0] >= 0 && argv[0] < VM_MAX_THREADS && vmThreads[argv[0]].stack != NULL) {
614 switch (opcode) {
615 case VM_KIL:
616 if (argv[0] == tid) return 1;
617 vmFreeThread(&vmThreads[argv[0]]);
618 fixLastThreadId();
619 break;
620 case VM_SUS:
621 if (argv[0] == tid) return -1;
622 vmThreads[argv[0]].suspended = 1;
623 break;
624 case VM_RES:
625 vmThreads[argv[0]].suspended = 0;
626 break;
629 break;
631 case VM_STA:
632 if (argc == 0) {
633 STACK_WANT(1);
634 argv[0] = STACK_POP();
636 if (argv[0] >= 0 && argv[0] < VM_MAX_THREADS && vmThreads[argv[0]].stack != NULL) {
637 argv[0] = vmThreads[argv[0]].suspended;
638 } else {
639 argv[0] = -1;
641 if (argc > 1) {
642 if (argp[1]) *(argp[1]) = argv[0];
643 } else {
644 STACK_FSPC(1);
645 STACK_PUSH(argv[0]);
647 break;
649 case VM_RXC:
650 if (argc == 0) {
651 STACK_WANT(1);
652 argv[0] = STACK_POP();
654 if (argv[0] < 0 || argv[0] >= vmCodeSize) vmfatal(tid, opc, "invalid address in RXC (%d)", argv[0]);
655 if (argc < 2) {
656 STACK_FSPC(1);
657 STACK_PUSH(vmCode[argv[0]]);
658 } else if (argp[1]) {
659 *(argp[1]) = vmCode[argv[0]];
661 break;
663 case VM_WXC:
664 switch (argc) {
665 case 0:
666 STACK_WANT(2);
667 argv[1] = STACK_POP(); // byte
668 argv[0] = STACK_POP(); // address
669 break;
670 case 1:
671 STACK_WANT(1);
672 argv[1] = STACK_POP(); // byte
673 break;
675 if (argv[0] < 0 || argv[0] >= vmCodeSize) vmfatal(tid, opc, "invalid address in WXC (%d)", argv[0]);
676 if (argv[1] < -128 || argv[1] > 255) vmfatal(tid, opc, "invalid value in WXC (%d)", argv[1]);
677 if (argv[1] < 0) argv[1] &= 0xff;
678 vmCode[argv[0]] = argv[1];
679 break;
681 case VM_RET:
682 switch (argc) {
683 case 0: // simple
684 STACK_WANT(1);
685 trd->pc = STACK_POP();
686 break;
687 case 1:
688 vmfatal(tid, opc, "invalid RET");
689 case 2: // # of locals to pop, # of arguments to pop
690 case 3: // # of locals to pop, # of arguments to pop, retvalue (taken before all pops, pushes after returning)
691 if (argv[0] < 0) argv[0] = 0;
692 if (argv[1] < 0) argv[1] = 0;
693 STACK_WANT(argv[0]+argv[1]+1);
694 trd->sp -= argv[0]; // drop locals
695 trd->pc = STACK_POP();
696 trd->sp -= argv[1]; // drop arguments
697 if (argc == 3) {
698 // push result
699 STACK_FSPC(1);
700 STACK_PUSH(argv[2]);
702 break;
704 break;
706 case VM_RST:
707 if (vmRSTCB) {
708 if (argc == 0) {
709 STACK_WANT(1);
710 argv[0] = STACK_POP();
711 argc = 1;
713 vmRSTCB(tid, opcode, argc, argv, argp);
714 } else if (argc == 0) {
715 STACK_WANT(1);
716 --(trd->sp);
718 break;
720 case VM_MGF:
721 case VM_MGB:
722 switch (argc) {
723 case 0: // nothing
724 case 1: // only dest
725 STACK_WANT(2);
726 argv[1] = STACK_POP(); // y
727 argv[0] = STACK_POP(); // x
728 break;
730 if (vmMapGetCB) argv[0] = vmMapGetCB(tid, opcode==VM_MGF, argv[0], argv[1]); else argv[0] = 0;
731 if (argc == 3) {
732 if (argp[2]) *(argp[2]) = argv[0];
733 } else if (argc == 1) {
734 if (argp[0]) *(argp[0]) = argv[0];
735 } else {
736 STACK_FSPC(1);
737 STACK_PUSH(argv[0]);
739 break;
740 case VM_MSF:
741 case VM_MSB:
742 switch (argc) {
743 case 0:
744 STACK_WANT(2);
745 argv[2] = STACK_POP(); // tile
746 argv[1] = STACK_POP(); // y
747 argv[0] = STACK_POP(); // x
748 break;
749 case 1:
750 STACK_WANT(2);
751 argv[2] = argv[0]; // tile
752 argv[1] = STACK_POP(); // y
753 argv[0] = STACK_POP(); // x
754 break;
755 case 2:
756 STACK_WANT(1);
757 argv[2] = STACK_POP(); // tile
758 break;
760 if (vmMapSetCB) vmMapSetCB(tid, opcode==VM_MSF, argv[0], argv[1], argv[2]);
761 break;
763 default:
764 vmfatal(tid, opc, "invalid opcode (%d)", opcode);
766 return 0;
770 void vmExecuteAll (void) {
771 int runned;
773 memset(vmExecuted, 0, sizeof(vmExecuted));
774 do {
775 runned = 0;
776 for (int t = 0; t < VM_MAX_THREADS; ++t) {
777 if (!vmExecuted[t] && vmThreads[t].stack != NULL && !vmThreads[t].suspended) {
778 int f;
780 vmExecuted[t] = 1;
781 runned = 1;
782 for (f = MAX_INST_COUNT; f >= 0; --f) {
783 int res = vmExecuteOne(t);
785 if (res < 0) {
786 // BRK
787 //fprintf(stderr, "!!!\n");
788 break;
790 if (res > 0) {
791 // END
792 vmFreeThread(&vmThreads[t]);
793 fixLastThreadId();
794 break;
797 if (f < 0) vmfatal(t, vmThreads[t].pc, "too many instructions in frame");
800 } while (runned);
804 // <0: BRK; >0: END; 0: ok
805 // maxinst<0: any number of instructions
806 int vmExecuteBSR (int tid, int pc, int maxinst) {
807 int opc;
809 if (tid < 0 || tid >= VM_MAX_THREADS || vmThreads[tid].stack == NULL || vmThreads[tid].suspended) return 2; // END
810 opc = vmThreads[tid].pc;
811 vmPush(tid, -666666);
812 vmThreads[tid].pc = pc;
813 if (maxinst == 0) maxinst = MAX_INST_COUNT;
814 for (;;) {
815 int res = vmExecuteOne(tid);
817 if (!vmThreads[tid].stack) return 1;
818 if (vmThreads[tid].suspended) return -2;
819 if (res > 0) vmFreeThread(&vmThreads[tid]);
820 if (res != 0) return res;
821 if (vmThreads[tid].pc == -666666) {
822 vmThreads[tid].pc = opc;
823 return 0;
825 if (maxinst == 1) vmfatal(tid, vmThreads[tid].pc, "too many instructions in vmExecuteBSR");
826 if (maxinst > 0) --maxinst;
831 int vmIsThreadAlive (int tid) {
832 if (tid >= 0 && tid < VM_MAX_THREADS) return vmThreads[tid].stack != NULL;
833 return 0;
837 int vmNewThread (int pc) {
838 int tid = vmFindFreeThread();
840 if (tid >= 0) {
841 if (vmInitThread(&vmThreads[tid]) != 0) return -1;
842 vmThreads[tid].pc = pc;
843 if (tid > vmLastThreadId) vmLastThreadId = tid;
845 return tid;
849 int vmKillThread (int tid) {
850 if (vmIsThreadAlive(tid)) {
851 vmFreeThread(&vmThreads[tid]);
852 fixLastThreadId();
853 return 1;
855 return 0;
859 int vmIsSuspendedThread (int tid) {
860 if (vmIsThreadAlive(tid)) return vmThreads[tid].suspended;
861 return 0;
865 int vmSuspendThread (int tid) {
866 if (vmIsThreadAlive(tid)) { vmThreads[tid].suspended = 1; return 0; }
867 return -1;
871 int vmResumeThread (int tid) {
872 if (vmIsThreadAlive(tid)) { vmThreads[tid].suspended = 0; return 0; }
873 return -1;
877 int vmGetTVar (int tid, int idx) {
878 if (idx >= 0 && idx < VM_VARS_SIZE && vmIsThreadAlive(tid)) return vmThreads[tid].tvars[idx];
879 return 0;
883 int vmSetTVar (int tid, int idx, int value) {
884 if (idx >= 0 && idx < VM_VARS_SIZE && vmIsThreadAlive(tid)) { vmThreads[tid].tvars[idx] = value; return 0; }
885 return -1;
889 int vmGetSP (int tid) {
890 if (vmIsThreadAlive(tid)) return vmThreads[tid].sp;
891 return -1;
895 int vmGetPC (int tid) {
896 if (vmIsThreadAlive(tid)) return vmThreads[tid].pc;
897 return -1;
901 int vmSetPC (int tid, int pc) {
902 if (vmIsThreadAlive(tid) && pc >= 0 && pc < vmCodeSize) { vmThreads[tid].pc = pc; return 0; }
903 return -1;
907 int vmSetSP (int tid, int value) {
908 if (vmIsThreadAlive(tid)) { vmThreads[tid].sp = value; return 0; }
909 return -1;
913 int vmGetStack (int tid, int idx) {
914 if (vmIsThreadAlive(tid)) {
915 if (idx < 0) idx += vmThreads[tid].sp;
916 if (idx >= 0 && idx < VM_STACK_SIZE) return vmThreads[tid].stack[idx];
918 return -1;
922 int vmSetStack (int tid, int idx, int value) {
923 if (vmIsThreadAlive(tid)) {
924 if (idx < 0) idx += vmThreads[tid].sp;
925 if (idx >= 0 && idx < VM_STACK_SIZE) { vmThreads[tid].stack[idx] = value; return 0; }
927 return -1;
931 int vmPush (int tid, int value) {
932 if (vmIsThreadAlive(tid) && vmThreads[tid].sp < VM_STACK_SIZE) {
933 vmThreads[tid].stack[vmThreads[tid].sp++] = value;
934 return 0;
936 return -1;
940 int vmPop (int tid) {
941 if (vmIsThreadAlive(tid) && vmThreads[tid].sp > 0) return vmThreads[tid].stack[--vmThreads[tid].sp];
942 return 0;
946 int vmLoadArgs (int tid, int argc, int argv[], int *argp[], int aargc, int aargv[], int *aargp[]) {
947 if (!vmIsThreadAlive(tid)) return -1;
948 if (argc > 0) {
949 int e = argc;
951 if (e > aargc) e = aargc;
952 for (int f = 0; f < e; ++f) { argv[f] = aargv[f]; argp[f] = aargp[f]; }
953 for (int f = e; f < argc; ++f) {
954 if (vmThreads[tid].sp <= 0) return -1;
955 argp[argc-f] = NULL;
956 argv[argc-f] = vmThreads[tid].stack[--vmThreads[tid].sp];
959 return 0;
963 int vmLastThread (void) {
964 return vmLastThreadId;
968 static int readBuf (FILE *fl, void *buf, int len) {
969 unsigned char *c = (unsigned char *)buf;
971 while (len-- > 0) {
972 unsigned char b;
974 if (fread(&b, 1, 1, fl) != 1) return -1;
975 b ^= SECRET;
976 *c++ = b;
978 return 0;
982 static int writeBuf (FILE *fl, const void *buf, int len) {
983 const unsigned char *c = (const unsigned char *)buf;
985 while (len-- > 0) {
986 unsigned char b = *c++;
988 b ^= SECRET;
989 if (fwrite(&b, 1, 1, fl) != 1) return -1;
991 return 0;
995 static int readDW (FILE *fl, int *v) {
996 int t[4];
998 for (int f = 0; f < 4; ++f) {
999 unsigned char b;
1001 if (fread(&b, 1, 1, fl) != 1) return -1;
1002 t[f] = b^SECRET;
1005 if (v) {
1006 *v = 0;
1007 for (int f = 0; f < 4; ++f) *v |= t[f]<<(8*f);
1009 return 0;
1013 static int writeDW (FILE *fl, int v) {
1014 for (int f = 0; f < 4; ++f) {
1015 unsigned char b;
1017 b = v&0xff;
1018 b ^= SECRET;
1019 if (fwrite(&b, 1, 1, fl) != 1) return -1;
1020 v >>= 8;
1022 return 0;
1026 ////////////////////////////////////////////////////////////////////////////////
1027 static VMLabelInfo *labels = NULL;
1030 ////////////////////////////////////////////////////////////////////////////////
1031 void vmFreeLabels (void) {
1032 while (labels != NULL) {
1033 VMLabelInfo *l = labels;
1035 labels = l->next;
1036 if (l->name) free(l->name);
1037 free(l);
1042 void vmFreeLabelsUntilMark (const char *name) {
1043 while (labels != NULL) {
1044 VMLabelInfo *l = labels;
1045 int isMark = (l->type == LB_MARK);
1047 if (!(isMark && ((name == NULL && l->name == NULL) || (l->name != NULL && name != NULL && strcmp(name, l->name) == 0)))) isMark = 0;
1048 labels = l->next;
1049 if (l->name) free(l->name);
1050 free(l);
1051 if (isMark) break;
1056 VMLabelInfo *vmLabelAddMark (const char *name) {
1057 VMLabelInfo *l = malloc(sizeof(VMLabelInfo));
1059 if (l == NULL) fatalis("out of memory");
1060 l->name = NULL;
1061 if (name != NULL) {
1062 l->name = strdup(name);
1063 if (l->name == NULL) fatalis("out of memory");
1065 l->type = LB_MARK;
1066 l->value = -1;
1067 l->next = labels;
1068 l->pub = 0;
1069 labels = l;
1070 return l;
1074 VMLabelInfo *vmAddLabel (const char *name, int type, int value, int pub) {
1075 if (name != NULL && name[0]) {
1076 VMLabelInfo *l = malloc(sizeof(VMLabelInfo));
1078 if (l == NULL) fatalis("out of memory");
1079 if (name != NULL) {
1080 l->name = strdup(name);
1081 if (l->name == NULL) fatalis("out of memory");
1082 } else {
1083 l->name = NULL;
1085 l->type = type;
1086 l->value = value;
1087 l->pub = pub;
1088 l->next = labels;
1089 labels = l;
1090 return l;
1092 return NULL;
1096 static VMLabelInfo *vmAddLabelToEnd (const char *name, int type, int value, int pub) {
1097 if (name != NULL && name[0]) {
1098 VMLabelInfo *l = malloc(sizeof(VMLabelInfo)), *last = NULL;
1100 if (l == NULL) fatalis("out of memory");
1101 if (name != NULL) {
1102 l->name = strdup(name);
1103 if (l->name == NULL) fatalis("out of memory");
1104 } else {
1105 l->name = NULL;
1107 l->type = type;
1108 l->value = value;
1109 l->pub = pub;
1110 l->next = NULL;
1111 if (labels == NULL) {
1112 labels = l;
1113 } else {
1114 for (last = labels; last->next != NULL; last = last->next) ;
1115 last->next = l;
1117 return l;
1119 return NULL;
1123 VMLabelInfo *vmFindLabel (const char *name) {
1124 if (name != NULL && name[0]) {
1125 for (VMLabelInfo *l = labels; l != NULL; l = l->next) {
1126 if (l->name && l->name[0] && strcasecmp(l->name, name) == 0) return l;
1129 return NULL;
1133 int vmFindPC (const char *name) {
1134 VMLabelInfo *l = vmFindLabel(name);
1136 if (l == NULL) fatalis("no code label: '%s'", name);
1137 return l->value;
1141 int vmFindVarIndex (const char *name) {
1142 VMLabelInfo *l = vmFindLabel(name);
1144 if (l == NULL || (l->type != LB_GVAR && l->type != LB_TVAR && l->type != LB_SVAR)) fatalis("no var label: '%s'", name);
1145 return l->value;
1149 int vmFindGVarIndex (const char *name) {
1150 VMLabelInfo *l = vmFindLabel(name);
1152 if (l == NULL || l->type != LB_GVAR) fatalis("no global var label: '%s'", name);
1153 return l->value;
1157 int vmFindTVarIndex (const char *name) {
1158 VMLabelInfo *l = vmFindLabel(name);
1160 if (l == NULL || l->type != LB_TVAR) fatalis("no thread var label: '%s'", name);
1161 return l->value;
1165 int vmFindSVarIndex (const char *name) {
1166 VMLabelInfo *l = vmFindLabel(name);
1168 if (l == NULL || l->type != LB_SVAR) fatalis("no thread var label: '%s'", name);
1169 return l->value;
1173 int vmFindConst (const char *name) {
1174 VMLabelInfo *l = vmFindLabel(name);
1176 if (l == NULL || l->type != LB_CONST) fatalis("no const label: '%s'", name);
1177 return l->value;
1181 VMLabelInfo *vmFindMark (const char *name) {
1182 for (VMLabelInfo *l = labels; l != NULL; l = l->next) {
1183 if (l->type != LB_MARK) continue;
1184 if (name == NULL && l->name == NULL) return l;
1185 if (name != NULL && l->name != NULL && strcmp(name, l->name) == 0) return l;
1187 return NULL;
1191 #define XREAD(dest,size,errlabel) do { \
1192 if (pos+(size) > rsz) goto errlabel; \
1193 if ((size) > 0) memcpy((dest), buf+pos, (size)); \
1194 pos += (size); \
1195 } while (0)
1198 #define XREADB(dest,errlabel) do { \
1199 if (pos+1 > rsz) goto errlabel; \
1200 (dest) = buf[pos]; \
1201 ++pos; \
1202 } while (0)
1205 #define XREADW(dest,errlabel) do { \
1206 if (pos+2 > rsz) goto errlabel; \
1207 (dest) = buf[pos+1]; \
1208 (dest) <<= 8; \
1209 (dest) |= buf[pos+0]; \
1210 pos += 2; \
1211 } while (0)
1215 extern int vmLoadCodeFileFromDump (const void *data, int datasize, int pc) {
1216 int rsz = datasize, pos = 4;
1217 const uint8_t *dta = (const uint8_t *)data;
1218 uint8_t *buf;
1219 int csize, lcnt, rcnt, elcnt;
1221 if (rsz < 4) return -1;
1222 if (memcmp(data, "AVM1", 4) != 0) goto quitbufonly;
1223 buf = malloc(rsz);
1224 if (buf == NULL) return 1;
1226 for (int f = 4; f < rsz; ++f) buf[f] = dta[f]^SECRET;
1228 XREADW(csize, quitbufonly);
1229 XREADW(rcnt, quitbufonly); // fixups
1230 XREADW(elcnt, quitbufonly); // extern labels
1231 XREADW(lcnt, quitbufonly); // labels
1232 if (pc < 0 || pc+csize > 65535) goto quitbufonly;
1234 /*if (goobers)*/ fprintf(stderr, "code: %d bytes, %d public labels, %d relocations, %d externs\n", csize, lcnt, rcnt, elcnt);
1236 XREAD(vmCode+pc, csize, quitbufonly);
1238 // do relocations
1239 for (int f = 0; f < rcnt; ++f) {
1240 int rel, newpc, val;
1242 XREADW(rel, quitbufonly);
1243 if (rel < 0 || rel+1 >= csize) goto quitbufonly;
1244 newpc = pc+rel; // fix here
1245 // get old value
1246 val = vmCode[newpc+1];
1247 val <<= 8;
1248 val |= vmCode[newpc+0];
1249 // fix it
1250 val += pc;
1251 // set new value
1252 vmCode[newpc+0] = val&0xff;
1253 vmCode[newpc+1] = (val>>8)&0xff;
1256 // load extern labels and perform fixups
1257 for (int f = 0; f < elcnt; ++f) {
1258 char name[258];
1259 int type, namelen;
1260 int rcnt;
1262 XREADB(type, quitbufonly);
1263 XREADB(namelen, quitbufonly);
1264 XREAD(name, namelen, quitbufonly);
1265 XREADW(rcnt, quitbufonly);
1267 if (type < LB_MIN_TYPE || type > LB_MAX_TYPE) goto quitbufonly;
1268 if (namelen < 1) goto quitbufonly;
1269 name[namelen] = 0;
1271 for (int c = 0; c < rcnt; ++c) {
1272 int xpc, size, val;
1273 VMLabelInfo *l;
1275 XREADB(size, quitbufonly);
1276 XREADW(xpc, quitbufonly);
1278 if (size != 1 && size != 2) goto quitbufonly;
1279 if (xpc < 0 || xpc+size > csize) goto quitbufonly;
1281 xpc += pc;
1282 l = vmFindLabel(name);
1283 if (l == NULL) {
1284 /*if (goobers)*/ fprintf(stderr, "VM: unknown extern: '%s'\n", name);
1285 goto quitbufonly;
1287 val = l->value;
1288 if (l->type == LB_GVAR) val |= 0x80;
1289 //fprintf(stderr, "%d: [%s]: ofs=%d, size=%d, value=%d (%d)\n", c, l->name, xpc, size, val, vmCode[xpc]);
1290 if (size == 1 && (val < -128 || val > 255)) {
1291 /*if (goobers)*/ fprintf(stderr, "VM: extern too big: '%s'\n", name);
1292 goto quitbufonly;
1294 vmCode[xpc+0] = val&0xff;
1295 if (size == 2) vmCode[xpc+1] = (val>>8)&0xff;
1299 vmLabelAddMark(NULL); // for this code labels
1300 for (int f = 0; f < lcnt; ++f) {
1301 unsigned char type, namelen, b;
1302 char name[258];
1303 int value;
1305 XREADB(type, quit);
1307 switch (type&0x7f) {
1308 case LB_GVAR:
1309 case LB_TVAR:
1310 XREADB(b, quit);
1311 value = b;
1312 break;
1313 case LB_SVAR:
1314 case LB_CONST:
1315 XREADW(value, quit);
1316 if (value >= 32768) value -= 65536;
1317 break;
1318 case LB_CODE:
1319 XREADW(value, quit);
1320 value += pc; // fixup
1321 break;
1322 default:
1323 fatalis("invalid label type: %d\n", type);
1324 goto quit;
1327 XREADB(namelen, quit);
1328 XREAD(name, namelen, quit);
1329 name[namelen] = 0;
1331 if (namelen > 0) vmAddLabel(name, type&0x7f, value, type&0x80?1:0);
1332 //if (l->type == LB_CODE) printf("%d/%d: [%s] %d : %d\n", f, lcnt, l->name, l->value-pc, l->value);
1335 free(buf);
1336 return csize;
1337 quit:
1338 vmFreeLabelsUntilMark(NULL);
1339 quitbufonly:
1340 free(buf);
1341 return -1;
1345 ////////////////////////////////////////////////////////////////////////////////
1346 int vmSaveState (FILE *fl) {
1347 int lcnt = 0;
1349 fixLastThreadId();
1351 if (writeDW(fl, VM_VARS_SIZE) != 0) return -1;
1352 //if (writeDW(fl, VM_STACK_SIZE) != 0) return -1;
1353 if (writeDW(fl, vmLastThreadId) != 0) return -1;
1354 if (writeDW(fl, vmCodeSize) != 0) return -1;
1355 if (writeBuf(fl, vmCode, vmCodeSize) != 0) return -1;
1356 for (int f = 0; f < VM_VARS_SIZE; ++f) if (writeDW(fl, vmGVars[f]) != 0) return -1;
1357 for (int f = 0; f <= vmLastThreadId; ++f) {
1358 if (vmThreads[f].stack != NULL) {
1359 if (writeDW(fl, f) != 0) return -1;
1360 if (writeDW(fl, vmThreads[f].pc) != 0) return -1;
1361 if (writeDW(fl, vmThreads[f].suspended) != 0) return -1;
1362 if (writeDW(fl, vmThreads[f].sp) != 0) return -1;
1363 for (int c = 0; c < vmThreads[f].sp; ++c) if (writeDW(fl, vmThreads[f].stack[c]) != 0) return -1;
1364 for (int c = 0; c < VM_VARS_SIZE; ++c) if (writeDW(fl, vmThreads[f].tvars[c]) != 0) return -1;
1365 } else {
1366 if (writeDW(fl, -1) != 0) return -1;
1370 for (VMLabelInfo *l = labels; l != NULL; l = l->next) ++lcnt;
1371 if (writeDW(fl, lcnt) != 0) return -1;
1372 for (VMLabelInfo *l = labels; l != NULL; l = l->next) {
1373 int nlen = l->name != NULL ? strlen(l->name) : -1;
1375 if (writeDW(fl, l->type) != 0) return -1;
1376 if (writeDW(fl, nlen) != 0) return -1;
1377 if (nlen > 0) {
1378 if (writeBuf(fl, l->name, nlen) != 0) return -1;
1380 if (writeDW(fl, l->value) != 0) return -1;
1381 if (writeDW(fl, l->pub) != 0) return -1;
1384 return 0;
1388 int vmLoadState (FILE *fl) {
1389 int v, lcnt;
1391 vmDeinitialize();
1392 if (vmInitialize() != 0) goto fail;
1394 if (readDW(fl, &v) != 0 || v != VM_VARS_SIZE) goto fail;
1395 //if (readDW(fl, &ssz) != 0) goto fail;
1396 if (readDW(fl, &vmLastThreadId) != 0 || vmLastThreadId < 0 || vmLastThreadId > VM_MAX_THREADS) goto fail;
1397 if (readDW(fl, &vmCodeSize) != 0 || vmCodeSize < 1 || vmCodeSize > 65536) goto fail;
1398 if (readBuf(fl, vmCode, vmCodeSize) != 0) goto fail;
1399 for (int f = 0; f < VM_VARS_SIZE; ++f) if (readDW(fl, &vmGVars[f]) != 0) goto fail;
1400 for (int f = 0; f <= vmLastThreadId; ++f) {
1401 int flag;
1403 if (readDW(fl, &flag) != 0) goto fail;
1404 if (flag == -1) continue;
1405 if (flag != f) goto fail;
1406 if (vmInitThread(&vmThreads[f]) != 0) goto fail;
1407 if (readDW(fl, &vmThreads[f].pc) != 0) goto fail;
1408 if (readDW(fl, &vmThreads[f].suspended) != 0) goto fail;
1409 if (readDW(fl, &vmThreads[f].sp) != 0) goto fail;
1410 if (!vmThreads[f].suspended) {
1411 if (vmThreads[f].pc < 0 || vmThreads[f].pc >= vmCodeSize) goto fail;
1412 if (vmThreads[f].sp < 0 || vmThreads[f].sp > VM_STACK_SIZE) goto fail;
1414 for (int c = 0; c < vmThreads[f].sp; ++c) if (readDW(fl, &vmThreads[f].stack[c]) != 0) goto fail;
1415 for (int c = 0; c < VM_VARS_SIZE; ++c) if (readDW(fl, &vmThreads[f].tvars[c]) != 0) goto fail;
1418 if (readDW(fl, &lcnt) != 0) goto fail;
1419 for (; lcnt > 0; --lcnt) {
1420 int type, namelen, value, pub;
1421 char name[257];
1423 if (readDW(fl, &type) != 0) goto fail;
1424 if (readDW(fl, &namelen) != 0) goto fail;
1425 if (namelen > 255) goto fail;
1426 if (namelen >= 0) {
1427 if (readBuf(fl, name, namelen) != 0) goto fail;
1428 name[namelen] = 0;
1430 if (readDW(fl, &value) != 0) goto fail;
1431 if (readDW(fl, &pub) != 0) goto fail;
1432 vmAddLabelToEnd(namelen>=0 ? name : NULL, type, value, pub);
1434 return 0;
1435 fail:
1436 vmDeinitialize();
1437 return -1;