12 #ifndef VM_FATAL_DEFINED
13 static __attribute__((__noreturn__
)) __attribute__((format(printf
, 3, 4))) void vmfatal (int tid
, int pc
, const char *fmt
, ...) {
16 fprintf(stderr
, "VM FATAL (thread #%d, pc=%04x): ", tid
, (unsigned int)pc
);
18 vfprintf(stderr
, fmt
, ap
);
20 fprintf(stderr
, "\n");
26 const char *vmOpNames
[] = {
83 VMRSTCB vmRSTCB
= NULL
;
84 VMMapGetCB vmMapGetCB
= NULL
;
85 VMMapSetCB vmMapSetCB
= NULL
;
92 int *stack
; // internal
93 int *tvars
; // internal
97 static VMThread vmThreads
[VM_MAX_THREADS
];
98 static int vmExecuted
[VM_MAX_THREADS
];
99 static int vmLastThreadId
= 0;
101 unsigned char vmCode
[65536];
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
;
117 static int vmInitThread (VMThread
*trd
) {
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; }
129 static void vmFreeThread (VMThread
*trd
) {
130 if (trd
->stack
) free(trd
->stack
);
131 if (trd
->tvars
) free(trd
->tvars
);
140 int vmInitialize (void) {
141 memset(vmThreads
, 0, sizeof(vmThreads
));
144 if (vmInitThread(vmThreads
)) return -1; // alas
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
));
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 { \
177 trd->stack[trd->sp-2] = (trd->stack[trd->sp-2]) op (trd->stack[trd->sp-1]); \
182 trd->stack[trd->sp-1] = (trd->stack[trd->sp-1]) op (argv[0]); \
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; \
190 #define CHECK_DIV_ZERO \
193 if (trd->sp > 0 && trd->stack[trd->sp-1] == 0) { fprintf(stderr, "VM FATAL: division by zero!\n"); exit(1); } \
196 if (argv[0] == 0) { fprintf(stderr, "VM FATAL: division by zero!\n"); exit(1); } \
199 if (argv[1] == 0) { fprintf(stderr, "VM FATAL: division by zero!\n"); exit(1); } \
204 #define JXX(op) do { \
208 argv[0] = STACK_POP(); \
211 argv[2] = STACK_POP(); \
212 argv[1] = STACK_POP(); \
217 argv[1] = STACK_POP(); \
220 if (argv[1] op argv[2]) trd->pc = argv[0]; \
224 static inline int isBranch (int opcode
) {
238 static int vmExecuteOne (int tid
) {
239 VMThread
*trd
= &vmThreads
[tid
];
242 int argv
[3]; // argument values
243 int *argp
[3]; // pointer to vars for each arg
246 // decode instruction
247 opcode
= vmGetByte(tid
, opc
, trd
);
248 argc
= (opcode
>>6)&0x03;
250 argv
[0] = argv
[1] = argv
[2] = 0;
251 argp
[0] = argp
[1] = argp
[2] = NULL
;
253 if (tid
> vmLastThreadId
) vmLastThreadId
= tid
;
254 for (int f
= 0; f
< argc
; ++f
) {
255 int vn
= vmGetByte(tid
, opc
, trd
);
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;
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
];
273 argp
[f
] = vn
&0x80 ? vmGVars
: trd
->tvars
;
276 argv
[f
] = *(argp
[f
]);
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
) {
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
);
294 } else if (argp
[f
] >= vmGVars
&& argp
[f
] < vmGVars
+VM_VARS_SIZE
) {
295 ofs
= (unsigned int)(argp
[f
]-vmGVars
);
298 fprintf(stderr
, "%u]", ofs
);
300 if (f
== 0 && isBranch(opcode
)) {
301 fprintf(stderr
, "(0x%04x)", argv
[f
]);
303 fprintf(stderr
, "(%d)", argv
[f
]);
310 case VM_ADD
: MATH(+); break;
311 case VM_SUB
: MATH(-); break;
312 case VM_MUL
: MATH(*); 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;
335 argv
[0] = STACK_POP();
344 argv
[0] = STACK_POP();
363 case VM_END
: return 1;
364 case VM_BRK
: return -1;
366 case VM_NEW
: { // new thread
367 int xtid
= vmFindFreeThread();
371 argv
[0] = STACK_POP();
374 if (vmInitThread(&vmThreads
[xtid
]) == 0) {
375 vmThreads
[xtid
].pc
= argv
[0];
380 if (xtid
< 0) vmfatal(tid
, opc
, "too many threads");
381 if (xtid
> vmLastThreadId
) vmLastThreadId
= xtid
;
383 if (argp
[1]) *(argp
[1]) = xtid
;
394 argv
[0] = STACK_POP(); // varid
395 argv
[1] = STACK_POP(); // value
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];
405 argv
[1] = STACK_POP(); // value
408 if (argp
[0]) *(argp
[0]) = argv
[1];
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];
420 int xtid
, vid
, pushit
;
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
];
452 if (argp
[2]) *(argp
[2]) = argv
[0];
462 for (int f
= 0; f
< argc
; ++f
) STACK_PUSH(argv
[f
]);
467 trd
->stack
[trd
->sp
] = trd
->stack
[trd
->sp
-1];
478 if (argp
[0] == NULL
&& argv
[0] < 0) {
479 // allocate stack space
482 for (; argc
> 0; --argc
) STACK_PUSH(0);
487 for (int f
= 0; f
< argc
; ++f
) {
491 *(argp
[f
]) = STACK_POP();
492 } else if (argv
[f
] > 0) {
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
;
512 argc
= trd
->stack
[trd
->sp
-1];
513 trd
->stack
[trd
->sp
-1] = argv
[0];
514 if (argp
[0]) *(argp
[0]) = argc
;
517 if (argp
[0]) *(argp
[0]) = argv
[1];
518 if (argp
[1]) *(argp
[1]) = argv
[0];
526 argv
[0] = STACK_POP();
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
];
545 if (argp
[1]) *(argp
[1]) = argv
[2];
551 if (argp
[0]) *(argp
[0]) = trd
->sp
;
561 if (argp
[0]) *(argp
[0]) = tid
;
573 argv
[0] = STACK_POP();
575 if (argv
[0] >= 0 && argv
[0] < VM_MAX_THREADS
&& vmThreads
[argv
[0]].stack
!= NULL
) {
578 if (argv
[0] == tid
) return 1;
579 vmFreeThread(&vmThreads
[argv
[0]]);
583 if (argv
[0] == tid
) return -1;
584 vmThreads
[argv
[0]].suspended
= 1;
587 vmThreads
[argv
[0]].suspended
= 0;
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
;
604 if (argp
[1]) *(argp
[1]) = argv
[0];
614 argv
[0] = STACK_POP();
616 if (argv
[0] < 0 || argv
[0] >= vmCodeSize
) vmfatal(tid
, opc
, "invalid address in RXC (%d)", argv
[0]);
619 STACK_PUSH(vmCode
[argv
[0]]);
620 } else if (argp
[1]) {
621 *(argp
[1]) = vmCode
[argv
[0]];
629 argv
[1] = STACK_POP(); // byte
630 argv
[0] = STACK_POP(); // address
634 argv
[1] = STACK_POP(); // byte
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];
647 trd
->pc
= STACK_POP();
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
672 argv
[0] = STACK_POP();
675 vmRSTCB(tid
, opcode
, argc
, argv
, argp
);
676 } else if (argc
== 0) {
688 argv
[1] = STACK_POP(); // y
689 argv
[0] = STACK_POP(); // x
692 if (vmMapGetCB
) argv
[0] = vmMapGetCB(tid
, opcode
==VM_MGF
, argv
[0], argv
[1]); else argv
[0] = 0;
694 if (argp
[2]) *(argp
[2]) = argv
[0];
695 } else if (argc
== 1) {
696 if (argp
[0]) *(argp
[0]) = argv
[0];
707 argv
[2] = STACK_POP(); // tile
708 argv
[1] = STACK_POP(); // y
709 argv
[0] = STACK_POP(); // x
713 argv
[2] = argv
[0]; // tile
714 argv
[1] = STACK_POP(); // y
715 argv
[0] = STACK_POP(); // x
719 argv
[2] = STACK_POP(); // tile
722 if (vmMapSetCB
) vmMapSetCB(tid
, opcode
==VM_MSF
, argv
[0], argv
[1], argv
[2]);
726 vmfatal(tid
, opc
, "invalid opcode (%d)", opcode
);
732 void vmExecuteAll (void) {
735 memset(vmExecuted
, 0, sizeof(vmExecuted
));
738 for (int t
= 0; t
< VM_MAX_THREADS
; ++t
) {
739 if (!vmExecuted
[t
] && vmThreads
[t
].stack
!= NULL
&& !vmThreads
[t
].suspended
) {
744 for (f
= 1000000; f
>= 0; --f
) {
745 int res
= vmExecuteOne(t
);
749 //fprintf(stderr, "!!!\n");
754 vmFreeThread(&vmThreads
[t
]);
759 if (f
< 0) vmfatal(t
, vmThreads
[t
].pc
, "too many instructions in frame");
766 int vmIsThreadAlive (int tid
) {
767 if (tid
>= 0 && tid
< VM_MAX_THREADS
) return vmThreads
[tid
].stack
!= NULL
;
772 int vmNewThread (int pc
) {
773 int tid
= vmFindFreeThread();
776 if (vmInitThread(&vmThreads
[tid
]) != 0) return -1;
777 vmThreads
[tid
].pc
= pc
;
778 if (tid
> vmLastThreadId
) vmLastThreadId
= tid
;
784 int vmKillThread (int tid
) {
785 if (vmIsThreadAlive(tid
)) {
786 vmFreeThread(&vmThreads
[tid
]);
794 int vmIsSuspendedThread (int tid
) {
795 if (vmIsThreadAlive(tid
)) return vmThreads
[tid
].suspended
;
800 int vmSuspendThread (int tid
) {
801 if (vmIsThreadAlive(tid
)) { vmThreads
[tid
].suspended
= 1; return 0; }
806 int vmResumeThread (int tid
) {
807 if (vmIsThreadAlive(tid
)) { vmThreads
[tid
].suspended
= 0; return 0; }
812 int vmGetTVar (int tid
, int idx
) {
813 if (idx
>= 0 && idx
< VM_VARS_SIZE
&& vmIsThreadAlive(tid
)) return vmThreads
[tid
].tvars
[idx
];
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; }
824 int vmGetSP (int tid
) {
825 if (vmIsThreadAlive(tid
)) return vmThreads
[tid
].sp
;
830 int vmGetPC (int tid
) {
831 if (vmIsThreadAlive(tid
)) return vmThreads
[tid
].pc
;
836 int vmSetPC (int tid
, int pc
) {
837 if (vmIsThreadAlive(tid
) && pc
>= 0 && pc
< vmCodeSize
) { vmThreads
[tid
].pc
= pc
; return 0; }
842 int vmSetSP (int tid
, int value
) {
843 if (vmIsThreadAlive(tid
)) { vmThreads
[tid
].sp
= value
; return 0; }
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
];
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; }
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
;
875 int vmPop (int tid
) {
876 if (vmIsThreadAlive(tid
) && vmThreads
[tid
].sp
> 0) return vmThreads
[tid
].stack
[--vmThreads
[tid
].sp
];
881 int vmLoadArgs (int tid
, int argc
, int argv
[], int *argp
[], int aargc
, int aargv
[], int *aargp
[]) {
882 if (!vmIsThreadAlive(tid
)) return -1;
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;
891 argv
[argc
-f
] = vmThreads
[tid
].stack
[--vmThreads
[tid
].sp
];
898 int vmLastThread (void) {
899 return vmLastThreadId
;