VM: extended BSR command
[awish.git] / src / vm.c
bloba4f26acd1d19402694e98b1ba1ad1e617c9f9506
1 #include <stdarg.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
6 #include "vm.h"
9 //#define VM_DEBUG
12 #ifndef VM_FATAL_DEFINED
13 static __attribute__((__noreturn__)) __attribute__((format(printf, 3, 4))) void vmfatal (int tid, int pc, const char *fmt, ...) {
14 va_list ap;
16 fprintf(stderr, "VM FATAL (thread #%d, pc=%04x): ", tid, (unsigned int)pc);
17 va_start(ap, fmt);
18 vfprintf(stderr, fmt, ap);
19 va_end(ap);
20 fprintf(stderr, "\n");
21 exit(1);
23 #endif
26 const char *vmOpNames[] = {
27 "add",
28 "sub",
29 "mul",
30 "div",
31 "mod",
32 "bor",
33 "xor",
34 "and",
36 "jeq",
37 "jne",
38 "jlt",
39 "jle",
40 "jgt",
41 "jge",
43 "jmp",
45 "bsr",
47 "brk",
48 "end",
49 "new",
51 "set",
52 "get",
54 "psh",
55 "pop",
56 "swp",
57 "pck",
58 "rol",
59 "dpt",
61 "tid",
62 "kil",
63 "sus",
64 "res",
65 "sta",
67 "rxc",
68 "wxc",
70 "ret",
72 "rst",
74 "mgf",
75 "mgb",
76 "msf",
77 "msb",
79 NULL
83 VMRSTCB vmRSTCB = NULL;
84 VMMapGetCB vmMapGetCB = NULL;
85 VMMapSetCB vmMapSetCB = NULL;
88 typedef struct {
89 int pc;
90 int suspended;
91 int sp;
92 int *stack; // internal
93 int *tvars; // internal
94 } VMThread;
97 static VMThread vmThreads[VM_MAX_THREADS];
98 static int vmExecuted[VM_MAX_THREADS];
99 static int vmLastThreadId = 0;
101 unsigned char vmCode[65536];
102 int vmCodeSize = 0;
103 int vmGVars[VM_VARS_SIZE];
106 static void fixLastThreadId (void) {
107 for (; vmLastThreadId > 0; --vmLastThreadId) if (vmThreads[vmLastThreadId].stack) break;
111 static int vmFindFreeThread (void) {
112 for (int f = 0; f < VM_MAX_THREADS; ++f) if (vmThreads[f].stack == NULL) return f;
113 return -1;
117 static int vmInitThread (VMThread *trd) {
118 trd->pc = 0;
119 trd->sp = 0;
120 trd->suspended = 0;
121 trd->stack = calloc(VM_STACK_SIZE, sizeof(int));
122 if (trd->stack == NULL) return -1;
123 trd->tvars = calloc(VM_VARS_SIZE, sizeof(int));
124 if (trd->tvars == NULL) { free(trd->stack); return -1; }
125 return 0;
129 static void vmFreeThread (VMThread *trd) {
130 if (trd->stack) free(trd->stack);
131 if (trd->tvars) free(trd->tvars);
132 trd->stack = NULL;
133 trd->tvars = NULL;
134 trd->pc = 0;
135 trd->sp = 0;
136 trd->suspended = 0;
140 int vmInitialize (void) {
141 memset(vmThreads, 0, sizeof(vmThreads));
142 // setup main thread
143 vmLastThreadId = 0;
144 if (vmInitThread(vmThreads)) return -1; // alas
145 return 0;
149 void vmDeinitialize (void) {
150 for (int f = 0; f < VM_MAX_THREADS; ++f) {
151 if (vmThreads[f].stack) free(vmThreads[f].stack);
152 if (vmThreads[f].tvars) free(vmThreads[f].tvars);
154 memset(vmThreads, 0, sizeof(vmThreads));
155 vmLastThreadId = 0;
159 static inline int vmGetByte (int tid, int opc, VMThread *trd) {
160 if (trd->pc < 0 || trd->pc >= vmCodeSize) vmfatal(tid, opc, "out of code");
161 return vmCode[trd->pc++];
165 #define STACK_WANT(n) do { if (trd->sp < (n)) vmfatal(tid, opc, "stack underflow"); } while (0)
166 #define STACK_FSPC(n) do { if (trd->sp+(n) > VM_STACK_SIZE) vmfatal(tid, opc, "stack overflow"); } while (0)
168 #define STACK_TOP() (trd->stack[trd->sp-1])
169 #define STACK_POP() (trd->stack[--(trd->sp)])
170 #define STACK_PUSH(n) trd->stack[(trd->sp)++] = (n)
173 #define MATH(op) do { \
174 switch (argc) { \
175 case 0: \
176 STACK_WANT(2); \
177 trd->stack[trd->sp-2] = (trd->stack[trd->sp-2]) op (trd->stack[trd->sp-1]); \
178 --(trd->sp); \
179 break; \
180 case 1: \
181 STACK_WANT(1); \
182 trd->stack[trd->sp-1] = (trd->stack[trd->sp-1]) op (argv[0]); \
183 break; \
184 case 2: if (argp[0] != NULL) *(argp[0]) = (argv[0]) op (argv[1]); break; \
185 case 3: if (argp[2] != NULL) *(argp[2]) = (argv[0]) op (argv[1]); break; \
187 } while(0)
190 #define CHECK_DIV_ZERO \
191 switch (argc) { \
192 case 0: \
193 if (trd->sp > 0 && trd->stack[trd->sp-1] == 0) { fprintf(stderr, "VM FATAL: division by zero!\n"); exit(1); } \
194 break; \
195 case 1: \
196 if (argv[0] == 0) { fprintf(stderr, "VM FATAL: division by zero!\n"); exit(1); } \
197 break; \
198 default: \
199 if (argv[1] == 0) { fprintf(stderr, "VM FATAL: division by zero!\n"); exit(1); } \
200 break; \
204 #define JXX(op) do { \
205 switch (argc) { \
206 case 0: \
207 STACK_WANT(3); \
208 argv[0] = STACK_POP(); \
209 case 1: \
210 STACK_WANT(2); \
211 argv[2] = STACK_POP(); \
212 argv[1] = STACK_POP(); \
213 break; \
214 case 2: \
215 STACK_WANT(1); \
216 argv[2] = argv[1]; \
217 argv[1] = STACK_POP(); \
218 break; \
220 if (argv[1] op argv[2]) trd->pc = argv[0]; \
221 } while (0)
224 static inline int isBranch (int opcode) {
225 return
226 opcode == VM_JMP ||
227 opcode == VM_BSR ||
228 opcode == VM_JEQ ||
229 opcode == VM_JNE ||
230 opcode == VM_JLT ||
231 opcode == VM_JLE ||
232 opcode == VM_JGT ||
233 opcode == VM_JGE;
237 // <0: BRK; >0: END
238 static int vmExecuteOne (int tid) {
239 VMThread *trd = &vmThreads[tid];
240 int opcode;
241 int argc;
242 int argv[3]; // argument values
243 int *argp[3]; // pointer to vars for each arg
244 int opc = trd->pc;
246 // decode instruction
247 opcode = vmGetByte(tid, opc, trd);
248 argc = (opcode>>6)&0x03;
249 opcode &= 0x3f;
250 argv[0] = argv[1] = argv[2] = 0;
251 argp[0] = argp[1] = argp[2] = NULL;
252 // read operands
253 if (tid > vmLastThreadId) vmLastThreadId = tid;
254 for (int f = 0; f < argc; ++f) {
255 int vn = vmGetByte(tid, opc, trd);
257 if (vn == 255) {
258 // immediate
259 argv[f] = vmGetByte(tid, opc, trd);
260 argv[f] |= vmGetByte(tid, opc, trd)<<8;
261 if (argv[f] >= 32768 && (f != 0 || !isBranch(opcode))) argv[f] -= 65536;
262 } else {
263 if (vn == 127) {
264 // stack var; <0: from stack top
265 argv[f] = vmGetByte(tid, opc, trd);
266 argv[f] |= (vmGetByte(tid, opc, trd)<<8);
267 if (argv[f] >= 32768) argv[f] -= 65536;
268 if (argv[f] < 0) argv[f] += trd->sp;
269 if (argv[f] < 0 || argv[f] >= VM_STACK_SIZE) vmfatal(tid, opc, "invalid stack offset");
270 argp[f] = trd->stack+argv[f];
271 } else {
272 // variable
273 argp[f] = vn&0x80 ? vmGVars : trd->tvars;
274 argp[f] += vn&0x7f;
276 argv[f] = *(argp[f]);
280 #ifdef VM_DEBUG
281 fprintf(stderr, "%04x: ", (unsigned int)opc);
282 if (opcode < sizeof(vmOpNames)/sizeof(char *)-1) fprintf(stderr, "%s", vmOpNames[opcode]); else fprintf(stderr, "BAD");
283 for (int f = 0; f < argc; ++f) {
284 fputc(' ', stderr);
285 if (argp[f]) {
286 unsigned int ofs;
288 fputc('[', stderr);
289 if (argp[f] >= trd->tvars && argp[f] < trd->tvars+VM_VARS_SIZE) {
290 ofs = (unsigned int)(argp[f]-trd->tvars);
291 } else if (argp[f] >= trd->stack && argp[f] < trd->stack+VM_VARS_SIZE) {
292 ofs = (unsigned int)(argp[f]-trd->stack);
293 fputc('.', stderr);
294 } else if (argp[f] >= vmGVars && argp[f] < vmGVars+VM_VARS_SIZE) {
295 ofs = (unsigned int)(argp[f]-vmGVars);
296 fputc('@', stderr);
298 fprintf(stderr, "%u]", ofs);
300 if (f == 0 && isBranch(opcode)) {
301 fprintf(stderr, "(0x%04x)", argv[f]);
302 } else {
303 fprintf(stderr, "(%d)", argv[f]);
306 fputc('\n', stderr);
307 #endif
309 switch (opcode) {
310 case VM_ADD: MATH(+); break;
311 case VM_SUB: MATH(-); break;
312 case VM_MUL: MATH(*); break;
313 case VM_DIV:
314 CHECK_DIV_ZERO
315 MATH(/);
316 break;
317 case VM_MOD:
318 CHECK_DIV_ZERO
319 MATH(%);
320 break;
321 case VM_BOR: MATH(|); break;
322 case VM_XOR: MATH(^); break;
323 case VM_AND: MATH(&); break;
325 case VM_JEQ: JXX(==); break;
326 case VM_JNE: JXX(!=); break;
327 case VM_JLT: JXX(<); break;
328 case VM_JLE: JXX(<=); break;
329 case VM_JGT: JXX(>); break;
330 case VM_JGE: JXX(>=); break;
332 case VM_JMP:
333 if (argc == 0) {
334 STACK_WANT(1);
335 argv[0] = STACK_POP();
337 trd->pc = argv[0];
338 break;
340 case VM_BSR:
341 switch (argc) {
342 case 0:
343 STACK_WANT(1);
344 argv[0] = STACK_POP();
345 break;
346 case 1:
347 break;
348 case 2:
349 STACK_FSPC(1);
350 STACK_PUSH(argv[1]);
351 break;
352 case 3:
353 STACK_FSPC(2);
354 STACK_PUSH(argv[1]);
355 STACK_PUSH(argv[2]);
356 break;
358 STACK_FSPC(1);
359 STACK_PUSH(trd->pc);
360 trd->pc = argv[0];
361 break;
363 case VM_END: return 1;
364 case VM_BRK: return -1;
366 case VM_NEW: { // new thread
367 int xtid = vmFindFreeThread();
369 if (argc == 0) {
370 STACK_WANT(1);
371 argv[0] = STACK_POP();
373 if (xtid >= 0) {
374 if (vmInitThread(&vmThreads[xtid]) == 0) {
375 vmThreads[xtid].pc = argv[0];
376 } else {
377 xtid = -1;
380 if (xtid < 0) vmfatal(tid, opc, "too many threads");
381 if (xtid > vmLastThreadId) vmLastThreadId = xtid;
382 if (argc > 1) {
383 if (argp[1]) *(argp[1]) = xtid;
384 } else {
385 STACK_FSPC(1);
386 STACK_PUSH(xtid);
388 break; }
390 case VM_SET:
391 switch (argc) {
392 case 0:
393 STACK_WANT(2);
394 argv[0] = STACK_POP(); // varid
395 argv[1] = STACK_POP(); // value
396 if (argv[0] < 0) {
397 argv[0] = -argv[0];
398 if (argv[0] < VM_VARS_SIZE) vmGVars[argv[0]] = argv[1];
399 } else if (argv[0] < VM_VARS_SIZE) {
400 trd->tvars[argv[0]] = argv[1];
402 break;
403 case 1:
404 STACK_WANT(1);
405 argv[1] = STACK_POP(); // value
406 // fallthru
407 case 2:
408 if (argp[0]) *(argp[0]) = argv[1];
409 break;
410 case 3: {
411 int xtid = argv[2], vid = argv[0];
413 if (xtid < 0 || xtid >= VM_MAX_THREADS || vmThreads[xtid].stack == NULL || vid < 0 || vid >= VM_VARS_SIZE) break;
414 vmThreads[xtid].tvars[vid] = argv[1];
415 break; }
417 break;
419 case VM_GET: {
420 int xtid, vid, pushit;
422 switch (argc) {
423 case 0:
424 STACK_WANT(2);
425 vid = STACK_POP();
426 xtid = STACK_POP();
427 pushit = 1;
428 break;
429 case 1:
430 STACK_WANT(1);
431 vid = argv[0];
432 xtid = STACK_POP();
433 pushit = 1;
434 break;
435 case 2:
436 xtid = argv[0];
437 vid = argv[1];
438 pushit = 1;
439 break;
440 default:
441 xtid = argv[0];
442 vid = argv[1];
443 pushit = 0;
444 break;
446 if (xtid >= 0 && xtid < VM_MAX_THREADS && vmThreads[xtid].stack != NULL && vid >= 0 && vid < VM_VARS_SIZE) {
447 argv[0] = vmThreads[xtid].tvars[vid];
448 } else {
449 argv[0] = 0;
451 if (!pushit) {
452 if (argp[2]) *(argp[2]) = argv[0];
453 } else {
454 STACK_FSPC(1);
455 STACK_PUSH(argv[0]);
457 break;
459 case VM_PSH:
460 if (argc > 0) {
461 STACK_FSPC(argc);
462 for (int f = 0; f < argc; ++f) STACK_PUSH(argv[f]);
463 } else {
464 // dup
465 STACK_WANT(1);
466 STACK_FSPC(1);
467 trd->stack[trd->sp] = trd->stack[trd->sp-1];
468 ++(trd->sp);
470 break;
471 case VM_POP:
472 switch (argc) {
473 case 0: // drop one
474 STACK_WANT(1);
475 --(trd->sp);
476 break;
477 case 1:
478 if (argp[0] == NULL && argv[0] < 0) {
479 // allocate stack space
480 argc = -argv[0];
481 STACK_FSPC(argc);
482 for (; argc > 0; --argc) STACK_PUSH(0);
483 break;
485 // fallthru
486 default:
487 for (int f = 0; f < argc; ++f) {
488 if (argp[f]) {
489 // pop
490 STACK_WANT(1);
491 *(argp[f]) = STACK_POP();
492 } else if (argv[f] > 0) {
493 // drop
494 STACK_WANT(argv[f]);
495 trd->sp -= argv[f];
498 break;
501 break;
502 case VM_SWP:
503 switch (argc) {
504 case 0:
505 STACK_WANT(2);
506 argc = trd->stack[trd->sp-2];
507 trd->stack[trd->sp-2] = trd->stack[trd->sp-1];
508 trd->stack[trd->sp-1] = argc;
509 break;
510 case 1:
511 STACK_WANT(1);
512 argc = trd->stack[trd->sp-1];
513 trd->stack[trd->sp-1] = argv[0];
514 if (argp[0]) *(argp[0]) = argc;
515 break;
516 default: // 2, 3
517 if (argp[0]) *(argp[0]) = argv[1];
518 if (argp[1]) *(argp[1]) = argv[0];
519 break;
521 break;
522 case VM_PCK:
523 case VM_ROL:
524 if (argc < 1) {
525 STACK_WANT(1);
526 argv[0] = STACK_POP();
528 // get item
529 if (argv[0] < 0) argv[0] += trd->sp;
530 if (argv[0] < 0 || argv[0] >= trd->sp) vmfatal(tid, opc, "invalid stack index (%d)", argv[0]);
531 argv[2] = trd->stack[argv[0]];
533 if (opcode == VM_ROL) {
534 for (int f = argv[0]+1; f < trd->sp; ++f) trd->stack[f-1] = trd->stack[f];
535 --(trd->sp);
538 switch (argc) {
539 case 0:
540 case 1:
541 STACK_FSPC(1);
542 STACK_PUSH(argv[2]);
543 break;
544 default:
545 if (argp[1]) *(argp[1]) = argv[2];
546 break;
548 break;
549 case VM_DPT:
550 if (argc > 0) {
551 if (argp[0]) *(argp[0]) = trd->sp;
552 } else {
553 STACK_FSPC(1);
554 argc = trd->sp;
555 STACK_PUSH(argc);
557 break;
559 case VM_TID:
560 if (argc > 0) {
561 if (argp[0]) *(argp[0]) = tid;
562 } else {
563 STACK_FSPC(1);
564 STACK_PUSH(tid);
566 break;
568 case VM_KIL:
569 case VM_SUS:
570 case VM_RES:
571 if (argc == 0) {
572 STACK_WANT(1);
573 argv[0] = STACK_POP();
575 if (argv[0] >= 0 && argv[0] < VM_MAX_THREADS && vmThreads[argv[0]].stack != NULL) {
576 switch (opcode) {
577 case VM_KIL:
578 if (argv[0] == tid) return 1;
579 vmFreeThread(&vmThreads[argv[0]]);
580 fixLastThreadId();
581 break;
582 case VM_SUS:
583 if (argv[0] == tid) return -1;
584 vmThreads[argv[0]].suspended = 1;
585 break;
586 case VM_RES:
587 vmThreads[argv[0]].suspended = 0;
588 break;
591 break;
593 case VM_STA:
594 if (argc == 0) {
595 STACK_WANT(1);
596 argv[0] = STACK_POP();
598 if (argv[0] >= 0 && argv[0] < VM_MAX_THREADS && vmThreads[argv[0]].stack != NULL) {
599 argv[0] = vmThreads[argv[0]].suspended;
600 } else {
601 argv[0] = -1;
603 if (argc > 1) {
604 if (argp[1]) *(argp[1]) = argv[0];
605 } else {
606 STACK_FSPC(1);
607 STACK_PUSH(argv[0]);
609 break;
611 case VM_RXC:
612 if (argc == 0) {
613 STACK_WANT(1);
614 argv[0] = STACK_POP();
616 if (argv[0] < 0 || argv[0] >= vmCodeSize) vmfatal(tid, opc, "invalid address in RXC (%d)", argv[0]);
617 if (argc < 2) {
618 STACK_FSPC(1);
619 STACK_PUSH(vmCode[argv[0]]);
620 } else if (argp[1]) {
621 *(argp[1]) = vmCode[argv[0]];
623 break;
625 case VM_WXC:
626 switch (argc) {
627 case 0:
628 STACK_WANT(2);
629 argv[1] = STACK_POP(); // byte
630 argv[0] = STACK_POP(); // address
631 break;
632 case 1:
633 STACK_WANT(1);
634 argv[1] = STACK_POP(); // byte
635 break;
637 if (argv[0] < 0 || argv[0] >= vmCodeSize) vmfatal(tid, opc, "invalid address in WXC (%d)", argv[0]);
638 if (argv[1] < -128 || argv[1] > 255) vmfatal(tid, opc, "invalid value in WXC (%d)", argv[1]);
639 if (argv[1] < 0) argv[1] &= 0xff;
640 vmCode[argv[0]] = argv[1];
641 break;
643 case VM_RET:
644 switch (argc) {
645 case 0: // simple
646 STACK_WANT(1);
647 trd->pc = STACK_POP();
648 break;
649 case 1:
650 vmfatal(tid, opc, "invalid RET");
651 case 2: // # of locals to pop, # of arguments to pop
652 case 3: // # of locals to pop, # of arguments to pop, retvalue (taken before all pops, pushes after returning)
653 if (argv[0] < 0) argv[0] = 0;
654 if (argv[1] < 0) argv[1] = 0;
655 STACK_WANT(argv[0]+argv[1]+1);
656 trd->sp -= argv[0]; // drop locals
657 trd->pc = STACK_POP();
658 trd->sp -= argv[1]; // drop arguments
659 if (argc == 3) {
660 // push result
661 STACK_FSPC(1);
662 STACK_PUSH(argv[2]);
664 break;
666 break;
668 case VM_RST:
669 if (vmRSTCB) {
670 if (argc == 0) {
671 STACK_WANT(1);
672 argv[0] = STACK_POP();
673 argc = 1;
675 vmRSTCB(tid, opcode, argc, argv, argp);
676 } else if (argc == 0) {
677 STACK_WANT(1);
678 --(trd->sp);
680 break;
682 case VM_MGF:
683 case VM_MGB:
684 switch (argc) {
685 case 0: // nothing
686 case 1: // only dest
687 STACK_WANT(2);
688 argv[1] = STACK_POP(); // y
689 argv[0] = STACK_POP(); // x
690 break;
692 if (vmMapGetCB) argv[0] = vmMapGetCB(tid, opcode==VM_MGF, argv[0], argv[1]); else argv[0] = 0;
693 if (argc == 3) {
694 if (argp[2]) *(argp[2]) = argv[0];
695 } else if (argc == 1) {
696 if (argp[0]) *(argp[0]) = argv[0];
697 } else {
698 STACK_FSPC(1);
699 STACK_PUSH(argv[0]);
701 break;
702 case VM_MSF:
703 case VM_MSB:
704 switch (argc) {
705 case 0:
706 STACK_WANT(2);
707 argv[2] = STACK_POP(); // tile
708 argv[1] = STACK_POP(); // y
709 argv[0] = STACK_POP(); // x
710 break;
711 case 1:
712 STACK_WANT(2);
713 argv[2] = argv[0]; // tile
714 argv[1] = STACK_POP(); // y
715 argv[0] = STACK_POP(); // x
716 break;
717 case 2:
718 STACK_WANT(1);
719 argv[2] = STACK_POP(); // tile
720 break;
722 if (vmMapSetCB) vmMapSetCB(tid, opcode==VM_MSF, argv[0], argv[1], argv[2]);
723 break;
725 default:
726 vmfatal(tid, opc, "invalid opcode (%d)", opcode);
728 return 0;
732 void vmExecuteAll (void) {
733 int runned;
735 memset(vmExecuted, 0, sizeof(vmExecuted));
736 do {
737 runned = 0;
738 for (int t = 0; t < VM_MAX_THREADS; ++t) {
739 if (!vmExecuted[t] && vmThreads[t].stack != NULL && !vmThreads[t].suspended) {
740 int f;
742 vmExecuted[t] = 1;
743 runned = 1;
744 for (f = 1000000; f >= 0; --f) {
745 int res = vmExecuteOne(t);
747 if (res < 0) {
748 // BRK
749 //fprintf(stderr, "!!!\n");
750 break;
752 if (res > 0) {
753 // END
754 vmFreeThread(&vmThreads[t]);
755 fixLastThreadId();
756 break;
759 if (f < 0) vmfatal(t, vmThreads[t].pc, "too many instructions in frame");
762 } while (runned);
766 int vmIsThreadAlive (int tid) {
767 if (tid >= 0 && tid < VM_MAX_THREADS) return vmThreads[tid].stack != NULL;
768 return 0;
772 int vmNewThread (int pc) {
773 int tid = vmFindFreeThread();
775 if (tid >= 0) {
776 if (vmInitThread(&vmThreads[tid]) != 0) return -1;
777 vmThreads[tid].pc = pc;
778 if (tid > vmLastThreadId) vmLastThreadId = tid;
780 return tid;
784 int vmKillThread (int tid) {
785 if (vmIsThreadAlive(tid)) {
786 vmFreeThread(&vmThreads[tid]);
787 fixLastThreadId();
788 return 1;
790 return 0;
794 int vmIsSuspendedThread (int tid) {
795 if (vmIsThreadAlive(tid)) return vmThreads[tid].suspended;
796 return 0;
800 int vmSuspendThread (int tid) {
801 if (vmIsThreadAlive(tid)) { vmThreads[tid].suspended = 1; return 0; }
802 return -1;
806 int vmResumeThread (int tid) {
807 if (vmIsThreadAlive(tid)) { vmThreads[tid].suspended = 0; return 0; }
808 return -1;
812 int vmGetTVar (int tid, int idx) {
813 if (idx >= 0 && idx < VM_VARS_SIZE && vmIsThreadAlive(tid)) return vmThreads[tid].tvars[idx];
814 return 0;
818 int vmSetTVar (int tid, int idx, int value) {
819 if (idx >= 0 && idx < VM_VARS_SIZE && vmIsThreadAlive(tid)) { vmThreads[tid].tvars[idx] = value; return 0; }
820 return -1;
824 int vmGetSP (int tid) {
825 if (vmIsThreadAlive(tid)) return vmThreads[tid].sp;
826 return -1;
830 int vmGetPC (int tid) {
831 if (vmIsThreadAlive(tid)) return vmThreads[tid].pc;
832 return -1;
836 int vmSetPC (int tid, int pc) {
837 if (vmIsThreadAlive(tid) && pc >= 0 && pc < vmCodeSize) { vmThreads[tid].pc = pc; return 0; }
838 return -1;
842 int vmSetSP (int tid, int value) {
843 if (vmIsThreadAlive(tid)) { vmThreads[tid].sp = value; return 0; }
844 return -1;
848 int vmGetStack (int tid, int idx) {
849 if (vmIsThreadAlive(tid)) {
850 if (idx < 0) idx += vmThreads[tid].sp;
851 if (idx >= 0 && idx < VM_STACK_SIZE) return vmThreads[tid].stack[idx];
853 return -1;
857 int vmSetStack (int tid, int idx, int value) {
858 if (vmIsThreadAlive(tid)) {
859 if (idx < 0) idx += vmThreads[tid].sp;
860 if (idx >= 0 && idx < VM_STACK_SIZE) { vmThreads[tid].stack[idx] = value; return 0; }
862 return -1;
866 int vmPush (int tid, int value) {
867 if (vmIsThreadAlive(tid) && vmThreads[tid].sp < VM_STACK_SIZE) {
868 vmThreads[tid].stack[vmThreads[tid].sp++] = value;
869 return 0;
871 return -1;
875 int vmPop (int tid) {
876 if (vmIsThreadAlive(tid) && vmThreads[tid].sp > 0) return vmThreads[tid].stack[--vmThreads[tid].sp];
877 return 0;
881 int vmLoadArgs (int tid, int argc, int argv[], int *argp[], int aargc, int aargv[], int *aargp[]) {
882 if (!vmIsThreadAlive(tid)) return -1;
883 if (argc > 0) {
884 int e = argc;
886 if (e > aargc) e = aargc;
887 for (int f = 0; f < e; ++f) { argv[f] = aargv[f]; argp[f] = aargp[f]; }
888 for (int f = e; f < argc; ++f) {
889 if (vmThreads[tid].sp <= 0) return -1;
890 argp[argc-f] = NULL;
891 argv[argc-f] = vmThreads[tid].stack[--vmThreads[tid].sp];
894 return 0;
898 int vmLastThread (void) {
899 return vmLastThreadId;