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 unsigned char stack_mem
[1000*4];
189 #define memory stack_mem
191 static int canread(int index
, int cnt
) {
192 return index
>= 0 && index
+cnt
< sizeof(memory
)/sizeof(memory
[0]);
195 static void grow_text(size_t req
) {
196 if(text
.len
+ req
> text
.capa
) {
197 text
.code
= realloc(text
.code
, (text
.capa
+1024)*sizeof(int));
202 static void append_code(int *code
, size_t cnt
) {
205 for(i
= 0; i
< cnt
; i
++) {
206 text
.code
[text
.len
++] = code
[i
];
210 static void vm_init() {
212 /* initialize registers to an easily recognisable junk value */
213 for(i
= AR_NULL
+ 1; i
< AR_MAX
; i
++) {
214 registers
[i
].i
= 2222222222;
215 registers
[i
].ru
= RU_NONE
;
217 registers
[AR_SP
].i
= 0;
218 registers
[AR_NULL
].i
= 0;
221 static inline int consume_int(int **eip
) {
226 static void change_reg_usage(int regno
, enum RegisterAccess ra
) {
227 enum RegisterUsage ru
= registers
[regno
].ru
;
230 if(ru
== RU_NONE
|| ru
== RU_READ
) ru
= RU_READ
;
231 else if(ru
== RU_WRITE
);
232 else if(ru
== RU_WRITE_AFTER_READ
);
235 if(ru
== RU_NONE
|| ru
== RU_WRITE
) ru
= RU_WRITE
;
236 else if(ru
== RU_READ
) ru
= RU_WRITE_AFTER_READ
;
237 else if(ru
== RU_WRITE_AFTER_READ
);
240 if(ru
== RU_NONE
|| ru
== RU_READ
) ru
= RU_WRITE_AFTER_READ
;
241 else if(ru
== RU_WRITE
);
242 else if(ru
== RU_WRITE_AFTER_READ
);
245 registers
[regno
].ru
= ru
;
248 static void vm_update_register_usage(int *eip
) {
249 const struct regaccess_info
*ri
= ®access_info
[*eip
];
250 if(ri
->ra_reg1
) change_reg_usage(eip
[1], ri
->ra_reg1
);
251 if(ri
->ra_reg2
) change_reg_usage(eip
[2], ri
->ra_reg2
);
254 static void write_mem(int off
, int val
) {
255 int *m
= (void*) memory
;
259 static int read_mem(int off
) {
260 int *m
= (void*) memory
;
264 #define CODE_INT(X) eip[X]
265 #define CODE_FLOAT(X) ((float*)eip)[X]
266 #define REGI(X) registers[CODE_INT(X)].i
267 #define REGF(X) registers[CODE_INT(X)].f
269 static void vm_step() {
270 /* we use register AR_NULL as instruction pointer */
271 int *eip
= &text
.code
[registers
[AR_NULL
].i
];
272 int eip_inc
= 1 + opcodes
[*eip
].argcount
;
274 vm_update_register_usage(eip
);
278 REGI(1) += CODE_INT(2);
281 REGI(1) -= CODE_INT(2);
287 REGI(1) = CODE_INT(2);
308 REGI(1) = !!(REGI(1) == REGI(2));
311 REGI(1) = !!(REGI(1) != REGI(2));
314 REGI(1) = !!(REGI(1) > REGI(2));
317 REGI(1) = !!(REGI(1) < REGI(2));
320 REGI(1) = !!(REGI(1) >= REGI(2));
323 REGI(1) = !!(REGI(1) <= REGI(2));
326 REGI(1) = !!(REGI(1) && REGI(2));
329 REGI(1) = !!(REGI(1) || REGI(2));
331 case SCMD_LOADSPOFFS
:
332 registers
[AR_MAR
].i
= registers
[AR_SP
].i
- CODE_INT(1);
335 write_mem(registers
[AR_SP
].i
, REGI(1));
336 registers
[AR_SP
].i
+= 4;
339 registers
[AR_SP
].i
-= 4;
340 REGI(1) = read_mem(registers
[AR_SP
].i
);
343 REGI(1) *= CODE_INT(2);
360 case SCMD_SHIFTRIGHT
:
364 REGF(1) += CODE_FLOAT(2);
367 REGF(1) -= CODE_FLOAT(2);
382 REGI(1) = !!(REGF(1) > REGF(2));
385 REGI(1) = !!(REGF(1) < REGF(2));
388 REGI(1) = !!(REGF(1) >= REGF(2));
391 REGI(1) = !!(REGF(1) <= REGF(2));
402 if(canread(registers
[AR_MAR
].i
, tmp
)) {
403 int val
= memory
[registers
[AR_MAR
].i
];
405 case 4: REGI(1) = val
; break;
406 case 2: REGI(1) = val
& 0xffff; break;
407 case 1: REGI(1) = val
& 0xff; break;
410 dprintf(2, "info: caught OOB memread\n");
414 case SCMD_DYNAMICBOUNDS
:
416 case SCMD_MEMZEROPTRND
:
417 case SCMD_LOOPCHECKOFF
:
418 case SCMD_CHECKNULLREG
:
419 case SCMD_STRINGSNOTEQ
:
420 case SCMD_STRINGSEQUAL
:
421 case SCMD_CREATESTRING
:
422 case SCMD_ZEROMEMORY
:
424 case SCMD_MEMINITPTR
:
425 case SCMD_MEMZEROPTR
:
426 case SCMD_MEMREADPTR
:
427 case SCMD_MEMWRITEPTR
:
428 case SCMD_CHECKBOUNDS
:
430 case SCMD_NUMFUNCARGS
:
432 case SCMD_SUBREALSTACK
:
444 dprintf(2, "info: %s not implemented yet\n", opcodes
[*eip
].mnemonic
);
446 size_t i
, l
= opcodes
[*eip
].argcount
;
447 for(i
= 0; i
< l
; i
++) ++(*eip
);
451 registers
[AR_NULL
].i
+= eip_inc
;
454 static inline char *int_to_str(int value
, char* out
) {
455 sprintf(out
, "%d", value
);
459 static void vm_state() {
460 static const char ru_strings
[][3] = {
462 [RU_READ
] = {'R', 0},
463 [RU_WRITE
] = {'W', 0},
464 [RU_WRITE_AFTER_READ
] = {'R', 'W', 0},
467 for(i
=0; i
< AR_MAX
; i
++)
468 printf("%s: %2s %d\n", i
== 0 ? "eip" : regnames
[i
], ru_strings
[registers
[i
].ru
], registers
[i
].i
);
470 for( i
= MIN(registers
[AR_SP
].i
+2*4, sizeof(stack_mem
)/4);
471 i
>= MAX(registers
[AR_SP
].i
-2*4, 0);
473 printf("SL %s %3zu %d\n", i
== registers
[AR_SP
].i
? ">" : " ", i
, read_mem(i
));
477 int *eip
= &text
.code
[registers
[AR_NULL
].i
];
478 char arg1buf
[32], arg2buf
[32];
479 const char *arg1
= opcodes
[*eip
].argcount
== 0 ? "" : \
480 (opcodes
[*eip
].regcount
> 0 ? regnames
[eip
[1]] : int_to_str(eip
[1], arg1buf
));
481 const char *arg2
= opcodes
[*eip
].argcount
< 2 ? "" : \
482 (opcodes
[*eip
].regcount
> 1 ? regnames
[eip
[2]] : int_to_str(eip
[2], arg2buf
));
483 printf(" > %s %s %s\n", opcodes
[*eip
].mnemonic
, arg1
, arg2
);
488 int *eip
= &text
.code
[registers
[AR_NULL
].i
];
495 static void execute_user_command(char *cmd
) {
496 if(!strcmp(cmd
, "s")) {
498 } else if(!strcmp(cmd
, "r")) {
500 } else if(!strcmp(cmd
, "i")) {
502 } else if(!strcmp(cmd
, "q")) {
508 int main(int argc
, char** argv
) {
511 "%s - simple ags vm simulator\n"
512 "implements the ALU and a small stack\n"
513 "useful to examine how a chunk of code modifies VM state\n"
514 "not implemented: memory access apart from stack, jumps, functions\n"
515 "supply the assembly code via stdin, then type one of the following\n"
517 "!i - reset VM state and IP\n"
523 char buf
[1024], *sym
;
524 char convbuf
[sizeof(buf
)]; /* to convert escaped string into non-escaped version */
527 while(fgets(buf
, sizeof buf
, stdin
)) {
531 char* p
= buf
, *pend
= buf
+ sizeof buf
;
532 if(*p
== '#' || *p
== ';') continue;
534 char *n
= strchr(p
, '\n');
536 execute_user_command(p
+1);
539 while(isspace(*p
) && p
< pend
) p
++;
543 while(!isspace(*p
) && p
< pend
) p
++;
545 size_t l
= strlen(sym
);
546 if(l
> 1 && sym
[l
-1] == ':') {
547 // functionstart or label
549 // we currently ignore that
552 unsigned instr
= find_insn(sym
);
554 dprintf(2, "line %zu: error: unknown instruction '%s'\n", lineno
, sym
);
559 for(arg
= 0; arg
< opcodes
[instr
].argcount
; arg
++) {
560 sym
= finalize_arg(&p
, pend
, convbuf
, sizeof(convbuf
));
562 dprintf(2, "line %zu: error: expected \"\n", lineno
);
566 if(arg
< opcodes
[instr
].regcount
) {
568 if(instr
== SCMD_REGTOREG
) {
569 /* fix reversed order of arguments */
572 while(p
< pend
&& *p
!= ',' && !isspace(*p
)) p
++;
583 /* immediate can be function name, string,
584 * variable name, stack fixup, or numeric value */
586 dprintf(2, "error: string handling not implemented\n");
588 } else if(sym
[0] == '@') {
589 dprintf(2, "error: global variable handling not implemented\n");
591 } else if(sym
[0] == '.') {
592 if(memcmp(sym
+1, "stack", 5)) {
593 dprintf(2, "error: expected stack\n");
596 dprintf(2, "error: stack fixup not implemented\n");
598 } else if(isdigit(sym
[0]) || sym
[0] == '-') {
599 if(sym
[0] == '-') assert(isdigit(sym
[1]));
602 dprintf(2, "error: function refs not implemented yet\n");
607 case SCMD_JMP: case SCMD_JZ: case SCMD_JNZ:
608 add_label_ref(a, sym, pos);
617 append_code(code
, pos
);