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/>.
29 // should be enough for everyone!
30 #define MAX_INST_COUNT (1000000)
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
, ...) {
39 fprintf(stderr
, "AWISH FATAL: ");
41 vfprintf(stderr
, fmt
, ap
);
43 fprintf(stderr
, "\n");
48 #ifndef VM_FATAL_DEFINED
49 static __attribute__((__noreturn__
)) __attribute__((format(printf
, 3, 4))) void vmfatal (int tid
, int pc
, const char *fmt
, ...) {
52 fprintf(stderr
, "VM FATAL (thread #%d, pc=%04x): ", tid
, (unsigned int)pc
);
54 vfprintf(stderr
, fmt
, ap
);
56 fprintf(stderr
, "\n");
62 const char *vmOpNames
[] = {
119 VMRSTCB vmRSTCB
= NULL
;
120 VMMapGetCB vmMapGetCB
= NULL
;
121 VMMapSetCB vmMapSetCB
= NULL
;
128 int *stack
; // internal
129 int *tvars
; // internal
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];
143 int vmGVars
[VM_VARS_SIZE
];
149 static void fixLastThreadId (void) {
150 for (; vmLastThreadId
> 0; --vmLastThreadId
) if (vmThreads
[vmLastThreadId
].stack
) break;
154 static int vmFindFreeThread (void) {
155 for (int f
= 0; f
< VM_MAX_THREADS
; ++f
) if (vmThreads
[f
].stack
== NULL
) return f
;
160 static int vmInitThread (VMThread
*trd
) {
164 trd
->stack
= calloc(VM_STACK_SIZE
, sizeof(int));
165 if (trd
->stack
== NULL
) return -1;
166 trd
->tvars
= calloc(VM_VARS_SIZE
, sizeof(int));
167 if (trd
->tvars
== NULL
) { free(trd
->stack
); return -1; }
172 static void vmFreeThread (VMThread
*trd
) {
173 if (trd
->stack
) free(trd
->stack
);
174 if (trd
->tvars
) free(trd
->tvars
);
183 int vmInitialize (void) {
185 memset(vmThreads
, 0, sizeof(vmThreads
));
188 if (vmInitThread(vmThreads
)) return -1; // alas
193 void vmDeinitialize (void) {
194 for (int f
= 0; f
< VM_MAX_THREADS
; ++f
) {
195 if (vmThreads
[f
].stack
) free(vmThreads
[f
].stack
);
196 if (vmThreads
[f
].tvars
) free(vmThreads
[f
].tvars
);
198 memset(vmThreads
, 0, sizeof(vmThreads
));
204 static inline int vmGetByte (int tid
, int opc
, VMThread
*trd
) {
205 if (trd
->pc
< 0 || trd
->pc
>= vmCodeSize
) vmfatal(tid
, opc
, "out of code");
206 return vmCode
[trd
->pc
++];
210 #define STACK_WANT(n) do { if (trd->sp < (n)) vmfatal(tid, opc, "stack underflow"); } while (0)
211 #define STACK_FSPC(n) do { if (trd->sp+(n) > VM_STACK_SIZE) vmfatal(tid, opc, "stack overflow"); } while (0)
213 #define STACK_TOP() (trd->stack[trd->sp-1])
214 #define STACK_POP() (trd->stack[--(trd->sp)])
215 #define STACK_PUSH(n) trd->stack[(trd->sp)++] = (n)
218 #define MATH(op) do { \
222 trd->stack[trd->sp-2] = (trd->stack[trd->sp-2]) op (trd->stack[trd->sp-1]); \
227 trd->stack[trd->sp-1] = (trd->stack[trd->sp-1]) op (argv[0]); \
229 case 2: if (argp[0] != NULL) *(argp[0]) = (argv[0]) op (argv[1]); break; \
230 case 3: if (argp[2] != NULL) *(argp[2]) = (argv[0]) op (argv[1]); break; \
235 #define CHECK_DIV_ZERO \
238 if (trd->sp > 0 && trd->stack[trd->sp-1] == 0) { fprintf(stderr, "VM FATAL: division by zero!\n"); exit(1); } \
241 if (argv[0] == 0) { fprintf(stderr, "VM FATAL: division by zero!\n"); exit(1); } \
244 if (argv[1] == 0) { fprintf(stderr, "VM FATAL: division by zero!\n"); exit(1); } \
249 #define JXX(op) do { \
253 argv[0] = STACK_POP(); \
256 argv[2] = STACK_POP(); \
257 argv[1] = STACK_POP(); \
262 argv[1] = STACK_POP(); \
265 if (argv[1] op argv[2]) trd->pc = argv[0]; \
269 static inline int isBranch (int opcode
) {
283 int vmExecuteOne (int tid
) {
284 VMThread
*trd
= &vmThreads
[tid
];
287 int argv
[3]; // argument values
288 int *argp
[3]; // pointer to vars for each arg
291 // decode instruction
292 opcode
= vmGetByte(tid
, opc
, trd
);
293 argc
= (opcode
>>6)&0x03;
295 argv
[0] = argv
[1] = argv
[2] = 0;
296 argp
[0] = argp
[1] = argp
[2] = NULL
;
298 if (tid
> vmLastThreadId
) vmLastThreadId
= tid
;
299 for (int f
= 0; f
< argc
; ++f
) {
300 int vn
= vmGetByte(tid
, opc
, trd
);
304 argv
[f
] = vmGetByte(tid
, opc
, trd
);
305 argv
[f
] |= vmGetByte(tid
, opc
, trd
)<<8;
306 if (argv
[f
] >= 32768 && (f
!= 0 || !isBranch(opcode
))) argv
[f
] -= 65536;
309 // stack var; <0: from stack top
310 argv
[f
] = vmGetByte(tid
, opc
, trd
);
311 argv
[f
] |= (vmGetByte(tid
, opc
, trd
)<<8);
312 if (argv
[f
] >= 32768) argv
[f
] -= 65536;
313 if (argv
[f
] < 0) argv
[f
] += trd
->sp
;
314 if (argv
[f
] < 0 || argv
[f
] >= VM_STACK_SIZE
) vmfatal(tid
, opc
, "invalid stack offset");
315 argp
[f
] = trd
->stack
+argv
[f
];
318 argp
[f
] = vn
&0x80 ? vmGVars
: trd
->tvars
;
321 argv
[f
] = *(argp
[f
]);
327 if (vmDebugOutput
== NULL
) vmDebugOutput
= stderr
;
329 fprintf(vmDebugOutput
, "(%04d) %04x: ", tid
, (unsigned int)opc
);
330 if (opcode
< sizeof(vmOpNames
)/sizeof(char *)-1) fprintf(vmDebugOutput
, "%s", vmOpNames
[opcode
]); else fprintf(vmDebugOutput
, "BAD");
331 for (int f
= 0; f
< argc
; ++f
) {
332 fputc(' ', vmDebugOutput
);
334 unsigned int ofs
= 0;
336 fputc('[', vmDebugOutput
);
337 if (argp
[f
] >= trd
->tvars
&& argp
[f
] < trd
->tvars
+VM_VARS_SIZE
) {
338 ofs
= (unsigned int)(argp
[f
]-trd
->tvars
);
339 } else if (argp
[f
] >= trd
->stack
&& argp
[f
] < trd
->stack
+VM_VARS_SIZE
) {
340 ofs
= (unsigned int)(argp
[f
]-trd
->stack
);
341 fputc('.', vmDebugOutput
);
342 } else if (argp
[f
] >= vmGVars
&& argp
[f
] < vmGVars
+VM_VARS_SIZE
) {
343 ofs
= (unsigned int)(argp
[f
]-vmGVars
);
344 fputc('@', vmDebugOutput
);
346 fprintf(vmDebugOutput
, "%u]", ofs
);
348 if (f
== 0 && isBranch(opcode
)) {
349 fprintf(vmDebugOutput
, "(0x%04x)", argv
[f
]);
351 fprintf(vmDebugOutput
, "(%d)", argv
[f
]);
354 fputc('\n', vmDebugOutput
);
355 if (vmDebugOutput
!= stderr
&& vmDebugOutput
!= stdout
) fflush(vmDebugOutput
);
360 case VM_ADD
: MATH(+); break;
361 case VM_SUB
: MATH(-); break;
362 case VM_MUL
: MATH(*); break;
371 case VM_BOR
: MATH(|); break;
372 case VM_XOR
: MATH(^); break;
373 case VM_AND
: MATH(&); break;
375 case VM_JEQ
: JXX(==); break;
376 case VM_JNE
: JXX(!=); break;
377 case VM_JLT
: JXX(<); break;
378 case VM_JLE
: JXX(<=); break;
379 case VM_JGT
: JXX(>); break;
380 case VM_JGE
: JXX(>=); break;
385 argv
[0] = STACK_POP();
394 argv
[0] = STACK_POP();
413 case VM_END
: return 1;
414 case VM_BRK
: return -1;
416 case VM_NEW
: { // new thread
417 int xtid
= vmFindFreeThread();
421 argv
[0] = STACK_POP();
424 if (vmInitThread(&vmThreads
[xtid
]) == 0) {
425 vmThreads
[xtid
].pc
= argv
[0];
430 if (xtid
< 0) vmfatal(tid
, opc
, "too many threads");
431 if (xtid
> vmLastThreadId
) vmLastThreadId
= xtid
;
433 if (argp
[1]) *(argp
[1]) = xtid
;
444 argv
[0] = STACK_POP(); // varid
445 argv
[1] = STACK_POP(); // value
448 if (argv
[0] < VM_VARS_SIZE
) vmGVars
[argv
[0]] = argv
[1];
449 } else if (argv
[0] < VM_VARS_SIZE
) {
450 trd
->tvars
[argv
[0]] = argv
[1];
455 argv
[1] = STACK_POP(); // value
458 if (argp
[0]) *(argp
[0]) = argv
[1];
461 int xtid
= argv
[2], vid
= argv
[0];
463 if (xtid
< 0 || xtid
>= VM_MAX_THREADS
|| vmThreads
[xtid
].stack
== NULL
|| vid
< 0 || vid
>= VM_VARS_SIZE
) break;
464 vmThreads
[xtid
].tvars
[vid
] = argv
[1];
470 int xtid
, vid
, pushit
;
496 if (xtid
>= 0 && xtid
< VM_MAX_THREADS
&& vmThreads
[xtid
].stack
!= NULL
&& vid
>= 0 && vid
< VM_VARS_SIZE
) {
497 argv
[0] = vmThreads
[xtid
].tvars
[vid
];
502 if (argp
[2]) *(argp
[2]) = argv
[0];
512 for (int f
= 0; f
< argc
; ++f
) STACK_PUSH(argv
[f
]);
517 trd
->stack
[trd
->sp
] = trd
->stack
[trd
->sp
-1];
528 if (argp
[0] == NULL
&& argv
[0] < 0) {
529 // allocate stack space
532 for (; argc
> 0; --argc
) STACK_PUSH(0);
537 for (int f
= 0; f
< argc
; ++f
) {
541 *(argp
[f
]) = STACK_POP();
542 } else if (argv
[f
] > 0) {
556 argc
= trd
->stack
[trd
->sp
-2];
557 trd
->stack
[trd
->sp
-2] = trd
->stack
[trd
->sp
-1];
558 trd
->stack
[trd
->sp
-1] = argc
;
562 argc
= trd
->stack
[trd
->sp
-1];
563 trd
->stack
[trd
->sp
-1] = argv
[0];
564 if (argp
[0]) *(argp
[0]) = argc
;
567 if (argp
[0]) *(argp
[0]) = argv
[1];
568 if (argp
[1]) *(argp
[1]) = argv
[0];
576 argv
[0] = STACK_POP();
579 if (argv
[0] < 0) argv
[0] += trd
->sp
;
580 if (argv
[0] < 0 || argv
[0] >= trd
->sp
) vmfatal(tid
, opc
, "invalid stack index (%d)", argv
[0]);
581 argv
[2] = trd
->stack
[argv
[0]];
583 if (opcode
== VM_ROL
) {
584 for (int f
= argv
[0]+1; f
< trd
->sp
; ++f
) trd
->stack
[f
-1] = trd
->stack
[f
];
595 if (argp
[1]) *(argp
[1]) = argv
[2];
601 if (argp
[0]) *(argp
[0]) = trd
->sp
;
611 if (argp
[0]) *(argp
[0]) = tid
;
623 argv
[0] = STACK_POP();
625 if (argv
[0] >= 0 && argv
[0] < VM_MAX_THREADS
&& vmThreads
[argv
[0]].stack
!= NULL
) {
628 if (argv
[0] == tid
) return 1;
629 vmFreeThread(&vmThreads
[argv
[0]]);
633 if (argv
[0] == tid
) return -1;
634 vmThreads
[argv
[0]].suspended
= 1;
637 vmThreads
[argv
[0]].suspended
= 0;
646 argv
[0] = STACK_POP();
648 if (argv
[0] >= 0 && argv
[0] < VM_MAX_THREADS
&& vmThreads
[argv
[0]].stack
!= NULL
) {
649 argv
[0] = vmThreads
[argv
[0]].suspended
;
654 if (argp
[1]) *(argp
[1]) = argv
[0];
664 argv
[0] = STACK_POP();
666 if (argv
[0] < 0 || argv
[0] >= vmCodeSize
) vmfatal(tid
, opc
, "invalid address in RXC (%d)", argv
[0]);
669 STACK_PUSH(vmCode
[argv
[0]]);
670 } else if (argp
[1]) {
671 *(argp
[1]) = vmCode
[argv
[0]];
679 argv
[1] = STACK_POP(); // byte
680 argv
[0] = STACK_POP(); // address
684 argv
[1] = STACK_POP(); // byte
687 if (argv
[0] < 0 || argv
[0] >= vmCodeSize
) vmfatal(tid
, opc
, "invalid address in WXC (%d)", argv
[0]);
688 if (argv
[1] < -128 || argv
[1] > 255) vmfatal(tid
, opc
, "invalid value in WXC (%d)", argv
[1]);
689 if (argv
[1] < 0) argv
[1] &= 0xff;
690 vmCode
[argv
[0]] = argv
[1];
697 trd
->pc
= STACK_POP();
700 vmfatal(tid
, opc
, "invalid RET");
701 case 2: // # of locals to pop, # of arguments to pop
702 case 3: // # of locals to pop, # of arguments to pop, retvalue (taken before all pops, pushes after returning)
703 if (argv
[0] < 0) argv
[0] = 0;
704 if (argv
[1] < 0) argv
[1] = 0;
705 STACK_WANT(argv
[0]+argv
[1]+1);
706 trd
->sp
-= argv
[0]; // drop locals
707 trd
->pc
= STACK_POP();
708 trd
->sp
-= argv
[1]; // drop arguments
722 argv
[0] = STACK_POP();
725 vmRSTCB(tid
, opcode
, argc
, argv
, argp
);
726 } else if (argc
== 0) {
738 argv
[1] = STACK_POP(); // y
739 argv
[0] = STACK_POP(); // x
742 if (vmMapGetCB
) argv
[0] = vmMapGetCB(tid
, opcode
==VM_MGF
, argv
[0], argv
[1]); else argv
[0] = 0;
744 if (argp
[2]) *(argp
[2]) = argv
[0];
745 } else if (argc
== 1) {
746 if (argp
[0]) *(argp
[0]) = argv
[0];
757 argv
[2] = STACK_POP(); // tile
758 argv
[1] = STACK_POP(); // y
759 argv
[0] = STACK_POP(); // x
763 argv
[2] = argv
[0]; // tile
764 argv
[1] = STACK_POP(); // y
765 argv
[0] = STACK_POP(); // x
769 argv
[2] = STACK_POP(); // tile
772 if (vmMapSetCB
) vmMapSetCB(tid
, opcode
==VM_MSF
, argv
[0], argv
[1], argv
[2]);
776 vmfatal(tid
, opc
, "invalid opcode (%d)", opcode
);
782 void vmExecuteAll (int menuCheckVar
, int inMenu
) {
785 memset(vmExecuted
, 0, sizeof(vmExecuted
));
788 for (int t
= 0; t
< VM_MAX_THREADS
; ++t
) {
789 if (!vmExecuted
[t
] && vmThreads
[t
].stack
!= NULL
&& !vmThreads
[t
].suspended
) {
792 // skip non-menu threads if we are in menu
793 if (inMenu
&& menuCheckVar
>= 0 && menuCheckVar
< VM_VARS_SIZE
&& vmThreads
[t
].tvars
[menuCheckVar
] == 0) continue;
796 for (f
= MAX_INST_COUNT
; f
>= 0; --f
) {
797 int res
= vmExecuteOne(t
);
801 //fprintf(stderr, "!!!\n");
806 vmFreeThread(&vmThreads
[t
]);
811 if (f
< 0) vmfatal(t
, vmThreads
[t
].pc
, "too many instructions in frame");
818 // <0: BRK; >0: END; 0: ok
819 // maxinst<0: any number of instructions
820 int vmExecuteBSR (int tid
, int pc
, int maxinst
) {
823 if (tid
< 0 || tid
>= VM_MAX_THREADS
|| vmThreads
[tid
].stack
== NULL
|| vmThreads
[tid
].suspended
) return 2; // END
824 opc
= vmThreads
[tid
].pc
;
825 vmPush(tid
, -666666);
826 vmThreads
[tid
].pc
= pc
;
827 if (maxinst
== 0) maxinst
= MAX_INST_COUNT
;
829 int res
= vmExecuteOne(tid
);
831 if (!vmThreads
[tid
].stack
) return 1;
832 if (vmThreads
[tid
].suspended
) return -2;
833 if (res
> 0) vmFreeThread(&vmThreads
[tid
]);
834 if (res
!= 0) return res
;
835 if (vmThreads
[tid
].pc
== -666666) {
836 vmThreads
[tid
].pc
= opc
;
839 if (maxinst
== 1) vmfatal(tid
, vmThreads
[tid
].pc
, "too many instructions in vmExecuteBSR");
840 if (maxinst
> 0) --maxinst
;
845 int vmIsThreadAlive (int tid
) {
846 if (tid
>= 0 && tid
< VM_MAX_THREADS
) return vmThreads
[tid
].stack
!= NULL
;
851 int vmNewThread (int pc
) {
852 int tid
= vmFindFreeThread();
855 if (vmInitThread(&vmThreads
[tid
]) != 0) return -1;
856 vmThreads
[tid
].pc
= pc
;
857 if (tid
> vmLastThreadId
) vmLastThreadId
= tid
;
863 int vmKillThread (int tid
) {
864 if (vmIsThreadAlive(tid
)) {
865 vmFreeThread(&vmThreads
[tid
]);
873 int vmIsSuspendedThread (int tid
) {
874 if (vmIsThreadAlive(tid
)) return vmThreads
[tid
].suspended
;
879 int vmSuspendThread (int tid
) {
880 if (vmIsThreadAlive(tid
)) { vmThreads
[tid
].suspended
= 1; return 0; }
885 int vmResumeThread (int tid
) {
886 if (vmIsThreadAlive(tid
)) { vmThreads
[tid
].suspended
= 0; return 0; }
891 int vmGetTVar (int tid
, int idx
) {
892 if (idx
>= 0 && idx
< VM_VARS_SIZE
&& vmIsThreadAlive(tid
)) return vmThreads
[tid
].tvars
[idx
];
897 int vmSetTVar (int tid
, int idx
, int value
) {
898 if (idx
>= 0 && idx
< VM_VARS_SIZE
&& vmIsThreadAlive(tid
)) { vmThreads
[tid
].tvars
[idx
] = value
; return 0; }
903 int vmGetSP (int tid
) {
904 if (vmIsThreadAlive(tid
)) return vmThreads
[tid
].sp
;
909 int vmGetPC (int tid
) {
910 if (vmIsThreadAlive(tid
)) return vmThreads
[tid
].pc
;
915 int vmSetPC (int tid
, int pc
) {
916 if (vmIsThreadAlive(tid
) && pc
>= 0 && pc
< vmCodeSize
) { vmThreads
[tid
].pc
= pc
; return 0; }
921 int vmSetSP (int tid
, int value
) {
922 if (vmIsThreadAlive(tid
)) { vmThreads
[tid
].sp
= value
; return 0; }
927 int vmGetStack (int tid
, int idx
) {
928 if (vmIsThreadAlive(tid
)) {
929 if (idx
< 0) idx
+= vmThreads
[tid
].sp
;
930 if (idx
>= 0 && idx
< VM_STACK_SIZE
) return vmThreads
[tid
].stack
[idx
];
936 int vmSetStack (int tid
, int idx
, int value
) {
937 if (vmIsThreadAlive(tid
)) {
938 if (idx
< 0) idx
+= vmThreads
[tid
].sp
;
939 if (idx
>= 0 && idx
< VM_STACK_SIZE
) { vmThreads
[tid
].stack
[idx
] = value
; return 0; }
945 int vmPush (int tid
, int value
) {
946 if (vmIsThreadAlive(tid
) && vmThreads
[tid
].sp
< VM_STACK_SIZE
) {
947 vmThreads
[tid
].stack
[vmThreads
[tid
].sp
++] = value
;
954 int vmPop (int tid
) {
955 if (vmIsThreadAlive(tid
) && vmThreads
[tid
].sp
> 0) return vmThreads
[tid
].stack
[--vmThreads
[tid
].sp
];
960 int vmLoadArgs (int tid
, int argc
, int argv
[], int *argp
[], int aargc
, int aargv
[], int *aargp
[]) {
961 if (!vmIsThreadAlive(tid
)) return -1;
965 if (e
> aargc
) e
= aargc
;
966 for (int f
= 0; f
< e
; ++f
) { argv
[f
] = aargv
[f
]; argp
[f
] = aargp
[f
]; }
967 for (int f
= e
; f
< argc
; ++f
) {
968 if (vmThreads
[tid
].sp
<= 0) return -1;
970 argv
[argc
-f
] = vmThreads
[tid
].stack
[--vmThreads
[tid
].sp
];
977 int vmLastThread (void) {
978 return vmLastThreadId
;
982 static int readBuf (FILE *fl
, void *buf
, int len
) {
983 unsigned char *c
= (unsigned char *)buf
;
988 if (fread(&b
, 1, 1, fl
) != 1) return -1;
996 static int writeBuf (FILE *fl
, const void *buf
, int len
) {
997 const unsigned char *c
= (const unsigned char *)buf
;
1000 unsigned char b
= *c
++;
1003 if (fwrite(&b
, 1, 1, fl
) != 1) return -1;
1009 static int readDW (FILE *fl
, int *v
) {
1012 for (int f
= 0; f
< 4; ++f
) {
1015 if (fread(&b
, 1, 1, fl
) != 1) return -1;
1021 for (int f
= 0; f
< 4; ++f
) *v
|= t
[f
]<<(8*f
);
1027 static int writeDW (FILE *fl
, int v
) {
1028 for (int f
= 0; f
< 4; ++f
) {
1033 if (fwrite(&b
, 1, 1, fl
) != 1) return -1;
1040 ////////////////////////////////////////////////////////////////////////////////
1041 static VMLabelInfo
*labels
= NULL
;
1044 ////////////////////////////////////////////////////////////////////////////////
1045 void vmFreeLabels (void) {
1046 while (labels
!= NULL
) {
1047 VMLabelInfo
*l
= labels
;
1050 if (l
->name
) free(l
->name
);
1056 void vmFreeLabelsUntilMark (const char *name
) {
1057 while (labels
!= NULL
) {
1058 VMLabelInfo
*l
= labels
;
1059 int isMark
= (l
->type
== LB_MARK
);
1061 if (!(isMark
&& ((name
== NULL
&& l
->name
== NULL
) || (l
->name
!= NULL
&& name
!= NULL
&& strcmp(name
, l
->name
) == 0)))) isMark
= 0;
1063 if (l
->name
) free(l
->name
);
1070 VMLabelInfo
*vmLabelAddMark (const char *name
) {
1071 VMLabelInfo
*l
= malloc(sizeof(VMLabelInfo
));
1073 if (l
== NULL
) fatalis("out of memory");
1076 l
->name
= strdup(name
);
1077 if (l
->name
== NULL
) fatalis("out of memory");
1088 VMLabelInfo
*vmAddLabel (const char *name
, int type
, int value
, int pub
) {
1089 if (name
!= NULL
&& name
[0]) {
1090 VMLabelInfo
*l
= malloc(sizeof(VMLabelInfo
));
1092 if (l
== NULL
) fatalis("out of memory");
1094 l
->name
= strdup(name
);
1095 if (l
->name
== NULL
) fatalis("out of memory");
1110 static VMLabelInfo
*vmAddLabelToEnd (const char *name
, int type
, int value
, int pub
) {
1111 if (name
!= NULL
&& name
[0]) {
1112 VMLabelInfo
*l
= malloc(sizeof(VMLabelInfo
)), *last
= NULL
;
1114 if (l
== NULL
) fatalis("out of memory");
1116 l
->name
= strdup(name
);
1117 if (l
->name
== NULL
) fatalis("out of memory");
1125 if (labels
== NULL
) {
1128 for (last
= labels
; last
->next
!= NULL
; last
= last
->next
) ;
1137 VMLabelInfo
*vmFindLabel (const char *name
) {
1138 if (name
!= NULL
&& name
[0]) {
1139 for (VMLabelInfo
*l
= labels
; l
!= NULL
; l
= l
->next
) {
1140 if (l
->name
&& l
->name
[0] && strcasecmp(l
->name
, name
) == 0) return l
;
1147 int vmFindPC (const char *name
) {
1148 VMLabelInfo
*l
= vmFindLabel(name
);
1150 if (l
== NULL
&& l
->type
!= LB_CODE
) fatalis("no code label: '%s'", name
);
1155 int vmFindVarIndex (const char *name
) {
1156 VMLabelInfo
*l
= vmFindLabel(name
);
1158 if (l
== NULL
|| (l
->type
!= LB_GVAR
&& l
->type
!= LB_TVAR
&& l
->type
!= LB_SVAR
)) fatalis("no var label: '%s'", name
);
1163 int vmFindGVarIndex (const char *name
) {
1164 VMLabelInfo
*l
= vmFindLabel(name
);
1166 if (l
== NULL
|| l
->type
!= LB_GVAR
) fatalis("no global var label: '%s'", name
);
1171 int vmFindTVarIndex (const char *name
) {
1172 VMLabelInfo
*l
= vmFindLabel(name
);
1174 if (l
== NULL
|| l
->type
!= LB_TVAR
) fatalis("no thread var label: '%s'", name
);
1179 int vmFindSVarIndex (const char *name
) {
1180 VMLabelInfo
*l
= vmFindLabel(name
);
1182 if (l
== NULL
|| l
->type
!= LB_SVAR
) fatalis("no thread var label: '%s'", name
);
1187 int vmFindConst (const char *name
) {
1188 VMLabelInfo
*l
= vmFindLabel(name
);
1190 if (l
== NULL
|| l
->type
!= LB_CONST
) fatalis("no const label: '%s'", name
);
1195 VMLabelInfo
*vmFindMark (const char *name
) {
1196 for (VMLabelInfo
*l
= labels
; l
!= NULL
; l
= l
->next
) {
1197 if (l
->type
!= LB_MARK
) continue;
1198 if (name
== NULL
&& l
->name
== NULL
) return l
;
1199 if (name
!= NULL
&& l
->name
!= NULL
&& strcmp(name
, l
->name
) == 0) return l
;
1205 #define XREAD(dest,size,errlabel) do { \
1206 if (pos+(size) > rsz) goto errlabel; \
1207 if ((size) > 0) memcpy((dest), buf+pos, (size)); \
1212 #define XREADB(dest,errlabel) do { \
1213 if (pos+1 > rsz) goto errlabel; \
1214 (dest) = buf[pos]; \
1219 #define XREADW(dest,errlabel) do { \
1220 if (pos+2 > rsz) goto errlabel; \
1221 (dest) = buf[pos+1]; \
1223 (dest) |= buf[pos+0]; \
1229 extern int vmLoadCodeFileFromDump (const void *data
, int datasize
, int pc
, int gvfirst
, int tvfirst
, int *gvmax
, int *tvmax
) {
1230 int rsz
= datasize
, pos
= 4;
1231 const uint8_t *dta
= (const uint8_t *)data
;
1233 int csize
, lcnt
, rcnt
, elcnt
, tlast
, glast
;
1235 if (rsz
< 4) return -1;
1236 if (memcmp(data
, "AVM2", 4) != 0) goto quitbufonly
;
1238 if (buf
== NULL
) return 1;
1240 for (int f
= 4; f
< rsz
; ++f
) buf
[f
] = dta
[f
]^SECRET
;
1242 XREADW(csize
, quitbufonly
);
1243 XREADW(rcnt
, quitbufonly
); // fixups
1244 XREADW(elcnt
, quitbufonly
); // extern labels
1245 XREADW(lcnt
, quitbufonly
); // labels
1246 XREADW(glast
, quitbufonly
); // last used global
1247 XREADW(tlast
, quitbufonly
); // last used thread local
1248 if (pc
< 0 || pc
+csize
> 65535) goto quitbufonly
;
1250 /*if (goobers)*/ //fprintf(stderr, "code: %d bytes, %d public labels, %d relocations, %d externs\n", csize, lcnt, rcnt, elcnt);
1252 XREAD(vmCode
+pc
, csize
, quitbufonly
);
1255 for (int f
= 0; f
< rcnt
; ++f
) {
1256 int rel
, newpc
, val
;
1258 XREADW(rel
, quitbufonly
);
1259 if (rel
< 0 || rel
+1 >= csize
) goto quitbufonly
;
1260 newpc
= pc
+rel
; // fix here
1262 val
= vmCode
[newpc
+1];
1264 val
|= vmCode
[newpc
+0];
1268 vmCode
[newpc
+0] = val
&0xff;
1269 vmCode
[newpc
+1] = (val
>>8)&0xff;
1272 // load extern labels and perform fixups
1273 for (int f
= 0; f
< elcnt
; ++f
) {
1278 XREADB(type
, quitbufonly
);
1279 XREADB(namelen
, quitbufonly
);
1280 XREAD(name
, namelen
, quitbufonly
);
1281 XREADW(rcnt
, quitbufonly
);
1283 if (type
< LB_MIN_TYPE
|| type
> LB_MAX_TYPE
) goto quitbufonly
;
1284 if (namelen
< 1) goto quitbufonly
;
1287 for (int c
= 0; c
< rcnt
; ++c
) {
1291 XREADB(size
, quitbufonly
);
1292 XREADW(xpc
, quitbufonly
);
1294 if (size
!= 1 && size
!= 2) goto quitbufonly
;
1295 if (xpc
< 0 || xpc
+size
> csize
) goto quitbufonly
;
1298 l
= vmFindLabel(name
);
1300 /*if (goobers)*/ fprintf(stderr
, "VM: unknown extern: '%s'\n", name
);
1304 if (l
->type
== LB_GVAR
) val
|= 0x80;
1305 //fprintf(stderr, "%d: [%s]: ofs=%d, size=%d, value=%d (%d)\n", c, l->name, xpc, size, val, vmCode[xpc]);
1306 if (size
== 1 && (val
< -128 || val
> 255)) {
1307 /*if (goobers)*/ fprintf(stderr
, "VM: extern too big: '%s'\n", name
);
1310 vmCode
[xpc
+0] = val
&0xff;
1311 if (size
== 2) vmCode
[xpc
+1] = (val
>>8)&0xff;
1315 vmLabelAddMark(NULL
); // for this code labels
1316 for (int f
= 0; f
< lcnt
; ++f
) {
1317 unsigned char type
, namelen
, b
;
1323 switch (type
&0x7f) {
1331 XREADW(value
, quit
);
1332 if (value
>= 32768) value
-= 65536;
1335 XREADW(value
, quit
);
1336 value
+= pc
; // fixup
1339 fatalis("invalid label type: %d\n", type
);
1343 XREADB(namelen
, quit
);
1344 XREAD(name
, namelen
, quit
);
1347 if (namelen
> 0) vmAddLabel(name
, type
&0x7f, value
, type
&0x80?1:0);
1348 //if (l->type == LB_CODE) printf("%d/%d: [%s] %d : %d\n", f, lcnt, l->name, l->value-pc, l->value);
1360 //fprintf(stderr, "GVFIX at 0x%04x: %d --> %d\n", fpc, vmCode[pc+fpc], vmCode[pc+fpc]+gvfirst);
1361 vmCode
[pc
+fpc
] += gvfirst
;
1371 //fprintf(stderr, "TVFIX at 0x%04x: %d --> %d\n", fpc, vmCode[pc+fpc], vmCode[pc+fpc]+tvfirst);
1372 vmCode
[pc
+fpc
] += tvfirst
;
1376 if (gvmax
) *gvmax
= glast
+gvfirst
+1;
1377 if (tvmax
) *tvmax
= tlast
+tvfirst
+1;
1382 vmFreeLabelsUntilMark(NULL
);
1389 ////////////////////////////////////////////////////////////////////////////////
1390 int vmSaveState (FILE *fl
) {
1395 if (writeDW(fl
, VM_VARS_SIZE
) != 0) return -1;
1396 //if (writeDW(fl, VM_STACK_SIZE) != 0) return -1;
1397 if (writeDW(fl
, vmMaxGVar
) != 0) return -1;
1398 if (writeDW(fl
, vmMaxTVar
) != 0) return -1;
1399 if (writeDW(fl
, vmLastThreadId
) != 0) return -1;
1400 if (writeDW(fl
, vmCodeSize
) != 0) return -1;
1401 if (writeBuf(fl
, vmCode
, vmCodeSize
) != 0) return -1;
1402 for (int f
= 0; f
< VM_VARS_SIZE
; ++f
) if (writeDW(fl
, vmGVars
[f
]) != 0) return -1;
1403 for (int f
= 0; f
<= vmLastThreadId
; ++f
) {
1404 if (vmThreads
[f
].stack
!= NULL
) {
1405 if (writeDW(fl
, f
) != 0) return -1;
1406 if (writeDW(fl
, vmThreads
[f
].pc
) != 0) return -1;
1407 if (writeDW(fl
, vmThreads
[f
].suspended
) != 0) return -1;
1408 if (writeDW(fl
, vmThreads
[f
].sp
) != 0) return -1;
1409 for (int c
= 0; c
< vmThreads
[f
].sp
; ++c
) if (writeDW(fl
, vmThreads
[f
].stack
[c
]) != 0) return -1;
1410 for (int c
= 0; c
< VM_VARS_SIZE
; ++c
) if (writeDW(fl
, vmThreads
[f
].tvars
[c
]) != 0) return -1;
1412 if (writeDW(fl
, -1) != 0) return -1;
1416 for (VMLabelInfo
*l
= labels
; l
!= NULL
; l
= l
->next
) ++lcnt
;
1417 if (writeDW(fl
, lcnt
) != 0) return -1;
1418 for (VMLabelInfo
*l
= labels
; l
!= NULL
; l
= l
->next
) {
1419 int nlen
= l
->name
!= NULL
? strlen(l
->name
) : -1;
1421 if (writeDW(fl
, l
->type
) != 0) return -1;
1422 if (writeDW(fl
, nlen
) != 0) return -1;
1424 if (writeBuf(fl
, l
->name
, nlen
) != 0) return -1;
1426 if (writeDW(fl
, l
->value
) != 0) return -1;
1427 if (writeDW(fl
, l
->pub
) != 0) return -1;
1434 int vmLoadState (FILE *fl
) {
1438 if (vmInitialize() != 0) goto fail
;
1440 if (readDW(fl
, &v
) != 0 || v
!= VM_VARS_SIZE
) goto fail
;
1441 //if (readDW(fl, &ssz) != 0) goto fail;
1442 if (readDW(fl
, &vmMaxGVar
) != 0 || vmMaxGVar
< 0 || vmMaxGVar
> VM_VARS_SIZE
) goto fail
;
1443 if (readDW(fl
, &vmMaxTVar
) != 0 || vmMaxTVar
< 0 || vmMaxTVar
> VM_VARS_SIZE
) goto fail
;
1444 if (readDW(fl
, &vmLastThreadId
) != 0 || vmLastThreadId
< 0 || vmLastThreadId
> VM_MAX_THREADS
) goto fail
;
1445 if (readDW(fl
, &vmCodeSize
) != 0 || vmCodeSize
< 1 || vmCodeSize
> 65536) goto fail
;
1446 if (readBuf(fl
, vmCode
, vmCodeSize
) != 0) goto fail
;
1447 for (int f
= 0; f
< VM_VARS_SIZE
; ++f
) if (readDW(fl
, &vmGVars
[f
]) != 0) goto fail
;
1448 for (int f
= 0; f
<= vmLastThreadId
; ++f
) {
1451 if (readDW(fl
, &flag
) != 0) goto fail
;
1452 if (flag
== -1) continue;
1453 if (flag
!= f
) goto fail
;
1454 if (vmInitThread(&vmThreads
[f
]) != 0) goto fail
;
1455 if (readDW(fl
, &vmThreads
[f
].pc
) != 0) goto fail
;
1456 if (readDW(fl
, &vmThreads
[f
].suspended
) != 0) goto fail
;
1457 if (readDW(fl
, &vmThreads
[f
].sp
) != 0) goto fail
;
1458 if (!vmThreads
[f
].suspended
) {
1459 if (vmThreads
[f
].pc
< 0 || vmThreads
[f
].pc
>= vmCodeSize
) goto fail
;
1460 if (vmThreads
[f
].sp
< 0 || vmThreads
[f
].sp
> VM_STACK_SIZE
) goto fail
;
1462 for (int c
= 0; c
< vmThreads
[f
].sp
; ++c
) if (readDW(fl
, &vmThreads
[f
].stack
[c
]) != 0) goto fail
;
1463 for (int c
= 0; c
< VM_VARS_SIZE
; ++c
) if (readDW(fl
, &vmThreads
[f
].tvars
[c
]) != 0) goto fail
;
1466 if (readDW(fl
, &lcnt
) != 0) goto fail
;
1467 for (; lcnt
> 0; --lcnt
) {
1468 int type
, namelen
, value
, pub
;
1471 if (readDW(fl
, &type
) != 0) goto fail
;
1472 if (readDW(fl
, &namelen
) != 0) goto fail
;
1473 if (namelen
> 255) goto fail
;
1475 if (readBuf(fl
, name
, namelen
) != 0) goto fail
;
1478 if (readDW(fl
, &value
) != 0) goto fail
;
1479 if (readDW(fl
, &pub
) != 0) goto fail
;
1480 vmAddLabelToEnd(namelen
>=0 ? name
: NULL
, type
, value
, pub
);