13 RA_READWRITE
= 1 << 2,
16 struct regaccess_info
{
17 /* enum RegisterAccess */ unsigned char ra_reg1
;
18 /* enum RegisterAccess */ unsigned char ra_reg2
;
19 } __attribute__((packed
));
21 static const struct regaccess_info regaccess_info
[] = {
22 [0] = {RA_NONE
, RA_NONE
},
23 [SCMD_ADD
] = {RA_READWRITE
, RA_NONE
},
24 [SCMD_SUB
] = {RA_READWRITE
, RA_NONE
},
25 [SCMD_REGTOREG
] = {RA_READ
, RA_WRITE
},
26 [SCMD_WRITELIT
] = {RA_NONE
, RA_NONE
}, // TODO
27 [SCMD_RET
] = {RA_NONE
, RA_NONE
},
28 [SCMD_LITTOREG
] = {RA_WRITE
, RA_NONE
},
29 [SCMD_MEMREAD
] = {RA_WRITE
, RA_NONE
},
30 [SCMD_MEMWRITE
] = {RA_READ
, RA_NONE
},
31 [SCMD_MULREG
] = {RA_READWRITE
, RA_READ
},
32 [SCMD_DIVREG
] = {RA_READWRITE
, RA_READ
},
33 [SCMD_ADDREG
] = {RA_READWRITE
, RA_READ
},
34 [SCMD_SUBREG
] = {RA_READWRITE
, RA_READ
},
35 [SCMD_BITAND
] = {RA_READWRITE
, RA_READ
},
36 [SCMD_BITOR
] = {RA_READWRITE
, RA_READ
},
37 [SCMD_ISEQUAL
] = {RA_READWRITE
, RA_READ
},
38 [SCMD_NOTEQUAL
] = {RA_READWRITE
, RA_READ
},
39 [SCMD_GREATER
] = {RA_READWRITE
, RA_READ
},
40 [SCMD_LESSTHAN
] = {RA_READWRITE
, RA_READ
},
41 [SCMD_GTE
] = {RA_READWRITE
, RA_READ
},
42 [SCMD_LTE
] = {RA_READWRITE
, RA_READ
},
43 [SCMD_AND
] = {RA_READWRITE
, RA_READ
}, /*logical*/
44 [SCMD_OR
] = {RA_READWRITE
, RA_READ
},
45 [SCMD_CALL
] = {RA_READ
, RA_NONE
},
46 [SCMD_MEMREADB
] = {RA_WRITE
, RA_NONE
},
47 [SCMD_MEMREADW
] = {RA_WRITE
, RA_NONE
},
48 [SCMD_MEMWRITEB
] = {RA_READ
, RA_NONE
},
49 [SCMD_MEMWRITEW
] = {RA_READ
, RA_NONE
},
50 [SCMD_JZ
] = {RA_READ
, RA_NONE
},
51 [SCMD_PUSHREG
] = {RA_READ
, RA_NONE
},
52 [SCMD_POPREG
] = {RA_WRITE
, RA_NONE
},
53 [SCMD_JMP
] = {RA_READ
, RA_NONE
},
54 [SCMD_MUL
] = {RA_READWRITE
, RA_NONE
},
55 [SCMD_CALLEXT
] = {RA_READ
, RA_NONE
},
56 [SCMD_PUSHREAL
] = {RA_READ
, RA_NONE
},
57 [SCMD_SUBREALSTACK
] = {RA_READ
, RA_NONE
},
58 [SCMD_LINENUM
] = {RA_NONE
, RA_NONE
},
59 [SCMD_CALLAS
] = {RA_READ
, RA_NONE
},
60 [SCMD_THISBASE
] = {RA_NONE
, RA_NONE
},
61 [SCMD_NUMFUNCARGS
] = {RA_NONE
, RA_NONE
},
62 [SCMD_MODREG
] = {RA_READWRITE
, RA_READ
},
63 [SCMD_XORREG
] = {RA_READWRITE
, RA_READ
},
64 [SCMD_NOTREG
] = {RA_READWRITE
, RA_READ
},
65 [SCMD_SHIFTLEFT
] = {RA_READWRITE
, RA_READ
},
66 [SCMD_SHIFTRIGHT
] = {RA_READWRITE
, RA_READ
},
67 [SCMD_CALLOBJ
] = {RA_READ
, RA_NONE
},
68 [SCMD_CHECKBOUNDS
] = {RA_READ
, RA_NONE
},
69 [SCMD_MEMWRITEPTR
] = {RA_NONE
, RA_NONE
}, //TODO
70 [SCMD_MEMREADPTR
] = {RA_NONE
, RA_NONE
}, //TODO
71 [SCMD_MEMZEROPTR
] = {RA_NONE
, RA_NONE
},
72 [SCMD_MEMINITPTR
] = {RA_NONE
, RA_NONE
}, //TODO
73 [SCMD_LOADSPOFFS
] = {RA_NONE
, RA_NONE
},
74 [SCMD_CHECKNULL
] = {RA_NONE
, RA_NONE
},
75 [SCMD_FADD
] = {RA_READWRITE
, RA_NONE
},
76 [SCMD_FSUB
] = {RA_READWRITE
, RA_NONE
},
77 [SCMD_FMULREG
] = {RA_READWRITE
, RA_READ
},
78 [SCMD_FDIVREG
] = {RA_READWRITE
, RA_READ
},
79 [SCMD_FADDREG
] = {RA_READWRITE
, RA_READ
},
80 [SCMD_FSUBREG
] = {RA_READWRITE
, RA_READ
},
81 [SCMD_FGREATER
] = {RA_READWRITE
, RA_READ
},
82 [SCMD_FLESSTHAN
] = {RA_READWRITE
, RA_READ
},
83 [SCMD_FGTE
] = {RA_READWRITE
, RA_READ
},
84 [SCMD_FLTE
] = {RA_READWRITE
, RA_READ
},
85 [SCMD_ZEROMEMORY
] = {RA_NONE
, RA_NONE
},
86 [SCMD_CREATESTRING
] = {RA_NONE
, RA_NONE
}, //TODO
87 [SCMD_STRINGSEQUAL
] = {RA_READWRITE
, RA_READ
},
88 [SCMD_STRINGSNOTEQ
] = {RA_READWRITE
, RA_READ
},
89 [SCMD_CHECKNULLREG
] = {RA_NONE
, RA_NONE
}, //TODO
90 [SCMD_LOOPCHECKOFF
] = {RA_NONE
, RA_NONE
},
91 [SCMD_MEMZEROPTRND
] = {RA_NONE
, RA_NONE
},
92 [SCMD_JNZ
] = {RA_NONE
, RA_NONE
},
93 [SCMD_DYNAMICBOUNDS
] = {RA_NONE
, RA_NONE
}, //TODO
94 [SCMD_NEWARRAY
] = {RA_NONE
, RA_NONE
}, //TODO
98 #define MAX(a, b) ((a) > (b) ? (a) : (b))
99 #define MIN(a, b) ((a) < (b) ? (a) : (b))
102 /* TODO: move duplicate code from Assembler.c into separate TU */
103 static int get_reg(char* regname
) {
105 for(; i
< AR_MAX
; i
++)
106 if(strcmp(regnames
[i
], regname
) == 0)
111 static size_t mnemolen
[SCMD_MAX
];
112 static int mnemolen_initdone
= 0;
114 static void init_mnemolen(void) {
116 for(; i
< SCMD_MAX
; i
++)
117 mnemolen
[i
] = strlen(opcodes
[i
].mnemonic
);
118 mnemolen_initdone
= 1;
121 static unsigned find_insn(char* sym
) {
122 if(!mnemolen_initdone
) init_mnemolen();
123 size_t i
= 0, l
= strlen(sym
);
124 for(; i
< SCMD_MAX
; i
++)
125 if(l
== mnemolen
[i
] && memcmp(sym
, opcodes
[i
].mnemonic
, l
) == 0)
130 #include "StringEscape.h"
131 /* expects a pointer to the first char after a opening " in a string,
132 * converts the string into convbuf, and returns the length of that string */
133 static size_t get_length_and_convert(char* x
, char* end
, char* convbuf
, size_t convbuflen
) {
135 char* e
= x
+ strlen(x
);
136 assert(e
> x
&& e
< end
&& *e
== 0);
138 while(isspace(*e
)) e
--;
139 if(*e
!= '"') return (size_t) -1;
141 result
= unescape(x
, convbuf
, convbuflen
);
145 /* sets lets char in arg to 0, and advances pointer till the next argstart */
146 static char* finalize_arg(char **p
, char* pend
, char* convbuf
, size_t convbuflen
) {
149 size_t l
= get_length_and_convert(*p
+ 1, pend
, convbuf
+1, convbuflen
- 1);
150 if(l
== (size_t) -1) return 0;
153 *p
= 0; /* make it crash if its accessed again, since a string should always be the last arg */
157 while(*p
< pend
&& **p
!= ',' && !isspace(**p
)) (*p
)++;
160 while(*p
< pend
&& isspace(**p
)) (*p
)++;
167 static struct text_segment
{
177 RU_WRITE_AFTER_READ
= 1 << 2,
185 enum RegisterUsage ru
;
188 static int stack_mem
[1000];
190 static void grow_text(size_t req
) {
191 if(text
.len
+ req
> text
.capa
) {
192 text
.code
= realloc(text
.code
, (text
.capa
+1024)*sizeof(int));
197 static void append_code(int *code
, size_t cnt
) {
200 for(i
= 0; i
< cnt
; i
++) {
201 text
.code
[text
.len
++] = code
[i
];
205 static void vm_init() {
207 /* initialize registers to an easily recognisable junk value */
208 for(i
= AR_NULL
+ 1; i
< AR_MAX
; i
++) {
209 registers
[i
].i
= 2222222222;
210 registers
[i
].ru
= RU_NONE
;
212 registers
[AR_SP
].i
= (sizeof(stack_mem
)/sizeof(stack_mem
[0])) -1;
213 registers
[AR_NULL
].i
= 0;
216 static inline int consume_int(int **eip
) {
221 static void change_reg_usage(int regno
, enum RegisterAccess ra
) {
222 enum RegisterUsage ru
= registers
[regno
].ru
;
225 if(ru
== RU_NONE
|| ru
== RU_READ
) ru
= RU_READ
;
226 else if(ru
== RU_WRITE
);
227 else if(ru
== RU_WRITE_AFTER_READ
);
230 if(ru
== RU_NONE
|| ru
== RU_WRITE
) ru
= RU_WRITE
;
231 else if(ru
== RU_READ
) ru
= RU_WRITE_AFTER_READ
;
232 else if(ru
== RU_WRITE_AFTER_READ
);
235 if(ru
== RU_NONE
|| ru
== RU_READ
) ru
= RU_WRITE_AFTER_READ
;
236 else if(ru
== RU_WRITE
);
237 else if(ru
== RU_WRITE_AFTER_READ
);
240 registers
[regno
].ru
= ru
;
243 static void vm_update_register_usage(int *eip
) {
244 const struct regaccess_info
*ri
= ®access_info
[*eip
];
245 if(ri
->ra_reg1
) change_reg_usage(eip
[1], ri
->ra_reg1
);
246 if(ri
->ra_reg2
) change_reg_usage(eip
[2], ri
->ra_reg2
);
249 #define CODE_INT(X) eip[X]
250 #define CODE_FLOAT(X) ((float*)eip)[X]
251 #define REGI(X) registers[CODE_INT(X)].i
252 #define REGF(X) registers[CODE_INT(X)].f
254 static void vm_step() {
255 /* we use register AR_NULL as instruction pointer */
256 int *eip
= &text
.code
[registers
[AR_NULL
].i
];
257 int eip_inc
= 1 + opcodes
[*eip
].argcount
;
258 vm_update_register_usage(eip
);
262 REGI(1) += CODE_INT(2);
265 REGI(1) -= CODE_INT(2);
271 REGI(1) = CODE_INT(2);
292 REGI(1) = !!(REGI(1) == REGI(2));
295 REGI(1) = !!(REGI(1) != REGI(2));
298 REGI(1) = !!(REGI(1) > REGI(2));
301 REGI(1) = !!(REGI(1) < REGI(2));
304 REGI(1) = !!(REGI(1) >= REGI(2));
307 REGI(1) = !!(REGI(1) <= REGI(2));
310 REGI(1) = !!(REGI(1) && REGI(2));
313 REGI(1) = !!(REGI(1) || REGI(2));
316 stack_mem
[--registers
[AR_SP
].i
] = REGI(1);
319 REGI(1) = stack_mem
[registers
[AR_SP
].i
++];
322 REGI(1) *= CODE_INT(2);
339 case SCMD_SHIFTRIGHT
:
343 REGF(1) += CODE_FLOAT(2);
346 REGF(1) -= CODE_FLOAT(2);
361 REGI(1) = !!(REGF(1) > REGF(2));
364 REGI(1) = !!(REGF(1) < REGF(2));
367 REGI(1) = !!(REGF(1) >= REGF(2));
370 REGI(1) = !!(REGF(1) <= REGF(2));
373 case SCMD_DYNAMICBOUNDS
:
375 case SCMD_MEMZEROPTRND
:
376 case SCMD_LOOPCHECKOFF
:
377 case SCMD_CHECKNULLREG
:
378 case SCMD_STRINGSNOTEQ
:
379 case SCMD_STRINGSEQUAL
:
380 case SCMD_CREATESTRING
:
381 case SCMD_ZEROMEMORY
:
383 case SCMD_LOADSPOFFS
:
384 case SCMD_MEMINITPTR
:
385 case SCMD_MEMZEROPTR
:
386 case SCMD_MEMREADPTR
:
387 case SCMD_MEMWRITEPTR
:
388 case SCMD_CHECKBOUNDS
:
390 case SCMD_NUMFUNCARGS
:
392 case SCMD_SUBREALSTACK
:
407 dprintf(2, "info: %s not implemented yet\n", opcodes
[*eip
].mnemonic
);
409 size_t i
, l
= opcodes
[*eip
].argcount
;
410 for(i
= 0; i
< l
; i
++) ++(*eip
);
414 registers
[AR_NULL
].i
+= eip_inc
;
417 static inline char *int_to_str(int value
, char* out
) {
418 sprintf(out
, "%d", value
);
422 static void vm_state() {
423 static const char ru_strings
[][3] = {
425 [RU_READ
] = {'R', 0},
426 [RU_WRITE
] = {'W', 0},
427 [RU_WRITE_AFTER_READ
] = {'R', 'W', 0},
430 for(i
=0; i
< AR_MAX
; i
++)
431 printf("%s: %2s %d\n", i
== 0 ? "eip" : regnames
[i
], ru_strings
[registers
[i
].ru
], registers
[i
].i
);
433 for(i
=MIN(registers
[AR_SP
].i
+2, 999); i
>= MAX(0, registers
[AR_SP
].i
-2); i
--) {
434 printf("SL %s %3zu %d\n", i
== registers
[AR_SP
].i
? ">" : " ", i
, stack_mem
[i
]);
437 int *eip
= &text
.code
[registers
[AR_NULL
].i
];
438 char arg1buf
[32], arg2buf
[32];
439 const char *arg1
= opcodes
[*eip
].argcount
== 0 ? "" : \
440 (opcodes
[*eip
].regcount
> 0 ? regnames
[eip
[1]] : int_to_str(eip
[1], arg1buf
));
441 const char *arg2
= opcodes
[*eip
].argcount
< 2 ? "" : \
442 (opcodes
[*eip
].regcount
> 1 ? regnames
[eip
[2]] : int_to_str(eip
[2], arg2buf
));
443 printf(" > %s %s %s\n", opcodes
[*eip
].mnemonic
, arg1
, arg2
);
448 int *eip
= &text
.code
[registers
[AR_NULL
].i
];
455 static void execute_user_command(char *cmd
) {
456 if(!strcmp(cmd
, "s")) {
458 } else if(!strcmp(cmd
, "r")) {
460 } else if(!strcmp(cmd
, "i")) {
466 int main(int argc
, char** argv
) {
469 "%s - simple ags vm simulator\n"
470 "implements the ALU and a small stack\n"
471 "useful to examine how a chunk of code modifies VM state\n"
472 "not implemented: memory access apart from stack, jumps, functions\n"
473 "supply the assembly code via stdin, then type one of the following\n"
475 "!i - reset VM state and IP\n"
481 char buf
[1024], *sym
;
482 char convbuf
[sizeof(buf
)]; /* to convert escaped string into non-escaped version */
485 while(fgets(buf
, sizeof buf
, stdin
)) {
489 char* p
= buf
, *pend
= buf
+ sizeof buf
;
490 if(*p
== '#' || *p
== ';') continue;
492 char *n
= strchr(p
, '\n');
494 execute_user_command(p
+1);
497 while(isspace(*p
) && p
< pend
) p
++;
501 while(!isspace(*p
) && p
< pend
) p
++;
503 size_t l
= strlen(sym
);
504 if(l
> 1 && sym
[l
-1] == ':') {
505 // functionstart or label
507 // we currently ignore that
510 unsigned instr
= find_insn(sym
);
512 dprintf(2, "line %zu: error: unknown instruction '%s'\n", lineno
, sym
);
517 for(arg
= 0; arg
< opcodes
[instr
].argcount
; arg
++) {
518 sym
= finalize_arg(&p
, pend
, convbuf
, sizeof(convbuf
));
520 dprintf(2, "line %zu: error: expected \"\n", lineno
);
524 if(arg
< opcodes
[instr
].regcount
) {
526 if(instr
== SCMD_REGTOREG
) {
527 /* fix reversed order of arguments */
530 while(p
< pend
&& *p
!= ',' && !isspace(*p
)) p
++;
541 /* immediate can be function name, string,
542 * variable name, stack fixup, or numeric value */
544 dprintf(2, "error: string handling not implemented\n");
546 } else if(sym
[0] == '@') {
547 dprintf(2, "error: global variable handling not implemented\n");
549 } else if(sym
[0] == '.') {
550 if(memcmp(sym
+1, "stack", 5)) {
551 dprintf(2, "error: expected stack\n");
554 dprintf(2, "error: stack fixup not implemented\n");
556 } else if(isdigit(sym
[0]) || sym
[0] == '-') {
557 if(sym
[0] == '-') assert(isdigit(sym
[1]));
560 dprintf(2, "error: function refs not implemented yet\n");
565 case SCMD_JMP: case SCMD_JZ: case SCMD_JNZ:
566 add_label_ref(a, sym, pos);
575 append_code(code
, pos
);