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/>.
26 #ifndef VM_FATAL_DEFINED
27 static __attribute__((__noreturn__
)) __attribute__((format(printf
, 3, 4))) void vmfatal (int tid
, int pc
, const char *fmt
, ...) {
30 fprintf(stderr
, "VM FATAL (thread #%d, pc=%04x): ", tid
, (unsigned int)pc
);
32 vfprintf(stderr
, fmt
, ap
);
34 fprintf(stderr
, "\n");
40 const char *vmOpNames
[] = {
97 VMRSTCB vmRSTCB
= NULL
;
98 VMMapGetCB vmMapGetCB
= NULL
;
99 VMMapSetCB vmMapSetCB
= NULL
;
106 int *stack
; // internal
107 int *tvars
; // internal
111 static VMThread vmThreads
[VM_MAX_THREADS
];
112 static int vmExecuted
[VM_MAX_THREADS
];
113 static int vmLastThreadId
= 0;
115 unsigned char vmCode
[65536];
117 int vmGVars
[VM_VARS_SIZE
];
120 static void fixLastThreadId (void) {
121 for (; vmLastThreadId
> 0; --vmLastThreadId
) if (vmThreads
[vmLastThreadId
].stack
) break;
125 static int vmFindFreeThread (void) {
126 for (int f
= 0; f
< VM_MAX_THREADS
; ++f
) if (vmThreads
[f
].stack
== NULL
) return f
;
131 static int vmInitThread (VMThread
*trd
) {
135 trd
->stack
= calloc(VM_STACK_SIZE
, sizeof(int));
136 if (trd
->stack
== NULL
) return -1;
137 trd
->tvars
= calloc(VM_VARS_SIZE
, sizeof(int));
138 if (trd
->tvars
== NULL
) { free(trd
->stack
); return -1; }
143 static void vmFreeThread (VMThread
*trd
) {
144 if (trd
->stack
) free(trd
->stack
);
145 if (trd
->tvars
) free(trd
->tvars
);
154 int vmInitialize (void) {
155 memset(vmThreads
, 0, sizeof(vmThreads
));
158 if (vmInitThread(vmThreads
)) return -1; // alas
163 void vmDeinitialize (void) {
164 for (int f
= 0; f
< VM_MAX_THREADS
; ++f
) {
165 if (vmThreads
[f
].stack
) free(vmThreads
[f
].stack
);
166 if (vmThreads
[f
].tvars
) free(vmThreads
[f
].tvars
);
168 memset(vmThreads
, 0, sizeof(vmThreads
));
173 static inline int vmGetByte (int tid
, int opc
, VMThread
*trd
) {
174 if (trd
->pc
< 0 || trd
->pc
>= vmCodeSize
) vmfatal(tid
, opc
, "out of code");
175 return vmCode
[trd
->pc
++];
179 #define STACK_WANT(n) do { if (trd->sp < (n)) vmfatal(tid, opc, "stack underflow"); } while (0)
180 #define STACK_FSPC(n) do { if (trd->sp+(n) > VM_STACK_SIZE) vmfatal(tid, opc, "stack overflow"); } while (0)
182 #define STACK_TOP() (trd->stack[trd->sp-1])
183 #define STACK_POP() (trd->stack[--(trd->sp)])
184 #define STACK_PUSH(n) trd->stack[(trd->sp)++] = (n)
187 #define MATH(op) do { \
191 trd->stack[trd->sp-2] = (trd->stack[trd->sp-2]) op (trd->stack[trd->sp-1]); \
196 trd->stack[trd->sp-1] = (trd->stack[trd->sp-1]) op (argv[0]); \
198 case 2: if (argp[0] != NULL) *(argp[0]) = (argv[0]) op (argv[1]); break; \
199 case 3: if (argp[2] != NULL) *(argp[2]) = (argv[0]) op (argv[1]); break; \
204 #define CHECK_DIV_ZERO \
207 if (trd->sp > 0 && trd->stack[trd->sp-1] == 0) { fprintf(stderr, "VM FATAL: division by zero!\n"); exit(1); } \
210 if (argv[0] == 0) { fprintf(stderr, "VM FATAL: division by zero!\n"); exit(1); } \
213 if (argv[1] == 0) { fprintf(stderr, "VM FATAL: division by zero!\n"); exit(1); } \
218 #define JXX(op) do { \
222 argv[0] = STACK_POP(); \
225 argv[2] = STACK_POP(); \
226 argv[1] = STACK_POP(); \
231 argv[1] = STACK_POP(); \
234 if (argv[1] op argv[2]) trd->pc = argv[0]; \
238 static inline int isBranch (int opcode
) {
252 int vmExecuteOne (int tid
) {
253 VMThread
*trd
= &vmThreads
[tid
];
256 int argv
[3]; // argument values
257 int *argp
[3]; // pointer to vars for each arg
260 // decode instruction
261 opcode
= vmGetByte(tid
, opc
, trd
);
262 argc
= (opcode
>>6)&0x03;
264 argv
[0] = argv
[1] = argv
[2] = 0;
265 argp
[0] = argp
[1] = argp
[2] = NULL
;
267 if (tid
> vmLastThreadId
) vmLastThreadId
= tid
;
268 for (int f
= 0; f
< argc
; ++f
) {
269 int vn
= vmGetByte(tid
, opc
, trd
);
273 argv
[f
] = vmGetByte(tid
, opc
, trd
);
274 argv
[f
] |= vmGetByte(tid
, opc
, trd
)<<8;
275 if (argv
[f
] >= 32768 && (f
!= 0 || !isBranch(opcode
))) argv
[f
] -= 65536;
278 // stack var; <0: from stack top
279 argv
[f
] = vmGetByte(tid
, opc
, trd
);
280 argv
[f
] |= (vmGetByte(tid
, opc
, trd
)<<8);
281 if (argv
[f
] >= 32768) argv
[f
] -= 65536;
282 if (argv
[f
] < 0) argv
[f
] += trd
->sp
;
283 if (argv
[f
] < 0 || argv
[f
] >= VM_STACK_SIZE
) vmfatal(tid
, opc
, "invalid stack offset");
284 argp
[f
] = trd
->stack
+argv
[f
];
287 argp
[f
] = vn
&0x80 ? vmGVars
: trd
->tvars
;
290 argv
[f
] = *(argp
[f
]);
295 fprintf(stderr
, "%04x: ", (unsigned int)opc
);
296 if (opcode
< sizeof(vmOpNames
)/sizeof(char *)-1) fprintf(stderr
, "%s", vmOpNames
[opcode
]); else fprintf(stderr
, "BAD");
297 for (int f
= 0; f
< argc
; ++f
) {
303 if (argp
[f
] >= trd
->tvars
&& argp
[f
] < trd
->tvars
+VM_VARS_SIZE
) {
304 ofs
= (unsigned int)(argp
[f
]-trd
->tvars
);
305 } else if (argp
[f
] >= trd
->stack
&& argp
[f
] < trd
->stack
+VM_VARS_SIZE
) {
306 ofs
= (unsigned int)(argp
[f
]-trd
->stack
);
308 } else if (argp
[f
] >= vmGVars
&& argp
[f
] < vmGVars
+VM_VARS_SIZE
) {
309 ofs
= (unsigned int)(argp
[f
]-vmGVars
);
312 fprintf(stderr
, "%u]", ofs
);
314 if (f
== 0 && isBranch(opcode
)) {
315 fprintf(stderr
, "(0x%04x)", argv
[f
]);
317 fprintf(stderr
, "(%d)", argv
[f
]);
324 case VM_ADD
: MATH(+); break;
325 case VM_SUB
: MATH(-); break;
326 case VM_MUL
: MATH(*); break;
335 case VM_BOR
: MATH(|); break;
336 case VM_XOR
: MATH(^); break;
337 case VM_AND
: MATH(&); break;
339 case VM_JEQ
: JXX(==); break;
340 case VM_JNE
: JXX(!=); break;
341 case VM_JLT
: JXX(<); break;
342 case VM_JLE
: JXX(<=); break;
343 case VM_JGT
: JXX(>); break;
344 case VM_JGE
: JXX(>=); break;
349 argv
[0] = STACK_POP();
358 argv
[0] = STACK_POP();
377 case VM_END
: return 1;
378 case VM_BRK
: return -1;
380 case VM_NEW
: { // new thread
381 int xtid
= vmFindFreeThread();
385 argv
[0] = STACK_POP();
388 if (vmInitThread(&vmThreads
[xtid
]) == 0) {
389 vmThreads
[xtid
].pc
= argv
[0];
394 if (xtid
< 0) vmfatal(tid
, opc
, "too many threads");
395 if (xtid
> vmLastThreadId
) vmLastThreadId
= xtid
;
397 if (argp
[1]) *(argp
[1]) = xtid
;
408 argv
[0] = STACK_POP(); // varid
409 argv
[1] = STACK_POP(); // value
412 if (argv
[0] < VM_VARS_SIZE
) vmGVars
[argv
[0]] = argv
[1];
413 } else if (argv
[0] < VM_VARS_SIZE
) {
414 trd
->tvars
[argv
[0]] = argv
[1];
419 argv
[1] = STACK_POP(); // value
422 if (argp
[0]) *(argp
[0]) = argv
[1];
425 int xtid
= argv
[2], vid
= argv
[0];
427 if (xtid
< 0 || xtid
>= VM_MAX_THREADS
|| vmThreads
[xtid
].stack
== NULL
|| vid
< 0 || vid
>= VM_VARS_SIZE
) break;
428 vmThreads
[xtid
].tvars
[vid
] = argv
[1];
434 int xtid
, vid
, pushit
;
460 if (xtid
>= 0 && xtid
< VM_MAX_THREADS
&& vmThreads
[xtid
].stack
!= NULL
&& vid
>= 0 && vid
< VM_VARS_SIZE
) {
461 argv
[0] = vmThreads
[xtid
].tvars
[vid
];
466 if (argp
[2]) *(argp
[2]) = argv
[0];
476 for (int f
= 0; f
< argc
; ++f
) STACK_PUSH(argv
[f
]);
481 trd
->stack
[trd
->sp
] = trd
->stack
[trd
->sp
-1];
492 if (argp
[0] == NULL
&& argv
[0] < 0) {
493 // allocate stack space
496 for (; argc
> 0; --argc
) STACK_PUSH(0);
501 for (int f
= 0; f
< argc
; ++f
) {
505 *(argp
[f
]) = STACK_POP();
506 } else if (argv
[f
] > 0) {
520 argc
= trd
->stack
[trd
->sp
-2];
521 trd
->stack
[trd
->sp
-2] = trd
->stack
[trd
->sp
-1];
522 trd
->stack
[trd
->sp
-1] = argc
;
526 argc
= trd
->stack
[trd
->sp
-1];
527 trd
->stack
[trd
->sp
-1] = argv
[0];
528 if (argp
[0]) *(argp
[0]) = argc
;
531 if (argp
[0]) *(argp
[0]) = argv
[1];
532 if (argp
[1]) *(argp
[1]) = argv
[0];
540 argv
[0] = STACK_POP();
543 if (argv
[0] < 0) argv
[0] += trd
->sp
;
544 if (argv
[0] < 0 || argv
[0] >= trd
->sp
) vmfatal(tid
, opc
, "invalid stack index (%d)", argv
[0]);
545 argv
[2] = trd
->stack
[argv
[0]];
547 if (opcode
== VM_ROL
) {
548 for (int f
= argv
[0]+1; f
< trd
->sp
; ++f
) trd
->stack
[f
-1] = trd
->stack
[f
];
559 if (argp
[1]) *(argp
[1]) = argv
[2];
565 if (argp
[0]) *(argp
[0]) = trd
->sp
;
575 if (argp
[0]) *(argp
[0]) = tid
;
587 argv
[0] = STACK_POP();
589 if (argv
[0] >= 0 && argv
[0] < VM_MAX_THREADS
&& vmThreads
[argv
[0]].stack
!= NULL
) {
592 if (argv
[0] == tid
) return 1;
593 vmFreeThread(&vmThreads
[argv
[0]]);
597 if (argv
[0] == tid
) return -1;
598 vmThreads
[argv
[0]].suspended
= 1;
601 vmThreads
[argv
[0]].suspended
= 0;
610 argv
[0] = STACK_POP();
612 if (argv
[0] >= 0 && argv
[0] < VM_MAX_THREADS
&& vmThreads
[argv
[0]].stack
!= NULL
) {
613 argv
[0] = vmThreads
[argv
[0]].suspended
;
618 if (argp
[1]) *(argp
[1]) = argv
[0];
628 argv
[0] = STACK_POP();
630 if (argv
[0] < 0 || argv
[0] >= vmCodeSize
) vmfatal(tid
, opc
, "invalid address in RXC (%d)", argv
[0]);
633 STACK_PUSH(vmCode
[argv
[0]]);
634 } else if (argp
[1]) {
635 *(argp
[1]) = vmCode
[argv
[0]];
643 argv
[1] = STACK_POP(); // byte
644 argv
[0] = STACK_POP(); // address
648 argv
[1] = STACK_POP(); // byte
651 if (argv
[0] < 0 || argv
[0] >= vmCodeSize
) vmfatal(tid
, opc
, "invalid address in WXC (%d)", argv
[0]);
652 if (argv
[1] < -128 || argv
[1] > 255) vmfatal(tid
, opc
, "invalid value in WXC (%d)", argv
[1]);
653 if (argv
[1] < 0) argv
[1] &= 0xff;
654 vmCode
[argv
[0]] = argv
[1];
661 trd
->pc
= STACK_POP();
664 vmfatal(tid
, opc
, "invalid RET");
665 case 2: // # of locals to pop, # of arguments to pop
666 case 3: // # of locals to pop, # of arguments to pop, retvalue (taken before all pops, pushes after returning)
667 if (argv
[0] < 0) argv
[0] = 0;
668 if (argv
[1] < 0) argv
[1] = 0;
669 STACK_WANT(argv
[0]+argv
[1]+1);
670 trd
->sp
-= argv
[0]; // drop locals
671 trd
->pc
= STACK_POP();
672 trd
->sp
-= argv
[1]; // drop arguments
686 argv
[0] = STACK_POP();
689 vmRSTCB(tid
, opcode
, argc
, argv
, argp
);
690 } else if (argc
== 0) {
702 argv
[1] = STACK_POP(); // y
703 argv
[0] = STACK_POP(); // x
706 if (vmMapGetCB
) argv
[0] = vmMapGetCB(tid
, opcode
==VM_MGF
, argv
[0], argv
[1]); else argv
[0] = 0;
708 if (argp
[2]) *(argp
[2]) = argv
[0];
709 } else if (argc
== 1) {
710 if (argp
[0]) *(argp
[0]) = argv
[0];
721 argv
[2] = STACK_POP(); // tile
722 argv
[1] = STACK_POP(); // y
723 argv
[0] = STACK_POP(); // x
727 argv
[2] = argv
[0]; // tile
728 argv
[1] = STACK_POP(); // y
729 argv
[0] = STACK_POP(); // x
733 argv
[2] = STACK_POP(); // tile
736 if (vmMapSetCB
) vmMapSetCB(tid
, opcode
==VM_MSF
, argv
[0], argv
[1], argv
[2]);
740 vmfatal(tid
, opc
, "invalid opcode (%d)", opcode
);
746 void vmExecuteAll (void) {
749 memset(vmExecuted
, 0, sizeof(vmExecuted
));
752 for (int t
= 0; t
< VM_MAX_THREADS
; ++t
) {
753 if (!vmExecuted
[t
] && vmThreads
[t
].stack
!= NULL
&& !vmThreads
[t
].suspended
) {
758 for (f
= 1000000; f
>= 0; --f
) {
759 int res
= vmExecuteOne(t
);
763 //fprintf(stderr, "!!!\n");
768 vmFreeThread(&vmThreads
[t
]);
773 if (f
< 0) vmfatal(t
, vmThreads
[t
].pc
, "too many instructions in frame");
780 // <0: BRK; >0: END; 0: ok
781 // maxinst<0: any number of instructions
782 int vmExecuteBSR (int tid
, int pc
, int maxinst
) {
785 if (tid
< 0 || tid
>= VM_MAX_THREADS
|| vmThreads
[tid
].stack
== NULL
|| vmThreads
[tid
].suspended
) return 2; // END
786 opc
= vmThreads
[tid
].pc
;
787 vmPush(tid
, -666666);
788 vmThreads
[tid
].pc
= pc
;
789 if (maxinst
== 0) maxinst
= 1000000;
791 int res
= vmExecuteOne(tid
);
793 if (res
> 0) vmFreeThread(&vmThreads
[tid
]);
794 if (res
!= 0) return res
;
795 if (vmThreads
[tid
].pc
== -666666) {
796 vmThreads
[tid
].pc
= opc
;
799 if (maxinst
== 1) vmfatal(tid
, vmThreads
[tid
].pc
, "too many instructions in vmExecuteBSR");
800 if (maxinst
> 0) --maxinst
;
805 int vmIsThreadAlive (int tid
) {
806 if (tid
>= 0 && tid
< VM_MAX_THREADS
) return vmThreads
[tid
].stack
!= NULL
;
811 int vmNewThread (int pc
) {
812 int tid
= vmFindFreeThread();
815 if (vmInitThread(&vmThreads
[tid
]) != 0) return -1;
816 vmThreads
[tid
].pc
= pc
;
817 if (tid
> vmLastThreadId
) vmLastThreadId
= tid
;
823 int vmKillThread (int tid
) {
824 if (vmIsThreadAlive(tid
)) {
825 vmFreeThread(&vmThreads
[tid
]);
833 int vmIsSuspendedThread (int tid
) {
834 if (vmIsThreadAlive(tid
)) return vmThreads
[tid
].suspended
;
839 int vmSuspendThread (int tid
) {
840 if (vmIsThreadAlive(tid
)) { vmThreads
[tid
].suspended
= 1; return 0; }
845 int vmResumeThread (int tid
) {
846 if (vmIsThreadAlive(tid
)) { vmThreads
[tid
].suspended
= 0; return 0; }
851 int vmGetTVar (int tid
, int idx
) {
852 if (idx
>= 0 && idx
< VM_VARS_SIZE
&& vmIsThreadAlive(tid
)) return vmThreads
[tid
].tvars
[idx
];
857 int vmSetTVar (int tid
, int idx
, int value
) {
858 if (idx
>= 0 && idx
< VM_VARS_SIZE
&& vmIsThreadAlive(tid
)) { vmThreads
[tid
].tvars
[idx
] = value
; return 0; }
863 int vmGetSP (int tid
) {
864 if (vmIsThreadAlive(tid
)) return vmThreads
[tid
].sp
;
869 int vmGetPC (int tid
) {
870 if (vmIsThreadAlive(tid
)) return vmThreads
[tid
].pc
;
875 int vmSetPC (int tid
, int pc
) {
876 if (vmIsThreadAlive(tid
) && pc
>= 0 && pc
< vmCodeSize
) { vmThreads
[tid
].pc
= pc
; return 0; }
881 int vmSetSP (int tid
, int value
) {
882 if (vmIsThreadAlive(tid
)) { vmThreads
[tid
].sp
= value
; return 0; }
887 int vmGetStack (int tid
, int idx
) {
888 if (vmIsThreadAlive(tid
)) {
889 if (idx
< 0) idx
+= vmThreads
[tid
].sp
;
890 if (idx
>= 0 && idx
< VM_STACK_SIZE
) return vmThreads
[tid
].stack
[idx
];
896 int vmSetStack (int tid
, int idx
, int value
) {
897 if (vmIsThreadAlive(tid
)) {
898 if (idx
< 0) idx
+= vmThreads
[tid
].sp
;
899 if (idx
>= 0 && idx
< VM_STACK_SIZE
) { vmThreads
[tid
].stack
[idx
] = value
; return 0; }
905 int vmPush (int tid
, int value
) {
906 if (vmIsThreadAlive(tid
) && vmThreads
[tid
].sp
< VM_STACK_SIZE
) {
907 vmThreads
[tid
].stack
[vmThreads
[tid
].sp
++] = value
;
914 int vmPop (int tid
) {
915 if (vmIsThreadAlive(tid
) && vmThreads
[tid
].sp
> 0) return vmThreads
[tid
].stack
[--vmThreads
[tid
].sp
];
920 int vmLoadArgs (int tid
, int argc
, int argv
[], int *argp
[], int aargc
, int aargv
[], int *aargp
[]) {
921 if (!vmIsThreadAlive(tid
)) return -1;
925 if (e
> aargc
) e
= aargc
;
926 for (int f
= 0; f
< e
; ++f
) { argv
[f
] = aargv
[f
]; argp
[f
] = aargp
[f
]; }
927 for (int f
= e
; f
< argc
; ++f
) {
928 if (vmThreads
[tid
].sp
<= 0) return -1;
930 argv
[argc
-f
] = vmThreads
[tid
].stack
[--vmThreads
[tid
].sp
];
937 int vmLastThread (void) {
938 return vmLastThreadId
;
942 static int readBuf (FILE *fl
, void *buf
, int len
) {
943 unsigned char *c
= (unsigned char *)buf
;
948 if (fread(&b
, 1, 1, fl
) != 1) return -1;
956 static int writeBuf (FILE *fl
, const void *buf
, int len
) {
957 const unsigned char *c
= (const unsigned char *)buf
;
960 unsigned char b
= *c
++;
963 if (fwrite(&b
, 1, 1, fl
) != 1) return -1;
969 static int readDW (FILE *fl
, int *v
) {
972 for (int f
= 0; f
< 4; ++f
) {
975 if (fread(&b
, 1, 1, fl
) != 1) return -1;
981 for (int f
= 0; f
< 4; ++f
) *v
|= t
[f
]<<(8*f
);
987 static int writeDW (FILE *fl
, int v
) {
988 for (int f
= 0; f
< 4; ++f
) {
993 if (fwrite(&b
, 1, 1, fl
) != 1) return -1;
1000 int vmSaveState (FILE *fl
) {
1003 if (writeDW(fl
, VM_VARS_SIZE
) != 0) return -1;
1004 //if (writeDW(fl, VM_STACK_SIZE) != 0) return -1;
1005 if (writeDW(fl
, vmLastThreadId
) != 0) return -1;
1006 if (writeDW(fl
, vmCodeSize
) != 0) return -1;
1007 if (writeBuf(fl
, vmCode
, vmCodeSize
) != 0) return -1;
1008 for (int f
= 0; f
< VM_VARS_SIZE
; ++f
) if (writeDW(fl
, vmGVars
[f
]) != 0) return -1;
1009 for (int f
= 0; f
<= vmLastThreadId
; ++f
) {
1010 if (vmThreads
[f
].stack
!= NULL
) {
1011 if (writeDW(fl
, f
) != 0) return -1;
1012 if (writeDW(fl
, vmThreads
[f
].pc
) != 0) return -1;
1013 if (writeDW(fl
, vmThreads
[f
].suspended
) != 0) return -1;
1014 if (writeDW(fl
, vmThreads
[f
].sp
) != 0) return -1;
1015 for (int c
= 0; c
< vmThreads
[f
].sp
; ++c
) if (writeDW(fl
, vmThreads
[f
].stack
[c
]) != 0) return -1;
1016 for (int c
= 0; c
< VM_VARS_SIZE
; ++c
) if (writeDW(fl
, vmThreads
[f
].tvars
[c
]) != 0) return -1;
1018 if (writeDW(fl
, -1) != 0) return -1;
1025 int vmLoadState (FILE *fl
) {
1030 if (vmInitialize() != 0) goto fail
;
1032 if (readDW(fl
, &v
) != 0 || v
!= VM_VARS_SIZE
) goto fail
;
1033 //if (readDW(fl, &ssz) != 0) goto fail;
1034 if (readDW(fl
, &vmLastThreadId
) != 0 || vmLastThreadId
< 0 || vmLastThreadId
> VM_MAX_THREADS
) goto fail
;
1035 if (readDW(fl
, &vmCodeSize
) != 0 || vmCodeSize
< 1 || vmCodeSize
> 65536) goto fail
;
1036 if (readBuf(fl
, vmCode
, vmCodeSize
) != 0) goto fail
;
1037 for (int f
= 0; f
< VM_VARS_SIZE
; ++f
) if (readDW(fl
, &vmGVars
[f
]) != 0) goto fail
;
1038 for (int f
= 0; f
<= vmLastThreadId
; ++f
) {
1041 if (readDW(fl
, &flag
) != 0) goto fail
;
1042 if (flag
== -1) continue;
1043 if (flag
!= f
) goto fail
;
1044 if (vmInitThread(&vmThreads
[f
]) != 0) goto fail
;
1045 if (readDW(fl
, &vmThreads
[f
].pc
) != 0) goto fail
;
1046 if (readDW(fl
, &vmThreads
[f
].suspended
) != 0) goto fail
;
1047 if (readDW(fl
, &vmThreads
[f
].sp
) != 0) goto fail
;
1048 if (!vmThreads
[f
].suspended
) {
1049 if (vmThreads
[f
].pc
< 0 || vmThreads
[f
].pc
>= vmCodeSize
) goto fail
;
1050 if (vmThreads
[f
].sp
< 0 || vmThreads
[f
].sp
> VM_STACK_SIZE
) goto fail
;
1052 for (int c
= 0; c
< vmThreads
[f
].sp
; ++c
) if (readDW(fl
, &vmThreads
[f
].stack
[c
]) != 0) goto fail
;
1053 for (int c
= 0; c
< VM_VARS_SIZE
; ++c
) if (readDW(fl
, &vmThreads
[f
].tvars
[c
]) != 0) goto fail
;