added some sounds
[awish.git] / src / vm.c
blobdb3a48a6dc98f1a54edac62750dd8942039d7ecc
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 int vmDebugTrace = 0;
134 FILE *vmDebugOutput = NULL;
137 static VMThread vmThreads[VM_MAX_THREADS];
138 static int vmExecuted[VM_MAX_THREADS];
139 static int vmLastThreadId = 0;
141 unsigned char vmCode[65536];
142 int vmCodeSize = 0;
143 int vmGVars[VM_VARS_SIZE];
146 static void fixLastThreadId (void) {
147 for (; vmLastThreadId > 0; --vmLastThreadId) if (vmThreads[vmLastThreadId].stack) break;
151 static int vmFindFreeThread (void) {
152 for (int f = 0; f < VM_MAX_THREADS; ++f) if (vmThreads[f].stack == NULL) return f;
153 return -1;
157 static int vmInitThread (VMThread *trd) {
158 trd->pc = 0;
159 trd->sp = 0;
160 trd->suspended = 0;
161 trd->stack = calloc(VM_STACK_SIZE, sizeof(int));
162 if (trd->stack == NULL) return -1;
163 trd->tvars = calloc(VM_VARS_SIZE, sizeof(int));
164 if (trd->tvars == NULL) { free(trd->stack); return -1; }
165 return 0;
169 static void vmFreeThread (VMThread *trd) {
170 if (trd->stack) free(trd->stack);
171 if (trd->tvars) free(trd->tvars);
172 trd->stack = NULL;
173 trd->tvars = NULL;
174 trd->pc = 0;
175 trd->sp = 0;
176 trd->suspended = 0;
180 int vmInitialize (void) {
181 vmFreeLabels();
182 memset(vmThreads, 0, sizeof(vmThreads));
183 // setup main thread
184 vmLastThreadId = 0;
185 if (vmInitThread(vmThreads)) return -1; // alas
186 return 0;
190 void vmDeinitialize (void) {
191 for (int f = 0; f < VM_MAX_THREADS; ++f) {
192 if (vmThreads[f].stack) free(vmThreads[f].stack);
193 if (vmThreads[f].tvars) free(vmThreads[f].tvars);
195 memset(vmThreads, 0, sizeof(vmThreads));
196 vmLastThreadId = 0;
197 vmFreeLabels();
201 static inline int vmGetByte (int tid, int opc, VMThread *trd) {
202 if (trd->pc < 0 || trd->pc >= vmCodeSize) vmfatal(tid, opc, "out of code");
203 return vmCode[trd->pc++];
207 #define STACK_WANT(n) do { if (trd->sp < (n)) vmfatal(tid, opc, "stack underflow"); } while (0)
208 #define STACK_FSPC(n) do { if (trd->sp+(n) > VM_STACK_SIZE) vmfatal(tid, opc, "stack overflow"); } while (0)
210 #define STACK_TOP() (trd->stack[trd->sp-1])
211 #define STACK_POP() (trd->stack[--(trd->sp)])
212 #define STACK_PUSH(n) trd->stack[(trd->sp)++] = (n)
215 #define MATH(op) do { \
216 switch (argc) { \
217 case 0: \
218 STACK_WANT(2); \
219 trd->stack[trd->sp-2] = (trd->stack[trd->sp-2]) op (trd->stack[trd->sp-1]); \
220 --(trd->sp); \
221 break; \
222 case 1: \
223 STACK_WANT(1); \
224 trd->stack[trd->sp-1] = (trd->stack[trd->sp-1]) op (argv[0]); \
225 break; \
226 case 2: if (argp[0] != NULL) *(argp[0]) = (argv[0]) op (argv[1]); break; \
227 case 3: if (argp[2] != NULL) *(argp[2]) = (argv[0]) op (argv[1]); break; \
229 } while(0)
232 #define CHECK_DIV_ZERO \
233 switch (argc) { \
234 case 0: \
235 if (trd->sp > 0 && trd->stack[trd->sp-1] == 0) { fprintf(stderr, "VM FATAL: division by zero!\n"); exit(1); } \
236 break; \
237 case 1: \
238 if (argv[0] == 0) { fprintf(stderr, "VM FATAL: division by zero!\n"); exit(1); } \
239 break; \
240 default: \
241 if (argv[1] == 0) { fprintf(stderr, "VM FATAL: division by zero!\n"); exit(1); } \
242 break; \
246 #define JXX(op) do { \
247 switch (argc) { \
248 case 0: \
249 STACK_WANT(3); \
250 argv[0] = STACK_POP(); \
251 case 1: \
252 STACK_WANT(2); \
253 argv[2] = STACK_POP(); \
254 argv[1] = STACK_POP(); \
255 break; \
256 case 2: \
257 STACK_WANT(1); \
258 argv[2] = argv[1]; \
259 argv[1] = STACK_POP(); \
260 break; \
262 if (argv[1] op argv[2]) trd->pc = argv[0]; \
263 } while (0)
266 static inline int isBranch (int opcode) {
267 return
268 opcode == VM_JMP ||
269 opcode == VM_BSR ||
270 opcode == VM_JEQ ||
271 opcode == VM_JNE ||
272 opcode == VM_JLT ||
273 opcode == VM_JLE ||
274 opcode == VM_JGT ||
275 opcode == VM_JGE;
279 // <0: BRK; >0: END
280 int vmExecuteOne (int tid) {
281 VMThread *trd = &vmThreads[tid];
282 int opcode;
283 int argc;
284 int argv[3]; // argument values
285 int *argp[3]; // pointer to vars for each arg
286 int opc = trd->pc;
288 // decode instruction
289 opcode = vmGetByte(tid, opc, trd);
290 argc = (opcode>>6)&0x03;
291 opcode &= 0x3f;
292 argv[0] = argv[1] = argv[2] = 0;
293 argp[0] = argp[1] = argp[2] = NULL;
294 // read operands
295 if (tid > vmLastThreadId) vmLastThreadId = tid;
296 for (int f = 0; f < argc; ++f) {
297 int vn = vmGetByte(tid, opc, trd);
299 if (vn == 255) {
300 // immediate
301 argv[f] = vmGetByte(tid, opc, trd);
302 argv[f] |= vmGetByte(tid, opc, trd)<<8;
303 if (argv[f] >= 32768 && (f != 0 || !isBranch(opcode))) argv[f] -= 65536;
304 } else {
305 if (vn == 127) {
306 // stack var; <0: from stack top
307 argv[f] = vmGetByte(tid, opc, trd);
308 argv[f] |= (vmGetByte(tid, opc, trd)<<8);
309 if (argv[f] >= 32768) argv[f] -= 65536;
310 if (argv[f] < 0) argv[f] += trd->sp;
311 if (argv[f] < 0 || argv[f] >= VM_STACK_SIZE) vmfatal(tid, opc, "invalid stack offset");
312 argp[f] = trd->stack+argv[f];
313 } else {
314 // variable
315 argp[f] = vn&0x80 ? vmGVars : trd->tvars;
316 argp[f] += vn&0x7f;
318 argv[f] = *(argp[f]);
322 //#ifdef VM_DEBUG
323 if (vmDebugTrace) {
324 if (vmDebugOutput == NULL) vmDebugOutput = stderr;
326 fprintf(vmDebugOutput, "(%04d) %04x: ", tid, (unsigned int)opc);
327 if (opcode < sizeof(vmOpNames)/sizeof(char *)-1) fprintf(vmDebugOutput, "%s", vmOpNames[opcode]); else fprintf(vmDebugOutput, "BAD");
328 for (int f = 0; f < argc; ++f) {
329 fputc(' ', vmDebugOutput);
330 if (argp[f]) {
331 unsigned int ofs = 0;
333 fputc('[', vmDebugOutput);
334 if (argp[f] >= trd->tvars && argp[f] < trd->tvars+VM_VARS_SIZE) {
335 ofs = (unsigned int)(argp[f]-trd->tvars);
336 } else if (argp[f] >= trd->stack && argp[f] < trd->stack+VM_VARS_SIZE) {
337 ofs = (unsigned int)(argp[f]-trd->stack);
338 fputc('.', vmDebugOutput);
339 } else if (argp[f] >= vmGVars && argp[f] < vmGVars+VM_VARS_SIZE) {
340 ofs = (unsigned int)(argp[f]-vmGVars);
341 fputc('@', vmDebugOutput);
343 fprintf(vmDebugOutput, "%u]", ofs);
345 if (f == 0 && isBranch(opcode)) {
346 fprintf(vmDebugOutput, "(0x%04x)", argv[f]);
347 } else {
348 fprintf(vmDebugOutput, "(%d)", argv[f]);
351 fputc('\n', vmDebugOutput);
352 if (vmDebugOutput != stderr && vmDebugOutput != stdout) fflush(vmDebugOutput);
354 //#endif
356 switch (opcode) {
357 case VM_ADD: MATH(+); break;
358 case VM_SUB: MATH(-); break;
359 case VM_MUL: MATH(*); break;
360 case VM_DIV:
361 CHECK_DIV_ZERO
362 MATH(/);
363 break;
364 case VM_MOD:
365 CHECK_DIV_ZERO
366 MATH(%);
367 break;
368 case VM_BOR: MATH(|); break;
369 case VM_XOR: MATH(^); break;
370 case VM_AND: MATH(&); break;
372 case VM_JEQ: JXX(==); break;
373 case VM_JNE: JXX(!=); break;
374 case VM_JLT: JXX(<); break;
375 case VM_JLE: JXX(<=); break;
376 case VM_JGT: JXX(>); break;
377 case VM_JGE: JXX(>=); break;
379 case VM_JMP:
380 if (argc == 0) {
381 STACK_WANT(1);
382 argv[0] = STACK_POP();
384 trd->pc = argv[0];
385 break;
387 case VM_BSR:
388 switch (argc) {
389 case 0:
390 STACK_WANT(1);
391 argv[0] = STACK_POP();
392 break;
393 case 1:
394 break;
395 case 2:
396 STACK_FSPC(1);
397 STACK_PUSH(argv[1]);
398 break;
399 case 3:
400 STACK_FSPC(2);
401 STACK_PUSH(argv[1]);
402 STACK_PUSH(argv[2]);
403 break;
405 STACK_FSPC(1);
406 STACK_PUSH(trd->pc);
407 trd->pc = argv[0];
408 break;
410 case VM_END: return 1;
411 case VM_BRK: return -1;
413 case VM_NEW: { // new thread
414 int xtid = vmFindFreeThread();
416 if (argc == 0) {
417 STACK_WANT(1);
418 argv[0] = STACK_POP();
420 if (xtid >= 0) {
421 if (vmInitThread(&vmThreads[xtid]) == 0) {
422 vmThreads[xtid].pc = argv[0];
423 } else {
424 xtid = -1;
427 if (xtid < 0) vmfatal(tid, opc, "too many threads");
428 if (xtid > vmLastThreadId) vmLastThreadId = xtid;
429 if (argc > 1) {
430 if (argp[1]) *(argp[1]) = xtid;
431 } else {
432 STACK_FSPC(1);
433 STACK_PUSH(xtid);
435 break; }
437 case VM_SET:
438 switch (argc) {
439 case 0:
440 STACK_WANT(2);
441 argv[0] = STACK_POP(); // varid
442 argv[1] = STACK_POP(); // value
443 if (argv[0] < 0) {
444 argv[0] = -argv[0];
445 if (argv[0] < VM_VARS_SIZE) vmGVars[argv[0]] = argv[1];
446 } else if (argv[0] < VM_VARS_SIZE) {
447 trd->tvars[argv[0]] = argv[1];
449 break;
450 case 1:
451 STACK_WANT(1);
452 argv[1] = STACK_POP(); // value
453 // fallthru
454 case 2:
455 if (argp[0]) *(argp[0]) = argv[1];
456 break;
457 case 3: {
458 int xtid = argv[2], vid = argv[0];
460 if (xtid < 0 || xtid >= VM_MAX_THREADS || vmThreads[xtid].stack == NULL || vid < 0 || vid >= VM_VARS_SIZE) break;
461 vmThreads[xtid].tvars[vid] = argv[1];
462 break; }
464 break;
466 case VM_GET: {
467 int xtid, vid, pushit;
469 switch (argc) {
470 case 0:
471 STACK_WANT(2);
472 vid = STACK_POP();
473 xtid = STACK_POP();
474 pushit = 1;
475 break;
476 case 1:
477 STACK_WANT(1);
478 vid = argv[0];
479 xtid = STACK_POP();
480 pushit = 1;
481 break;
482 case 2:
483 xtid = argv[0];
484 vid = argv[1];
485 pushit = 1;
486 break;
487 default:
488 xtid = argv[0];
489 vid = argv[1];
490 pushit = 0;
491 break;
493 if (xtid >= 0 && xtid < VM_MAX_THREADS && vmThreads[xtid].stack != NULL && vid >= 0 && vid < VM_VARS_SIZE) {
494 argv[0] = vmThreads[xtid].tvars[vid];
495 } else {
496 argv[0] = 0;
498 if (!pushit) {
499 if (argp[2]) *(argp[2]) = argv[0];
500 } else {
501 STACK_FSPC(1);
502 STACK_PUSH(argv[0]);
504 break;
506 case VM_PSH:
507 if (argc > 0) {
508 STACK_FSPC(argc);
509 for (int f = 0; f < argc; ++f) STACK_PUSH(argv[f]);
510 } else {
511 // dup
512 STACK_WANT(1);
513 STACK_FSPC(1);
514 trd->stack[trd->sp] = trd->stack[trd->sp-1];
515 ++(trd->sp);
517 break;
518 case VM_POP:
519 switch (argc) {
520 case 0: // drop one
521 STACK_WANT(1);
522 --(trd->sp);
523 break;
524 case 1:
525 if (argp[0] == NULL && argv[0] < 0) {
526 // allocate stack space
527 argc = -argv[0];
528 STACK_FSPC(argc);
529 for (; argc > 0; --argc) STACK_PUSH(0);
530 break;
532 // fallthru
533 default:
534 for (int f = 0; f < argc; ++f) {
535 if (argp[f]) {
536 // pop
537 STACK_WANT(1);
538 *(argp[f]) = STACK_POP();
539 } else if (argv[f] > 0) {
540 // drop
541 STACK_WANT(argv[f]);
542 trd->sp -= argv[f];
545 break;
548 break;
549 case VM_SWP:
550 switch (argc) {
551 case 0:
552 STACK_WANT(2);
553 argc = trd->stack[trd->sp-2];
554 trd->stack[trd->sp-2] = trd->stack[trd->sp-1];
555 trd->stack[trd->sp-1] = argc;
556 break;
557 case 1:
558 STACK_WANT(1);
559 argc = trd->stack[trd->sp-1];
560 trd->stack[trd->sp-1] = argv[0];
561 if (argp[0]) *(argp[0]) = argc;
562 break;
563 default: // 2, 3
564 if (argp[0]) *(argp[0]) = argv[1];
565 if (argp[1]) *(argp[1]) = argv[0];
566 break;
568 break;
569 case VM_PCK:
570 case VM_ROL:
571 if (argc < 1) {
572 STACK_WANT(1);
573 argv[0] = STACK_POP();
575 // get item
576 if (argv[0] < 0) argv[0] += trd->sp;
577 if (argv[0] < 0 || argv[0] >= trd->sp) vmfatal(tid, opc, "invalid stack index (%d)", argv[0]);
578 argv[2] = trd->stack[argv[0]];
580 if (opcode == VM_ROL) {
581 for (int f = argv[0]+1; f < trd->sp; ++f) trd->stack[f-1] = trd->stack[f];
582 --(trd->sp);
585 switch (argc) {
586 case 0:
587 case 1:
588 STACK_FSPC(1);
589 STACK_PUSH(argv[2]);
590 break;
591 default:
592 if (argp[1]) *(argp[1]) = argv[2];
593 break;
595 break;
596 case VM_DPT:
597 if (argc > 0) {
598 if (argp[0]) *(argp[0]) = trd->sp;
599 } else {
600 STACK_FSPC(1);
601 argc = trd->sp;
602 STACK_PUSH(argc);
604 break;
606 case VM_TID:
607 if (argc > 0) {
608 if (argp[0]) *(argp[0]) = tid;
609 } else {
610 STACK_FSPC(1);
611 STACK_PUSH(tid);
613 break;
615 case VM_KIL:
616 case VM_SUS:
617 case VM_RES:
618 if (argc == 0) {
619 STACK_WANT(1);
620 argv[0] = STACK_POP();
622 if (argv[0] >= 0 && argv[0] < VM_MAX_THREADS && vmThreads[argv[0]].stack != NULL) {
623 switch (opcode) {
624 case VM_KIL:
625 if (argv[0] == tid) return 1;
626 vmFreeThread(&vmThreads[argv[0]]);
627 fixLastThreadId();
628 break;
629 case VM_SUS:
630 if (argv[0] == tid) return -1;
631 vmThreads[argv[0]].suspended = 1;
632 break;
633 case VM_RES:
634 vmThreads[argv[0]].suspended = 0;
635 break;
638 break;
640 case VM_STA:
641 if (argc == 0) {
642 STACK_WANT(1);
643 argv[0] = STACK_POP();
645 if (argv[0] >= 0 && argv[0] < VM_MAX_THREADS && vmThreads[argv[0]].stack != NULL) {
646 argv[0] = vmThreads[argv[0]].suspended;
647 } else {
648 argv[0] = -1;
650 if (argc > 1) {
651 if (argp[1]) *(argp[1]) = argv[0];
652 } else {
653 STACK_FSPC(1);
654 STACK_PUSH(argv[0]);
656 break;
658 case VM_RXC:
659 if (argc == 0) {
660 STACK_WANT(1);
661 argv[0] = STACK_POP();
663 if (argv[0] < 0 || argv[0] >= vmCodeSize) vmfatal(tid, opc, "invalid address in RXC (%d)", argv[0]);
664 if (argc < 2) {
665 STACK_FSPC(1);
666 STACK_PUSH(vmCode[argv[0]]);
667 } else if (argp[1]) {
668 *(argp[1]) = vmCode[argv[0]];
670 break;
672 case VM_WXC:
673 switch (argc) {
674 case 0:
675 STACK_WANT(2);
676 argv[1] = STACK_POP(); // byte
677 argv[0] = STACK_POP(); // address
678 break;
679 case 1:
680 STACK_WANT(1);
681 argv[1] = STACK_POP(); // byte
682 break;
684 if (argv[0] < 0 || argv[0] >= vmCodeSize) vmfatal(tid, opc, "invalid address in WXC (%d)", argv[0]);
685 if (argv[1] < -128 || argv[1] > 255) vmfatal(tid, opc, "invalid value in WXC (%d)", argv[1]);
686 if (argv[1] < 0) argv[1] &= 0xff;
687 vmCode[argv[0]] = argv[1];
688 break;
690 case VM_RET:
691 switch (argc) {
692 case 0: // simple
693 STACK_WANT(1);
694 trd->pc = STACK_POP();
695 break;
696 case 1:
697 vmfatal(tid, opc, "invalid RET");
698 case 2: // # of locals to pop, # of arguments to pop
699 case 3: // # of locals to pop, # of arguments to pop, retvalue (taken before all pops, pushes after returning)
700 if (argv[0] < 0) argv[0] = 0;
701 if (argv[1] < 0) argv[1] = 0;
702 STACK_WANT(argv[0]+argv[1]+1);
703 trd->sp -= argv[0]; // drop locals
704 trd->pc = STACK_POP();
705 trd->sp -= argv[1]; // drop arguments
706 if (argc == 3) {
707 // push result
708 STACK_FSPC(1);
709 STACK_PUSH(argv[2]);
711 break;
713 break;
715 case VM_RST:
716 if (vmRSTCB) {
717 if (argc == 0) {
718 STACK_WANT(1);
719 argv[0] = STACK_POP();
720 argc = 1;
722 vmRSTCB(tid, opcode, argc, argv, argp);
723 } else if (argc == 0) {
724 STACK_WANT(1);
725 --(trd->sp);
727 break;
729 case VM_MGF:
730 case VM_MGB:
731 switch (argc) {
732 case 0: // nothing
733 case 1: // only dest
734 STACK_WANT(2);
735 argv[1] = STACK_POP(); // y
736 argv[0] = STACK_POP(); // x
737 break;
739 if (vmMapGetCB) argv[0] = vmMapGetCB(tid, opcode==VM_MGF, argv[0], argv[1]); else argv[0] = 0;
740 if (argc == 3) {
741 if (argp[2]) *(argp[2]) = argv[0];
742 } else if (argc == 1) {
743 if (argp[0]) *(argp[0]) = argv[0];
744 } else {
745 STACK_FSPC(1);
746 STACK_PUSH(argv[0]);
748 break;
749 case VM_MSF:
750 case VM_MSB:
751 switch (argc) {
752 case 0:
753 STACK_WANT(2);
754 argv[2] = STACK_POP(); // tile
755 argv[1] = STACK_POP(); // y
756 argv[0] = STACK_POP(); // x
757 break;
758 case 1:
759 STACK_WANT(2);
760 argv[2] = argv[0]; // tile
761 argv[1] = STACK_POP(); // y
762 argv[0] = STACK_POP(); // x
763 break;
764 case 2:
765 STACK_WANT(1);
766 argv[2] = STACK_POP(); // tile
767 break;
769 if (vmMapSetCB) vmMapSetCB(tid, opcode==VM_MSF, argv[0], argv[1], argv[2]);
770 break;
772 default:
773 vmfatal(tid, opc, "invalid opcode (%d)", opcode);
775 return 0;
779 void vmExecuteAll (void) {
780 int runned;
782 memset(vmExecuted, 0, sizeof(vmExecuted));
783 do {
784 runned = 0;
785 for (int t = 0; t < VM_MAX_THREADS; ++t) {
786 if (!vmExecuted[t] && vmThreads[t].stack != NULL && !vmThreads[t].suspended) {
787 int f;
789 vmExecuted[t] = 1;
790 runned = 1;
791 for (f = MAX_INST_COUNT; f >= 0; --f) {
792 int res = vmExecuteOne(t);
794 if (res < 0) {
795 // BRK
796 //fprintf(stderr, "!!!\n");
797 break;
799 if (res > 0) {
800 // END
801 vmFreeThread(&vmThreads[t]);
802 fixLastThreadId();
803 break;
806 if (f < 0) vmfatal(t, vmThreads[t].pc, "too many instructions in frame");
809 } while (runned);
813 // <0: BRK; >0: END; 0: ok
814 // maxinst<0: any number of instructions
815 int vmExecuteBSR (int tid, int pc, int maxinst) {
816 int opc;
818 if (tid < 0 || tid >= VM_MAX_THREADS || vmThreads[tid].stack == NULL || vmThreads[tid].suspended) return 2; // END
819 opc = vmThreads[tid].pc;
820 vmPush(tid, -666666);
821 vmThreads[tid].pc = pc;
822 if (maxinst == 0) maxinst = MAX_INST_COUNT;
823 for (;;) {
824 int res = vmExecuteOne(tid);
826 if (!vmThreads[tid].stack) return 1;
827 if (vmThreads[tid].suspended) return -2;
828 if (res > 0) vmFreeThread(&vmThreads[tid]);
829 if (res != 0) return res;
830 if (vmThreads[tid].pc == -666666) {
831 vmThreads[tid].pc = opc;
832 return 0;
834 if (maxinst == 1) vmfatal(tid, vmThreads[tid].pc, "too many instructions in vmExecuteBSR");
835 if (maxinst > 0) --maxinst;
840 int vmIsThreadAlive (int tid) {
841 if (tid >= 0 && tid < VM_MAX_THREADS) return vmThreads[tid].stack != NULL;
842 return 0;
846 int vmNewThread (int pc) {
847 int tid = vmFindFreeThread();
849 if (tid >= 0) {
850 if (vmInitThread(&vmThreads[tid]) != 0) return -1;
851 vmThreads[tid].pc = pc;
852 if (tid > vmLastThreadId) vmLastThreadId = tid;
854 return tid;
858 int vmKillThread (int tid) {
859 if (vmIsThreadAlive(tid)) {
860 vmFreeThread(&vmThreads[tid]);
861 fixLastThreadId();
862 return 1;
864 return 0;
868 int vmIsSuspendedThread (int tid) {
869 if (vmIsThreadAlive(tid)) return vmThreads[tid].suspended;
870 return 0;
874 int vmSuspendThread (int tid) {
875 if (vmIsThreadAlive(tid)) { vmThreads[tid].suspended = 1; return 0; }
876 return -1;
880 int vmResumeThread (int tid) {
881 if (vmIsThreadAlive(tid)) { vmThreads[tid].suspended = 0; return 0; }
882 return -1;
886 int vmGetTVar (int tid, int idx) {
887 if (idx >= 0 && idx < VM_VARS_SIZE && vmIsThreadAlive(tid)) return vmThreads[tid].tvars[idx];
888 return 0;
892 int vmSetTVar (int tid, int idx, int value) {
893 if (idx >= 0 && idx < VM_VARS_SIZE && vmIsThreadAlive(tid)) { vmThreads[tid].tvars[idx] = value; return 0; }
894 return -1;
898 int vmGetSP (int tid) {
899 if (vmIsThreadAlive(tid)) return vmThreads[tid].sp;
900 return -1;
904 int vmGetPC (int tid) {
905 if (vmIsThreadAlive(tid)) return vmThreads[tid].pc;
906 return -1;
910 int vmSetPC (int tid, int pc) {
911 if (vmIsThreadAlive(tid) && pc >= 0 && pc < vmCodeSize) { vmThreads[tid].pc = pc; return 0; }
912 return -1;
916 int vmSetSP (int tid, int value) {
917 if (vmIsThreadAlive(tid)) { vmThreads[tid].sp = value; return 0; }
918 return -1;
922 int vmGetStack (int tid, int idx) {
923 if (vmIsThreadAlive(tid)) {
924 if (idx < 0) idx += vmThreads[tid].sp;
925 if (idx >= 0 && idx < VM_STACK_SIZE) return vmThreads[tid].stack[idx];
927 return -1;
931 int vmSetStack (int tid, int idx, int value) {
932 if (vmIsThreadAlive(tid)) {
933 if (idx < 0) idx += vmThreads[tid].sp;
934 if (idx >= 0 && idx < VM_STACK_SIZE) { vmThreads[tid].stack[idx] = value; return 0; }
936 return -1;
940 int vmPush (int tid, int value) {
941 if (vmIsThreadAlive(tid) && vmThreads[tid].sp < VM_STACK_SIZE) {
942 vmThreads[tid].stack[vmThreads[tid].sp++] = value;
943 return 0;
945 return -1;
949 int vmPop (int tid) {
950 if (vmIsThreadAlive(tid) && vmThreads[tid].sp > 0) return vmThreads[tid].stack[--vmThreads[tid].sp];
951 return 0;
955 int vmLoadArgs (int tid, int argc, int argv[], int *argp[], int aargc, int aargv[], int *aargp[]) {
956 if (!vmIsThreadAlive(tid)) return -1;
957 if (argc > 0) {
958 int e = argc;
960 if (e > aargc) e = aargc;
961 for (int f = 0; f < e; ++f) { argv[f] = aargv[f]; argp[f] = aargp[f]; }
962 for (int f = e; f < argc; ++f) {
963 if (vmThreads[tid].sp <= 0) return -1;
964 argp[argc-f] = NULL;
965 argv[argc-f] = vmThreads[tid].stack[--vmThreads[tid].sp];
968 return 0;
972 int vmLastThread (void) {
973 return vmLastThreadId;
977 static int readBuf (FILE *fl, void *buf, int len) {
978 unsigned char *c = (unsigned char *)buf;
980 while (len-- > 0) {
981 unsigned char b;
983 if (fread(&b, 1, 1, fl) != 1) return -1;
984 b ^= SECRET;
985 *c++ = b;
987 return 0;
991 static int writeBuf (FILE *fl, const void *buf, int len) {
992 const unsigned char *c = (const unsigned char *)buf;
994 while (len-- > 0) {
995 unsigned char b = *c++;
997 b ^= SECRET;
998 if (fwrite(&b, 1, 1, fl) != 1) return -1;
1000 return 0;
1004 static int readDW (FILE *fl, int *v) {
1005 int t[4];
1007 for (int f = 0; f < 4; ++f) {
1008 unsigned char b;
1010 if (fread(&b, 1, 1, fl) != 1) return -1;
1011 t[f] = b^SECRET;
1014 if (v) {
1015 *v = 0;
1016 for (int f = 0; f < 4; ++f) *v |= t[f]<<(8*f);
1018 return 0;
1022 static int writeDW (FILE *fl, int v) {
1023 for (int f = 0; f < 4; ++f) {
1024 unsigned char b;
1026 b = v&0xff;
1027 b ^= SECRET;
1028 if (fwrite(&b, 1, 1, fl) != 1) return -1;
1029 v >>= 8;
1031 return 0;
1035 ////////////////////////////////////////////////////////////////////////////////
1036 static VMLabelInfo *labels = NULL;
1039 ////////////////////////////////////////////////////////////////////////////////
1040 void vmFreeLabels (void) {
1041 while (labels != NULL) {
1042 VMLabelInfo *l = labels;
1044 labels = l->next;
1045 if (l->name) free(l->name);
1046 free(l);
1051 void vmFreeLabelsUntilMark (const char *name) {
1052 while (labels != NULL) {
1053 VMLabelInfo *l = labels;
1054 int isMark = (l->type == LB_MARK);
1056 if (!(isMark && ((name == NULL && l->name == NULL) || (l->name != NULL && name != NULL && strcmp(name, l->name) == 0)))) isMark = 0;
1057 labels = l->next;
1058 if (l->name) free(l->name);
1059 free(l);
1060 if (isMark) break;
1065 VMLabelInfo *vmLabelAddMark (const char *name) {
1066 VMLabelInfo *l = malloc(sizeof(VMLabelInfo));
1068 if (l == NULL) fatalis("out of memory");
1069 l->name = NULL;
1070 if (name != NULL) {
1071 l->name = strdup(name);
1072 if (l->name == NULL) fatalis("out of memory");
1074 l->type = LB_MARK;
1075 l->value = -1;
1076 l->next = labels;
1077 l->pub = 0;
1078 labels = l;
1079 return l;
1083 VMLabelInfo *vmAddLabel (const char *name, int type, int value, int pub) {
1084 if (name != NULL && name[0]) {
1085 VMLabelInfo *l = malloc(sizeof(VMLabelInfo));
1087 if (l == NULL) fatalis("out of memory");
1088 if (name != NULL) {
1089 l->name = strdup(name);
1090 if (l->name == NULL) fatalis("out of memory");
1091 } else {
1092 l->name = NULL;
1094 l->type = type;
1095 l->value = value;
1096 l->pub = pub;
1097 l->next = labels;
1098 labels = l;
1099 return l;
1101 return NULL;
1105 static VMLabelInfo *vmAddLabelToEnd (const char *name, int type, int value, int pub) {
1106 if (name != NULL && name[0]) {
1107 VMLabelInfo *l = malloc(sizeof(VMLabelInfo)), *last = NULL;
1109 if (l == NULL) fatalis("out of memory");
1110 if (name != NULL) {
1111 l->name = strdup(name);
1112 if (l->name == NULL) fatalis("out of memory");
1113 } else {
1114 l->name = NULL;
1116 l->type = type;
1117 l->value = value;
1118 l->pub = pub;
1119 l->next = NULL;
1120 if (labels == NULL) {
1121 labels = l;
1122 } else {
1123 for (last = labels; last->next != NULL; last = last->next) ;
1124 last->next = l;
1126 return l;
1128 return NULL;
1132 VMLabelInfo *vmFindLabel (const char *name) {
1133 if (name != NULL && name[0]) {
1134 for (VMLabelInfo *l = labels; l != NULL; l = l->next) {
1135 if (l->name && l->name[0] && strcasecmp(l->name, name) == 0) return l;
1138 return NULL;
1142 int vmFindPC (const char *name) {
1143 VMLabelInfo *l = vmFindLabel(name);
1145 if (l == NULL) fatalis("no code label: '%s'", name);
1146 return l->value;
1150 int vmFindVarIndex (const char *name) {
1151 VMLabelInfo *l = vmFindLabel(name);
1153 if (l == NULL || (l->type != LB_GVAR && l->type != LB_TVAR && l->type != LB_SVAR)) fatalis("no var label: '%s'", name);
1154 return l->value;
1158 int vmFindGVarIndex (const char *name) {
1159 VMLabelInfo *l = vmFindLabel(name);
1161 if (l == NULL || l->type != LB_GVAR) fatalis("no global var label: '%s'", name);
1162 return l->value;
1166 int vmFindTVarIndex (const char *name) {
1167 VMLabelInfo *l = vmFindLabel(name);
1169 if (l == NULL || l->type != LB_TVAR) fatalis("no thread var label: '%s'", name);
1170 return l->value;
1174 int vmFindSVarIndex (const char *name) {
1175 VMLabelInfo *l = vmFindLabel(name);
1177 if (l == NULL || l->type != LB_SVAR) fatalis("no thread var label: '%s'", name);
1178 return l->value;
1182 int vmFindConst (const char *name) {
1183 VMLabelInfo *l = vmFindLabel(name);
1185 if (l == NULL || l->type != LB_CONST) fatalis("no const label: '%s'", name);
1186 return l->value;
1190 VMLabelInfo *vmFindMark (const char *name) {
1191 for (VMLabelInfo *l = labels; l != NULL; l = l->next) {
1192 if (l->type != LB_MARK) continue;
1193 if (name == NULL && l->name == NULL) return l;
1194 if (name != NULL && l->name != NULL && strcmp(name, l->name) == 0) return l;
1196 return NULL;
1200 #define XREAD(dest,size,errlabel) do { \
1201 if (pos+(size) > rsz) goto errlabel; \
1202 if ((size) > 0) memcpy((dest), buf+pos, (size)); \
1203 pos += (size); \
1204 } while (0)
1207 #define XREADB(dest,errlabel) do { \
1208 if (pos+1 > rsz) goto errlabel; \
1209 (dest) = buf[pos]; \
1210 ++pos; \
1211 } while (0)
1214 #define XREADW(dest,errlabel) do { \
1215 if (pos+2 > rsz) goto errlabel; \
1216 (dest) = buf[pos+1]; \
1217 (dest) <<= 8; \
1218 (dest) |= buf[pos+0]; \
1219 pos += 2; \
1220 } while (0)
1224 extern int vmLoadCodeFileFromDump (const void *data, int datasize, int pc) {
1225 int rsz = datasize, pos = 4;
1226 const uint8_t *dta = (const uint8_t *)data;
1227 uint8_t *buf;
1228 int csize, lcnt, rcnt, elcnt;
1230 if (rsz < 4) return -1;
1231 if (memcmp(data, "AVM1", 4) != 0) goto quitbufonly;
1232 buf = malloc(rsz);
1233 if (buf == NULL) return 1;
1235 for (int f = 4; f < rsz; ++f) buf[f] = dta[f]^SECRET;
1237 XREADW(csize, quitbufonly);
1238 XREADW(rcnt, quitbufonly); // fixups
1239 XREADW(elcnt, quitbufonly); // extern labels
1240 XREADW(lcnt, quitbufonly); // labels
1241 if (pc < 0 || pc+csize > 65535) goto quitbufonly;
1243 /*if (goobers)*/ fprintf(stderr, "code: %d bytes, %d public labels, %d relocations, %d externs\n", csize, lcnt, rcnt, elcnt);
1245 XREAD(vmCode+pc, csize, quitbufonly);
1247 // do relocations
1248 for (int f = 0; f < rcnt; ++f) {
1249 int rel, newpc, val;
1251 XREADW(rel, quitbufonly);
1252 if (rel < 0 || rel+1 >= csize) goto quitbufonly;
1253 newpc = pc+rel; // fix here
1254 // get old value
1255 val = vmCode[newpc+1];
1256 val <<= 8;
1257 val |= vmCode[newpc+0];
1258 // fix it
1259 val += pc;
1260 // set new value
1261 vmCode[newpc+0] = val&0xff;
1262 vmCode[newpc+1] = (val>>8)&0xff;
1265 // load extern labels and perform fixups
1266 for (int f = 0; f < elcnt; ++f) {
1267 char name[258];
1268 int type, namelen;
1269 int rcnt;
1271 XREADB(type, quitbufonly);
1272 XREADB(namelen, quitbufonly);
1273 XREAD(name, namelen, quitbufonly);
1274 XREADW(rcnt, quitbufonly);
1276 if (type < LB_MIN_TYPE || type > LB_MAX_TYPE) goto quitbufonly;
1277 if (namelen < 1) goto quitbufonly;
1278 name[namelen] = 0;
1280 for (int c = 0; c < rcnt; ++c) {
1281 int xpc, size, val;
1282 VMLabelInfo *l;
1284 XREADB(size, quitbufonly);
1285 XREADW(xpc, quitbufonly);
1287 if (size != 1 && size != 2) goto quitbufonly;
1288 if (xpc < 0 || xpc+size > csize) goto quitbufonly;
1290 xpc += pc;
1291 l = vmFindLabel(name);
1292 if (l == NULL) {
1293 /*if (goobers)*/ fprintf(stderr, "VM: unknown extern: '%s'\n", name);
1294 goto quitbufonly;
1296 val = l->value;
1297 if (l->type == LB_GVAR) val |= 0x80;
1298 //fprintf(stderr, "%d: [%s]: ofs=%d, size=%d, value=%d (%d)\n", c, l->name, xpc, size, val, vmCode[xpc]);
1299 if (size == 1 && (val < -128 || val > 255)) {
1300 /*if (goobers)*/ fprintf(stderr, "VM: extern too big: '%s'\n", name);
1301 goto quitbufonly;
1303 vmCode[xpc+0] = val&0xff;
1304 if (size == 2) vmCode[xpc+1] = (val>>8)&0xff;
1308 vmLabelAddMark(NULL); // for this code labels
1309 for (int f = 0; f < lcnt; ++f) {
1310 unsigned char type, namelen, b;
1311 char name[258];
1312 int value;
1314 XREADB(type, quit);
1316 switch (type&0x7f) {
1317 case LB_GVAR:
1318 case LB_TVAR:
1319 XREADB(b, quit);
1320 value = b;
1321 break;
1322 case LB_SVAR:
1323 case LB_CONST:
1324 XREADW(value, quit);
1325 if (value >= 32768) value -= 65536;
1326 break;
1327 case LB_CODE:
1328 XREADW(value, quit);
1329 value += pc; // fixup
1330 break;
1331 default:
1332 fatalis("invalid label type: %d\n", type);
1333 goto quit;
1336 XREADB(namelen, quit);
1337 XREAD(name, namelen, quit);
1338 name[namelen] = 0;
1340 if (namelen > 0) vmAddLabel(name, type&0x7f, value, type&0x80?1:0);
1341 //if (l->type == LB_CODE) printf("%d/%d: [%s] %d : %d\n", f, lcnt, l->name, l->value-pc, l->value);
1344 free(buf);
1345 return csize;
1346 quit:
1347 vmFreeLabelsUntilMark(NULL);
1348 quitbufonly:
1349 free(buf);
1350 return -1;
1354 ////////////////////////////////////////////////////////////////////////////////
1355 int vmSaveState (FILE *fl) {
1356 int lcnt = 0;
1358 fixLastThreadId();
1360 if (writeDW(fl, VM_VARS_SIZE) != 0) return -1;
1361 //if (writeDW(fl, VM_STACK_SIZE) != 0) return -1;
1362 if (writeDW(fl, vmLastThreadId) != 0) return -1;
1363 if (writeDW(fl, vmCodeSize) != 0) return -1;
1364 if (writeBuf(fl, vmCode, vmCodeSize) != 0) return -1;
1365 for (int f = 0; f < VM_VARS_SIZE; ++f) if (writeDW(fl, vmGVars[f]) != 0) return -1;
1366 for (int f = 0; f <= vmLastThreadId; ++f) {
1367 if (vmThreads[f].stack != NULL) {
1368 if (writeDW(fl, f) != 0) return -1;
1369 if (writeDW(fl, vmThreads[f].pc) != 0) return -1;
1370 if (writeDW(fl, vmThreads[f].suspended) != 0) return -1;
1371 if (writeDW(fl, vmThreads[f].sp) != 0) return -1;
1372 for (int c = 0; c < vmThreads[f].sp; ++c) if (writeDW(fl, vmThreads[f].stack[c]) != 0) return -1;
1373 for (int c = 0; c < VM_VARS_SIZE; ++c) if (writeDW(fl, vmThreads[f].tvars[c]) != 0) return -1;
1374 } else {
1375 if (writeDW(fl, -1) != 0) return -1;
1379 for (VMLabelInfo *l = labels; l != NULL; l = l->next) ++lcnt;
1380 if (writeDW(fl, lcnt) != 0) return -1;
1381 for (VMLabelInfo *l = labels; l != NULL; l = l->next) {
1382 int nlen = l->name != NULL ? strlen(l->name) : -1;
1384 if (writeDW(fl, l->type) != 0) return -1;
1385 if (writeDW(fl, nlen) != 0) return -1;
1386 if (nlen > 0) {
1387 if (writeBuf(fl, l->name, nlen) != 0) return -1;
1389 if (writeDW(fl, l->value) != 0) return -1;
1390 if (writeDW(fl, l->pub) != 0) return -1;
1393 return 0;
1397 int vmLoadState (FILE *fl) {
1398 int v, lcnt;
1400 vmDeinitialize();
1401 if (vmInitialize() != 0) goto fail;
1403 if (readDW(fl, &v) != 0 || v != VM_VARS_SIZE) goto fail;
1404 //if (readDW(fl, &ssz) != 0) goto fail;
1405 if (readDW(fl, &vmLastThreadId) != 0 || vmLastThreadId < 0 || vmLastThreadId > VM_MAX_THREADS) goto fail;
1406 if (readDW(fl, &vmCodeSize) != 0 || vmCodeSize < 1 || vmCodeSize > 65536) goto fail;
1407 if (readBuf(fl, vmCode, vmCodeSize) != 0) goto fail;
1408 for (int f = 0; f < VM_VARS_SIZE; ++f) if (readDW(fl, &vmGVars[f]) != 0) goto fail;
1409 for (int f = 0; f <= vmLastThreadId; ++f) {
1410 int flag;
1412 if (readDW(fl, &flag) != 0) goto fail;
1413 if (flag == -1) continue;
1414 if (flag != f) goto fail;
1415 if (vmInitThread(&vmThreads[f]) != 0) goto fail;
1416 if (readDW(fl, &vmThreads[f].pc) != 0) goto fail;
1417 if (readDW(fl, &vmThreads[f].suspended) != 0) goto fail;
1418 if (readDW(fl, &vmThreads[f].sp) != 0) goto fail;
1419 if (!vmThreads[f].suspended) {
1420 if (vmThreads[f].pc < 0 || vmThreads[f].pc >= vmCodeSize) goto fail;
1421 if (vmThreads[f].sp < 0 || vmThreads[f].sp > VM_STACK_SIZE) goto fail;
1423 for (int c = 0; c < vmThreads[f].sp; ++c) if (readDW(fl, &vmThreads[f].stack[c]) != 0) goto fail;
1424 for (int c = 0; c < VM_VARS_SIZE; ++c) if (readDW(fl, &vmThreads[f].tvars[c]) != 0) goto fail;
1427 if (readDW(fl, &lcnt) != 0) goto fail;
1428 for (; lcnt > 0; --lcnt) {
1429 int type, namelen, value, pub;
1430 char name[257];
1432 if (readDW(fl, &type) != 0) goto fail;
1433 if (readDW(fl, &namelen) != 0) goto fail;
1434 if (namelen > 255) goto fail;
1435 if (namelen >= 0) {
1436 if (readBuf(fl, name, namelen) != 0) goto fail;
1437 name[namelen] = 0;
1439 if (readDW(fl, &value) != 0) goto fail;
1440 if (readDW(fl, &pub) != 0) goto fail;
1441 vmAddLabelToEnd(namelen>=0 ? name : NULL, type, value, pub);
1443 return 0;
1444 fail:
1445 vmDeinitialize();
1446 return -1;