agssim: document mar register use
[rofl0r-agsutils.git] / agssim.c
blob40a4b52fb1fff35213f4cf7e1311e61675cdfc6e
1 #include <stdio.h>
2 #include <ctype.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include <assert.h>
7 #include "ags_cpu.h"
8 #include "version.h"
9 #define ADS ":::AGSSim " VERSION " by rofl0r:::"
11 enum RegisterAccess {
12 RA_NONE = 0,
13 RA_READ = 1 << 0,
14 RA_WRITE = 1 << 1,
15 RA_READWRITE = 1 << 2,
18 struct regaccess_info {
19 /* enum RegisterAccess */ unsigned char ra_reg1;
20 /* enum RegisterAccess */ unsigned char ra_reg2;
21 /* enum RegisterAccess */ unsigned char ra_mar;
22 } __attribute__((packed));
24 static const struct regaccess_info regaccess_info[] = {
25 [0] = {RA_NONE, RA_NONE, RA_NONE},
26 [SCMD_ADD] = {RA_READWRITE, RA_NONE, RA_NONE},
27 [SCMD_SUB] = {RA_READWRITE, RA_NONE, RA_NONE},
28 [SCMD_REGTOREG] = {RA_READ, RA_WRITE, RA_NONE},
29 [SCMD_WRITELIT] = {RA_NONE, RA_NONE, RA_READ},
30 [SCMD_RET] = {RA_NONE, RA_NONE, RA_NONE},
31 [SCMD_LITTOREG] = {RA_WRITE, RA_NONE, RA_NONE},
32 [SCMD_MEMREAD] = {RA_WRITE, RA_NONE, RA_READ},
33 [SCMD_MEMWRITE] = {RA_READ, RA_NONE, RA_READ},
34 [SCMD_MULREG] = {RA_READWRITE, RA_READ, RA_NONE},
35 [SCMD_DIVREG] = {RA_READWRITE, RA_READ, RA_NONE},
36 [SCMD_ADDREG] = {RA_READWRITE, RA_READ, RA_NONE},
37 [SCMD_SUBREG] = {RA_READWRITE, RA_READ, RA_NONE},
38 [SCMD_BITAND] = {RA_READWRITE, RA_READ, RA_NONE},
39 [SCMD_BITOR] = {RA_READWRITE, RA_READ, RA_NONE},
40 [SCMD_ISEQUAL] = {RA_READWRITE, RA_READ, RA_NONE},
41 [SCMD_NOTEQUAL] = {RA_READWRITE, RA_READ, RA_NONE},
42 [SCMD_GREATER] = {RA_READWRITE, RA_READ, RA_NONE},
43 [SCMD_LESSTHAN] = {RA_READWRITE, RA_READ, RA_NONE},
44 [SCMD_GTE] = {RA_READWRITE, RA_READ, RA_NONE},
45 [SCMD_LTE] = {RA_READWRITE, RA_READ, RA_NONE},
46 [SCMD_AND] = {RA_READWRITE, RA_READ, RA_NONE}, /*logical*/
47 [SCMD_OR] = {RA_READWRITE, RA_READ, RA_NONE},
48 [SCMD_CALL] = {RA_READ, RA_NONE, RA_NONE},
49 [SCMD_MEMREADB] = {RA_WRITE, RA_NONE, RA_READ},
50 [SCMD_MEMREADW] = {RA_WRITE, RA_NONE, RA_READ},
51 [SCMD_MEMWRITEB] = {RA_READ, RA_NONE, RA_READ},
52 [SCMD_MEMWRITEW] = {RA_READ, RA_NONE, RA_READ},
53 [SCMD_JZ] = {RA_READ, RA_NONE, RA_NONE},
54 [SCMD_PUSHREG] = {RA_READ, RA_NONE, RA_NONE},
55 [SCMD_POPREG] = {RA_WRITE, RA_NONE, RA_NONE},
56 [SCMD_JMP] = {RA_READ, RA_NONE, RA_NONE},
57 [SCMD_MUL] = {RA_READWRITE, RA_NONE, RA_NONE},
58 [SCMD_CALLEXT] = {RA_READ, RA_NONE, RA_NONE},
59 [SCMD_PUSHREAL] = {RA_READ, RA_NONE, RA_NONE},
60 [SCMD_SUBREALSTACK] = {RA_READ, RA_NONE, RA_NONE},
61 [SCMD_LINENUM] = {RA_NONE, RA_NONE, RA_NONE},
62 [SCMD_CALLAS] = {RA_READ, RA_NONE, RA_NONE},
63 [SCMD_THISBASE] = {RA_NONE, RA_NONE, RA_NONE},
64 [SCMD_NUMFUNCARGS] = {RA_NONE, RA_NONE, RA_NONE},
65 [SCMD_MODREG] = {RA_READWRITE, RA_READ, RA_NONE},
66 [SCMD_XORREG] = {RA_READWRITE, RA_READ, RA_NONE},
67 [SCMD_NOTREG] = {RA_READWRITE, RA_READ, RA_NONE},
68 [SCMD_SHIFTLEFT] = {RA_READWRITE, RA_READ, RA_NONE},
69 [SCMD_SHIFTRIGHT] = {RA_READWRITE, RA_READ, RA_NONE},
70 [SCMD_CALLOBJ] = {RA_READ, RA_NONE, RA_NONE},
71 [SCMD_CHECKBOUNDS] = {RA_READ, RA_NONE, RA_NONE},
72 [SCMD_MEMWRITEPTR] = {RA_NONE, RA_NONE, RA_NONE}, //TODO
73 [SCMD_MEMREADPTR] = {RA_NONE, RA_NONE, RA_NONE}, //TODO
74 [SCMD_MEMZEROPTR] = {RA_NONE, RA_NONE, RA_NONE},
75 [SCMD_MEMINITPTR] = {RA_NONE, RA_NONE, RA_NONE}, //TODO
76 [SCMD_LOADSPOFFS] = {RA_NONE, RA_NONE, RA_WRITE},
77 [SCMD_CHECKNULL] = {RA_NONE, RA_NONE, RA_NONE},
78 [SCMD_FADD] = {RA_READWRITE, RA_NONE, RA_NONE},
79 [SCMD_FSUB] = {RA_READWRITE, RA_NONE, RA_NONE},
80 [SCMD_FMULREG] = {RA_READWRITE, RA_READ, RA_NONE},
81 [SCMD_FDIVREG] = {RA_READWRITE, RA_READ, RA_NONE},
82 [SCMD_FADDREG] = {RA_READWRITE, RA_READ, RA_NONE},
83 [SCMD_FSUBREG] = {RA_READWRITE, RA_READ, RA_NONE},
84 [SCMD_FGREATER] = {RA_READWRITE, RA_READ, RA_NONE},
85 [SCMD_FLESSTHAN] = {RA_READWRITE, RA_READ, RA_NONE},
86 [SCMD_FGTE] = {RA_READWRITE, RA_READ, RA_NONE},
87 [SCMD_FLTE] = {RA_READWRITE, RA_READ, RA_NONE},
88 [SCMD_ZEROMEMORY] = {RA_NONE, RA_NONE, RA_READ},
89 [SCMD_CREATESTRING] = {RA_NONE, RA_NONE, RA_NONE}, //TODO
90 [SCMD_STRINGSEQUAL] = {RA_READWRITE, RA_READ, RA_NONE},
91 [SCMD_STRINGSNOTEQ] = {RA_READWRITE, RA_READ, RA_NONE},
92 [SCMD_CHECKNULLREG] = {RA_NONE, RA_NONE, RA_NONE}, //TODO
93 [SCMD_LOOPCHECKOFF] = {RA_NONE, RA_NONE, RA_NONE},
94 [SCMD_MEMZEROPTRND] = {RA_NONE, RA_NONE, RA_READ},
95 [SCMD_JNZ] = {RA_NONE, RA_NONE, RA_NONE},
96 [SCMD_DYNAMICBOUNDS] = {RA_NONE, RA_NONE, RA_NONE}, //TODO
97 [SCMD_NEWARRAY] = {RA_NONE, RA_NONE, RA_NONE}, //TODO
100 #ifndef MAX
101 #define MAX(a, b) ((a) > (b) ? (a) : (b))
102 #define MIN(a, b) ((a) < (b) ? (a) : (b))
103 #endif
105 /* TODO: move duplicate code from Assembler.c into separate TU */
106 static int get_reg(char* regname) {
107 int i = AR_NULL + 1;
108 for(; i < AR_MAX; i++)
109 if(strcmp(regnames[i], regname) == 0)
110 return i;
111 return AR_NULL;
114 static size_t mnemolen[SCMD_MAX];
115 static int mnemolen_initdone = 0;
117 static void init_mnemolen(void) {
118 size_t i = 0;
119 for(; i< SCMD_MAX; i++)
120 mnemolen[i] = strlen(opcodes[i].mnemonic);
121 mnemolen_initdone = 1;
124 static unsigned find_insn(char* sym) {
125 if(!mnemolen_initdone) init_mnemolen();
126 size_t i = 0, l = strlen(sym);
127 for(; i< SCMD_MAX; i++)
128 if(l == mnemolen[i] && memcmp(sym, opcodes[i].mnemonic, l) == 0)
129 return i;
130 return 0;
133 #include "StringEscape.h"
134 /* expects a pointer to the first char after a opening " in a string,
135 * converts the string into convbuf, and returns the length of that string */
136 static size_t get_length_and_convert(char* x, char* end, char* convbuf, size_t convbuflen) {
137 size_t result = 0;
138 char* e = x + strlen(x);
139 assert(e > x && e < end && *e == 0);
140 e--;
141 while(isspace(*e)) e--;
142 if(*e != '"') return (size_t) -1;
143 *e = 0;
144 result = unescape(x, convbuf, convbuflen);
145 return result;
148 /* sets lets char in arg to 0, and advances pointer till the next argstart */
149 static char* finalize_arg(char **p, char* pend, char* convbuf, size_t convbuflen) {
150 if(**p == '"') {
151 convbuf[0] = '"';
152 size_t l= get_length_and_convert(*p + 1, pend, convbuf+1, convbuflen - 1);
153 if(l == (size_t) -1) return 0;
154 convbuf[l+1] = '"';
155 convbuf[l+2] = 0;
156 *p = 0; /* make it crash if its accessed again, since a string should always be the last arg */
157 return convbuf;
158 } else {
159 char* ret = *p;
160 while(*p < pend && **p != ',' && !isspace(**p)) (*p)++;
161 assert(*p < pend);
162 **p = 0; (*p)++;
163 while(*p < pend && isspace(**p)) (*p)++;
164 assert(*p < pend);
165 return ret;
170 static struct text_segment {
171 int *code;
172 size_t len;
173 size_t capa;
174 } text;
176 enum RegisterUsage {
177 RU_NONE = 0,
178 RU_READ = 1 << 0,
179 RU_WRITE = 1 << 1,
180 RU_WRITE_AFTER_READ = 1 << 2,
183 static struct rval {
184 union {
185 int i;
186 float f;
188 enum RegisterUsage ru;
189 } registers[AR_MAX];
191 static unsigned char stack_mem[1000*4];
192 #define memory stack_mem
194 static int canread(int index, int cnt) {
195 return index >= 0 && index+cnt < sizeof(memory)/sizeof(memory[0]);
198 static void grow_text(size_t req) {
199 if(text.len + req > text.capa) {
200 text.code = realloc(text.code, (text.capa+1024)*sizeof(int));
201 text.capa += 1024;
205 static void append_code(int *code, size_t cnt) {
206 grow_text(cnt);
207 size_t i;
208 for(i = 0; i < cnt; i++) {
209 text.code[text.len++] = code[i];
213 static void vm_init() {
214 size_t i;
215 /* initialize registers to an easily recognisable junk value */
216 for(i = AR_NULL + 1; i < AR_MAX; i++) {
217 registers[i].i = 2222222222;
218 registers[i].ru = RU_NONE;
220 registers[AR_SP].i = 0;
221 registers[AR_NULL].i = 0;
222 /* set up EIP so vm_state() doesn't crash */
223 grow_text(16);
226 static inline int consume_int(int **eip) {
227 *eip = *eip+1;
228 return **eip;
231 static void change_reg_usage(int regno, enum RegisterAccess ra) {
232 enum RegisterUsage ru = registers[regno].ru;
233 switch(ra) {
234 case RA_READ:
235 if(ru == RU_NONE || ru == RU_READ) ru = RU_READ;
236 else if(ru == RU_WRITE);
237 else if(ru == RU_WRITE_AFTER_READ);
238 break;
239 case RA_WRITE:
240 if(ru == RU_NONE || ru == RU_WRITE) ru = RU_WRITE;
241 else if(ru == RU_READ) ru = RU_WRITE_AFTER_READ;
242 else if(ru == RU_WRITE_AFTER_READ);
243 break;
244 case RA_READWRITE:
245 if(ru == RU_NONE || ru == RU_READ) ru = RU_WRITE_AFTER_READ;
246 else if(ru == RU_WRITE);
247 else if(ru == RU_WRITE_AFTER_READ);
248 break;
250 registers[regno].ru = ru;
253 static void vm_update_register_usage(int *eip) {
254 const struct regaccess_info *ri = &regaccess_info[*eip];
255 if(ri->ra_reg1) change_reg_usage(eip[1], ri->ra_reg1);
256 if(ri->ra_reg2) change_reg_usage(eip[2], ri->ra_reg2);
257 if(ri->ra_mar) change_reg_usage(AR_MAR, ri->ra_mar);
260 static void write_mem1(int off, int val) {
261 unsigned char *m = (void*) memory;
262 m[off] = val&0xff;
264 static void write_mem2(int off, int val) {
265 unsigned short *m = (void*) memory;
266 m[off/2] = val&0xffff;
268 static void write_mem(int off, int val) {
269 int *m = (void*) memory;
270 m[off/4] = val;
273 static int read_mem(int off) {
274 int *m = (void*) memory;
275 return m[off/4];
278 #define CODE_INT(X) eip[X]
279 #define CODE_FLOAT(X) ((float*)eip)[X]
280 #define REGI(X) registers[CODE_INT(X)].i
281 #define REGF(X) registers[CODE_INT(X)].f
283 static void vm_step() {
284 /* we use register AR_NULL as instruction pointer */
285 int *eip = &text.code[registers[AR_NULL].i];
286 int eip_inc = 1 + opcodes[*eip].argcount;
287 int tmp, val;
288 vm_update_register_usage(eip);
290 switch(*eip) {
291 case 0:
292 /* don't modify IP */
293 dprintf(2, "no code at IP.\n");
294 return;
295 case SCMD_ADD:
296 REGI(1) += CODE_INT(2);
297 break;
298 case SCMD_SUB:
299 REGI(1) -= CODE_INT(2);
300 break;
301 case SCMD_REGTOREG:
302 REGI(2) = REGI(1);
303 break;
304 case SCMD_LITTOREG:
305 REGI(1) = CODE_INT(2);
306 break;
307 case SCMD_MULREG:
308 REGI(1) *= REGI(2);
309 break;
310 case SCMD_DIVREG:
311 REGI(1) /= REGI(2);
312 break;
313 case SCMD_ADDREG:
314 REGI(1) += REGI(2);
315 break;
316 case SCMD_SUBREG:
317 REGI(1) -= REGI(2);
318 break;
319 case SCMD_BITAND:
320 REGI(1) &= REGI(2);
321 break;
322 case SCMD_BITOR:
323 REGI(1) &= REGI(2);
324 break;
325 case SCMD_ISEQUAL:
326 REGI(1) = !!(REGI(1) == REGI(2));
327 break;
328 case SCMD_NOTEQUAL:
329 REGI(1) = !!(REGI(1) != REGI(2));
330 break;
331 case SCMD_GREATER:
332 REGI(1) = !!(REGI(1) > REGI(2));
333 break;
334 case SCMD_LESSTHAN:
335 REGI(1) = !!(REGI(1) < REGI(2));
336 break;
337 case SCMD_GTE:
338 REGI(1) = !!(REGI(1) >= REGI(2));
339 break;
340 case SCMD_LTE:
341 REGI(1) = !!(REGI(1) <= REGI(2));
342 break;
343 case SCMD_AND:
344 REGI(1) = !!(REGI(1) && REGI(2));
345 break;
346 case SCMD_OR:
347 REGI(1) = !!(REGI(1) || REGI(2));
348 break;
349 case SCMD_LOADSPOFFS:
350 registers[AR_MAR].i = registers[AR_SP].i - CODE_INT(1);
351 break;
352 case SCMD_PUSHREG:
353 write_mem(registers[AR_SP].i, REGI(1));
354 registers[AR_SP].i += 4;
355 break;
356 case SCMD_POPREG:
357 registers[AR_SP].i -= 4;
358 REGI(1) = read_mem(registers[AR_SP].i);
359 break;
360 case SCMD_MUL:
361 REGI(1) *= CODE_INT(2);
362 break;
363 case SCMD_THISBASE:
364 case SCMD_LINENUM:
365 break;
366 case SCMD_MODREG:
367 REGI(1) %= REGI(2);
368 break;
369 case SCMD_XORREG:
370 REGI(1) ^= REGI(2);
371 break;
372 case SCMD_NOTREG:
373 REGI(1) = !REGI(2);
374 break;
375 case SCMD_SHIFTLEFT:
376 REGI(1) <<= REGI(2);
377 break;
378 case SCMD_SHIFTRIGHT:
379 REGI(1) >>= REGI(2);
380 break;
381 case SCMD_FADD:
382 REGF(1) += CODE_FLOAT(2);
383 break;
384 case SCMD_FSUB:
385 REGF(1) -= CODE_FLOAT(2);
386 break;
387 case SCMD_FMULREG:
388 REGF(1) *= REGF(2);
389 break;
390 case SCMD_FDIVREG:
391 REGF(1) /= REGF(2);
392 break;
393 case SCMD_FADDREG:
394 REGF(1) += REGF(2);
395 break;
396 case SCMD_FSUBREG:
397 REGF(1) -= REGF(2);
398 break;
399 case SCMD_FGREATER:
400 REGI(1) = !!(REGF(1) > REGF(2));
401 break;
402 case SCMD_FLESSTHAN:
403 REGI(1) = !!(REGF(1) < REGF(2));
404 break;
405 case SCMD_FGTE:
406 REGI(1) = !!(REGF(1) >= REGF(2));
407 break;
408 case SCMD_FLTE:
409 REGI(1) = !!(REGF(1) <= REGF(2));
410 break;
411 case SCMD_WRITELIT:
412 tmp = CODE_INT(1);
413 if(tmp <= 0 || tmp > 4 || tmp == 3) {
414 dprintf(2, "invalid memcpy use\n");
415 break;
417 val = CODE_INT(2);
418 goto mwrite;
419 case SCMD_MEMWRITE:
420 tmp = 4;
421 val = REGI(1);
422 goto mwrite;
423 case SCMD_MEMWRITEW:
424 tmp = 2;
425 val = REGI(1);
426 goto mwrite;
427 case SCMD_MEMWRITEB:
428 tmp = 1;
429 val = REGI(1);
430 mwrite:
431 if(canread(registers[AR_MAR].i, tmp)) {
432 switch(tmp) {
433 case 4: write_mem (registers[AR_MAR].i, val); break;
434 case 2: write_mem2(registers[AR_MAR].i, val); break;
435 case 1: write_mem1(registers[AR_MAR].i, val); break;
437 } else {
438 dprintf(2, "info: caught OOB memwrite\n");
440 break;
441 case SCMD_MEMREAD:
442 tmp = 4;
443 goto mread;
444 case SCMD_MEMREADW:
445 tmp = 2;
446 goto mread;
447 case SCMD_MEMREADB:
448 tmp = 1;
449 mread:
450 if(canread(registers[AR_MAR].i, tmp)) {
451 int val = memory[registers[AR_MAR].i];
452 switch(tmp) {
453 case 4: REGI(1) = val; break;
454 case 2: REGI(1) = val & 0xffff; break;
455 case 1: REGI(1) = val & 0xff; break;
457 } else {
458 dprintf(2, "info: caught OOB memread\n");
460 break;
461 case SCMD_NEWARRAY:
462 case SCMD_DYNAMICBOUNDS:
463 case SCMD_JNZ:
464 case SCMD_MEMZEROPTRND:
465 case SCMD_LOOPCHECKOFF:
466 case SCMD_CHECKNULLREG:
467 case SCMD_STRINGSNOTEQ:
468 case SCMD_STRINGSEQUAL:
469 case SCMD_CREATESTRING:
470 case SCMD_ZEROMEMORY:
471 case SCMD_CHECKNULL:
472 case SCMD_MEMINITPTR:
473 case SCMD_MEMZEROPTR:
474 case SCMD_MEMREADPTR:
475 case SCMD_MEMWRITEPTR:
476 case SCMD_CHECKBOUNDS:
477 case SCMD_CALLOBJ:
478 case SCMD_NUMFUNCARGS:
479 case SCMD_CALLAS:
480 case SCMD_SUBREALSTACK:
481 case SCMD_PUSHREAL:
482 case SCMD_CALLEXT:
483 case SCMD_JMP:
484 case SCMD_JZ:
485 case SCMD_CALL:
486 case SCMD_RET:
487 default:
488 dprintf(2, "info: %s not implemented yet\n", opcodes[*eip].mnemonic);
490 size_t i, l = opcodes[*eip].argcount;
491 for(i = 0; i < l; i++) ++(*eip);
493 break;
495 registers[AR_NULL].i += eip_inc;
498 static inline char *int_to_str(int value, char* out) {
499 sprintf(out, "%d", value);
500 return out;
503 static void vm_state() {
504 static const char ru_strings[][3] = {
505 [RU_NONE] = {0},
506 [RU_READ] = {'R', 0},
507 [RU_WRITE] = {'W', 0},
508 [RU_WRITE_AFTER_READ] = {'R', 'W', 0},
510 size_t i;
511 for(i=0; i< AR_MAX; i++)
512 printf("%s: %2s %d\n", i == 0 ? "eip" : regnames[i], ru_strings[registers[i].ru], registers[i].i);
514 for( i = MIN(registers[AR_SP].i+2*4, sizeof(stack_mem)/4);
515 i >= MAX(registers[AR_SP].i-2*4, 0);
516 i-=4) {
517 printf("SL %s %3zu %d\n", i == registers[AR_SP].i ? ">" : " ", i, read_mem(i));
518 if(i == 0) break;
521 int *eip = &text.code[registers[AR_NULL].i];
522 char arg1buf[32], arg2buf[32];
523 const char *arg1 = opcodes[*eip].argcount == 0 ? "" : \
524 (opcodes[*eip].regcount > 0 ? regnames[eip[1]] : int_to_str(eip[1], arg1buf));
525 const char *arg2 = opcodes[*eip].argcount < 2 ? "" : \
526 (opcodes[*eip].regcount > 1 ? regnames[eip[2]] : int_to_str(eip[2], arg2buf));
527 printf(" > %s %s %s\n", opcodes[*eip].mnemonic, arg1, arg2);
530 void vm_run(void) {
531 while(1) {
532 int *eip = &text.code[registers[AR_NULL].i];
533 if(!*eip) break;
534 vm_step();
538 static int usage(int fd, char *a0) {
539 dprintf(fd,
540 "%s - simple ags vm simulator\n"
541 "implements the ALU and a small stack\n"
542 "useful to examine how a chunk of code modifies VM state\n"
543 "not implemented: memory access apart from stack, jumps, functions\n"
544 "supply the assembly code via stdin, then type one of the following\n"
545 "commands:\n"
546 "!i - reset VM state and IP\n"
547 "!s - single-step\n"
548 "!r - run\n"
549 , a0);
550 return 1;
553 static int lastcommand;
554 enum UserCommand {
555 UC_STEP = 1,
556 UC_RUN,
557 UC_INIT,
558 UC_QUIT,
559 UC_HELP,
561 static void execute_user_command_i(int uc) {
562 switch(uc) {
563 case UC_STEP: vm_step(); break;
564 case UC_RUN : vm_run(); break;
565 case UC_INIT: vm_init(); break;
566 case UC_QUIT: exit(0); break;
567 case UC_HELP: usage(1, "agssim"); break;
569 lastcommand = uc;
570 vm_state();
572 static void execute_user_command(char *cmd) {
573 int uc = 0;
574 if(0) ;
575 else if(!strcmp(cmd, "s")) uc = UC_STEP;
576 else if(!strcmp(cmd, "r")) uc = UC_RUN;
577 else if(!strcmp(cmd, "i")) uc = UC_INIT;
578 else if(!strcmp(cmd, "q")) uc = UC_QUIT;
579 else if(!strcmp(cmd, "h")) uc = UC_HELP;
580 else {
581 dprintf(2, "unknown command\n");
582 return;
584 execute_user_command_i(uc);
587 int main(int argc, char** argv) {
588 if(argc != 1) return usage(2, argv[0]);
589 char buf[1024], *sym;
590 char convbuf[sizeof(buf)]; /* to convert escaped string into non-escaped version */
591 int lineno = 0;
592 vm_init();
593 printf(ADS " - type !h for help\n");
594 while(fgets(buf, sizeof buf, stdin)) {
595 int code[3];
596 size_t pos = 0;
597 lineno++;
598 char* p = buf, *pend = buf + sizeof buf;
599 if(*p == '\n' && lastcommand) {
600 execute_user_command_i(lastcommand);
601 continue;
603 if(*p == '#' || *p == ';') continue;
604 if(*p == '!') {
605 char *n = strchr(p, '\n');
606 if(n) *n = 0;
607 execute_user_command(p+1);
608 continue;
610 while(isspace(*p) && p < pend) p++;
611 assert(p < pend);
612 if(!*p) continue;
613 char* sym = p;
614 while(!isspace(*p) && p < pend) p++;
615 *p = 0; p++;
616 size_t l = strlen(sym);
617 if(l > 1 && sym[l-1] == ':') {
618 // functionstart or label
619 sym[l-1] = 0;
620 // we currently ignore that
621 continue;
623 unsigned instr = find_insn(sym);
624 if(!instr) {
625 dprintf(2, "line %zu: error: unknown instruction '%s'\n", lineno, sym);
626 continue;
628 code[pos++] = instr;
629 size_t arg;
630 for(arg = 0; arg < opcodes[instr].argcount; arg++) {
631 sym = finalize_arg(&p, pend, convbuf, sizeof(convbuf));
632 if(sym == 0) {
633 dprintf(2, "line %zu: error: expected \"\n", lineno);
634 goto loop_footer;
636 int value = 0;
637 if(arg < opcodes[instr].regcount) {
638 value=get_reg(sym);
639 if(instr == SCMD_REGTOREG) {
640 /* fix reversed order of arguments */
641 int dst = value;
642 sym = p;
643 while(p < pend && *p != ',' && !isspace(*p)) p++;
644 assert(p < pend);
645 *p = 0;
646 value=get_reg(sym);
647 code[pos++] = value;
648 code[pos++] = dst;
649 break;
651 } else {
652 switch(instr) {
653 case SCMD_LITTOREG:
654 /* immediate can be function name, string,
655 * variable name, stack fixup, or numeric value */
656 if(sym[0] == '"') {
657 dprintf(2, "error: string handling not implemented\n");
658 goto loop_footer;
659 } else if(sym[0] == '@') {
660 dprintf(2, "error: global variable handling not implemented\n");
661 goto loop_footer;
662 } else if(sym[0] == '.') {
663 if(memcmp(sym+1, "stack", 5)) {
664 dprintf(2, "error: expected stack\n");
665 goto loop_footer;;
667 dprintf(2, "error: stack fixup not implemented\n");
668 goto loop_footer;
669 } else if(isdigit(sym[0]) || sym[0] == '-') {
670 if(sym[0] == '-') assert(isdigit(sym[1]));
671 value = atoi(sym);
672 } else {
673 dprintf(2, "error: function refs not implemented yet\n");
674 goto loop_footer;
676 break;
678 case SCMD_JMP: case SCMD_JZ: case SCMD_JNZ:
679 add_label_ref(a, sym, pos);
680 break;
682 default:
683 value = atoi(sym);
686 code[pos++] = value;
688 append_code(code, pos);
689 loop_footer: ;