11 #define ADS ":::AGSSim " VERSION " by rofl0r:::"
14 #define MAX(a, b) ((a) > (b) ? (a) : (b))
17 #define MIN(a, b) ((a) < (b) ? (a) : (b))
21 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
24 static struct text_segment
{
30 static void patch_label_offset(int old
, int new) {
31 int *p
= text
.code
, *pe
= text
.code
+ text
.len
;
32 while(p
< pe
) switch(*p
) {
33 case SCMD_JMP
: case SCMD_JZ
: case SCMD_JNZ
:
34 if(p
[1] == old
) p
[1] = new;
37 p
+= opcodes
[*p
].argcount
+ 1;
41 hbmap(char*, int, 32) *label_map
;
42 static int *get_label_offset(char* name
) {
43 return hbmap_get(label_map
, name
);
45 static int add_label(char* name
, int insno
) {
46 char* tmp
= strdup(name
);
47 int *old
= get_label_offset(name
);
48 if(old
&& (*old
& (1<<31)))
49 patch_label_offset(*old
, insno
);
50 return hbmap_insert(label_map
, tmp
, insno
) != -1;
52 static int strptrcmp(const void *a
, const void *b
) {
53 const char * const *x
= a
;
54 const char * const *y
= b
;
55 return strcmp(*x
, *y
);
57 static unsigned string_hash(const char* s
) {
65 static void init_labels() {
66 label_map
= hbmap_new(strptrcmp
, string_hash
, 32);
69 /* TODO: move duplicate code from Assembler.c into separate TU */
70 static int get_reg(char* regname
) {
72 for(; i
< AR_MAX
; i
++)
73 if(strcmp(regnames
[i
], regname
) == 0)
78 static size_t mnemolen
[SCMD_MAX
];
79 static int mnemolen_initdone
= 0;
81 static void init_mnemolen(void) {
83 for(; i
< SCMD_MAX
; i
++)
84 mnemolen
[i
] = strlen(opcodes
[i
].mnemonic
);
85 mnemolen_initdone
= 1;
88 static unsigned find_insn(char* sym
) {
89 if(!mnemolen_initdone
) init_mnemolen();
90 size_t i
= 0, l
= strlen(sym
);
91 for(; i
< SCMD_MAX
; i
++)
92 if(l
== mnemolen
[i
] && memcmp(sym
, opcodes
[i
].mnemonic
, l
) == 0)
97 #include "StringEscape.h"
98 /* expects a pointer to the first char after a opening " in a string,
99 * converts the string into convbuf, and returns the length of that string */
100 static size_t get_length_and_convert(char* x
, char* end
, char* convbuf
, size_t convbuflen
) {
102 char* e
= x
+ strlen(x
);
103 assert(e
> x
&& e
< end
&& *e
== 0);
105 while(isspace(*e
)) e
--;
106 if(*e
!= '"') return (size_t) -1;
108 result
= unescape(x
, convbuf
, convbuflen
);
112 /* sets lets char in arg to 0, and advances pointer till the next argstart */
113 static char* finalize_arg(char **p
, char* pend
, char* convbuf
, size_t convbuflen
) {
116 size_t l
= get_length_and_convert(*p
+ 1, pend
, convbuf
+1, convbuflen
- 1);
117 if(l
== (size_t) -1) return 0;
120 *p
= 0; /* make it crash if its accessed again, since a string should always be the last arg */
124 while(*p
< pend
&& **p
!= ',' && !isspace(**p
)) (*p
)++;
127 while(*p
< pend
&& isspace(**p
)) (*p
)++;
138 enum RegisterUsage ru
;
141 static unsigned char stack_mem
[1000*4];
142 #define memory stack_mem
144 static int canread(int index
, int cnt
) {
145 return index
>= 0 && index
+cnt
< sizeof(memory
)/sizeof(memory
[0]);
148 static void grow_text(size_t req
) {
149 if(text
.len
+ req
> text
.capa
) {
150 text
.code
= realloc(text
.code
, (text
.capa
+1024)*sizeof(int));
155 static void append_code(int *code
, size_t cnt
) {
158 for(i
= 0; i
< cnt
; i
++) {
159 text
.code
[text
.len
++] = code
[i
];
161 text
.code
[text
.len
] = 0;
164 static void vm_reset_register_usage() {
166 for(i
= AR_NULL
+ 1; i
< AR_MAX
; i
++)
167 registers
[i
].ru
= RU_NONE
;
170 static void vm_init() {
172 /* initialize registers to an easily recognisable junk value */
173 for(i
= AR_NULL
+ 1; i
< AR_MAX
; i
++) {
174 registers
[i
].i
= 2222222222;
176 vm_reset_register_usage();
177 registers
[AR_SP
].i
= 0;
178 registers
[AR_NULL
].i
= 0;
179 /* set up EIP so vm_state() doesn't crash */
181 /* put NULL insn as first instruction so VM doesn't execute
182 random garbage in mem */
186 static inline int consume_int(int **eip
) {
191 static void change_reg_usage(int regno
, enum RegisterAccess ra
) {
192 registers
[regno
].ru
= get_reg_usage(regno
, registers
[regno
].ru
, ra
);
195 static void vm_update_register_usage(int *eip
) {
196 const struct regaccess_info
*ri
= ®access_info
[*eip
];
197 if(ri
->ra_reg1
) change_reg_usage(eip
[1], ri
->ra_reg1
);
198 if(ri
->ra_reg2
) change_reg_usage(eip
[2], ri
->ra_reg2
);
199 if(ri
->ra_mar
) change_reg_usage(AR_MAR
, ri
->ra_mar
);
200 if(ri
->ra_sp
) change_reg_usage(AR_SP
, ri
->ra_sp
);
203 static void write_mem1(int off
, int val
) {
204 unsigned char *m
= (void*) memory
;
207 static void write_mem2(int off
, int val
) {
208 unsigned short *m
= (void*) memory
;
209 m
[off
/2] = val
&0xffff;
211 static void write_mem(int off
, int val
) {
212 int *m
= (void*) memory
;
216 static int read_mem(int off
) {
217 int *m
= (void*) memory
;
221 #define CODE_INT(X) eip[X]
222 #define CODE_FLOAT(X) ((float*)eip)[X]
223 #define REGI(X) registers[CODE_INT(X)].i
224 #define REGF(X) registers[CODE_INT(X)].f
226 static int vm_step(int run_context
) {
227 /* we use register AR_NULL as instruction pointer */
228 int *eip
= &text
.code
[registers
[AR_NULL
].i
];
229 int eip_inc
= 1 + opcodes
[*eip
].argcount
;
231 if(!run_context
) vm_reset_register_usage();
232 vm_update_register_usage(eip
);
236 /* don't modify IP */
237 dprintf(2, "no code at IP.\n");
240 REGI(1) += CODE_INT(2);
243 REGI(1) -= CODE_INT(2);
249 REGI(1) = CODE_INT(2);
270 REGI(1) = !!(REGI(1) == REGI(2));
273 REGI(1) = !!(REGI(1) != REGI(2));
276 REGI(1) = !!(REGI(1) > REGI(2));
279 REGI(1) = !!(REGI(1) < REGI(2));
282 REGI(1) = !!(REGI(1) >= REGI(2));
285 REGI(1) = !!(REGI(1) <= REGI(2));
288 REGI(1) = !!(REGI(1) && REGI(2));
291 REGI(1) = !!(REGI(1) || REGI(2));
293 case SCMD_LOADSPOFFS
:
294 registers
[AR_MAR
].i
= registers
[AR_SP
].i
- CODE_INT(1);
297 write_mem(registers
[AR_SP
].i
, REGI(1));
298 registers
[AR_SP
].i
+= 4;
301 registers
[AR_SP
].i
-= 4;
302 REGI(1) = read_mem(registers
[AR_SP
].i
);
305 REGI(1) *= CODE_INT(2);
322 case SCMD_SHIFTRIGHT
:
326 REGF(1) += CODE_FLOAT(2);
329 REGF(1) -= CODE_FLOAT(2);
344 REGI(1) = !!(REGF(1) > REGF(2));
347 REGI(1) = !!(REGF(1) < REGF(2));
350 REGI(1) = !!(REGF(1) >= REGF(2));
353 REGI(1) = !!(REGF(1) <= REGF(2));
357 if(tmp
<= 0 || tmp
> 4 || tmp
== 3) {
358 dprintf(2, "invalid memcpy use\n");
375 if(canread(registers
[AR_MAR
].i
, tmp
)) {
377 case 4: write_mem (registers
[AR_MAR
].i
, val
); break;
378 case 2: write_mem2(registers
[AR_MAR
].i
, val
); break;
379 case 1: write_mem1(registers
[AR_MAR
].i
, val
); break;
382 dprintf(2, "info: caught OOB memwrite\n");
394 if(canread(registers
[AR_MAR
].i
, tmp
)) {
395 int val
= memory
[registers
[AR_MAR
].i
];
397 case 4: REGI(1) = val
; break;
398 case 2: REGI(1) = val
& 0xffff; break;
399 case 1: REGI(1) = val
& 0xff; break;
402 dprintf(2, "info: caught OOB memread\n");
406 if(registers
[AR_AX
].i
== 0) goto jump
;
409 if(registers
[AR_AX
].i
== 0) break;
414 if((tmp
& (1<<31)) == 0) {
415 registers
[AR_NULL
].i
= CODE_INT(1);
417 dprintf(2, "error: jump target lacks definition\n");
423 case SCMD_DYNAMICBOUNDS
:
424 case SCMD_MEMZEROPTRND
:
425 case SCMD_LOOPCHECKOFF
:
426 case SCMD_CHECKNULLREG
:
427 case SCMD_STRINGSNOTEQ
:
428 case SCMD_STRINGSEQUAL
:
429 case SCMD_CREATESTRING
:
430 case SCMD_ZEROMEMORY
:
432 case SCMD_MEMINITPTR
:
433 case SCMD_MEMZEROPTR
:
434 case SCMD_MEMREADPTR
:
435 case SCMD_MEMWRITEPTR
:
436 case SCMD_CHECKBOUNDS
:
438 case SCMD_NUMFUNCARGS
:
440 case SCMD_SUBREALSTACK
:
446 dprintf(2, "info: %s not implemented yet\n", opcodes
[*eip
].mnemonic
);
448 size_t i
, l
= opcodes
[*eip
].argcount
;
449 for(i
= 0; i
< l
; i
++) ++(*eip
);
453 registers
[AR_NULL
].i
+= eip_inc
;
457 static inline char *int_to_str(int value
, char* out
) {
458 sprintf(out
, "%d", value
);
462 static void vm_state() {
463 static const char ru_strings
[][3] = {
465 [RU_READ
] = {'R', 0},
466 [RU_WRITE
] = {'W', 0},
467 [RU_WRITE_AFTER_READ
] = {'R', 'W', 0},
469 static const char regorder
[] = {
470 0, AR_MAR
, AR_OP
, AR_SP
, -1,
471 AR_AX
, AR_BX
, AR_CX
, AR_DX
, -1, -1};
473 for(j
=0; j
< ARRAY_SIZE(regorder
)-1; ++j
) {
475 if(i
== -1) printf("\n");
477 printf("%-3s: %-2s %-11d", i
== 0 ? "eip" : regnames
[i
], ru_strings
[registers
[i
].ru
], registers
[i
].i
);
478 if(regorder
[j
+1] != -1) printf(" ");
482 for( i
= MIN(registers
[AR_SP
].i
+2*4, sizeof(stack_mem
)/4);
483 i
>= MAX(registers
[AR_SP
].i
-2*4, 0);
485 printf("SL %s %3zu %d\n", i
== registers
[AR_SP
].i
? ">" : " ", i
, read_mem(i
));
489 int *eip
= &text
.code
[registers
[AR_NULL
].i
];
490 char arg1buf
[32], arg2buf
[32];
491 const char *arg1
= opcodes
[*eip
].argcount
== 0 ? "" : \
492 (opcodes
[*eip
].regcount
> 0 ? regnames
[eip
[1]] : int_to_str(eip
[1], arg1buf
));
493 const char *arg2
= opcodes
[*eip
].argcount
< 2 ? "" : \
494 (opcodes
[*eip
].regcount
> 1 ? regnames
[eip
[2]] : int_to_str(eip
[2], arg2buf
));
495 printf(" > %s %s %s\n", opcodes
[*eip
].mnemonic
, arg1
, arg2
);
500 int *eip
= &text
.code
[registers
[AR_NULL
].i
];
502 if(!vm_step(1)) break;
506 static int usage(int fd
, char *a0
) {
508 "%s - simple ags vm simulator\n"
509 "implements the ALU and a small stack\n"
510 "useful to examine how a chunk of code modifies VM state\n"
511 "not implemented: memory access apart from stack, jumps, functions\n"
512 "supply the assembly code via stdin, then type one of the following\n"
514 "!i - reset VM state and IP\n"
521 static int lastcommand
;
529 static void execute_user_command_i(int uc
) {
531 case UC_STEP
: vm_step(0); break;
532 case UC_RUN
: vm_run(); break;
533 case UC_INIT
: vm_init(); break;
534 case UC_QUIT
: exit(0); break;
535 case UC_HELP
: usage(1, "agssim"); break;
540 static void execute_user_command(char *cmd
) {
543 else if(!strcmp(cmd
, "s")) uc
= UC_STEP
;
544 else if(!strcmp(cmd
, "r")) uc
= UC_RUN
;
545 else if(!strcmp(cmd
, "i")) uc
= UC_INIT
;
546 else if(!strcmp(cmd
, "q")) uc
= UC_QUIT
;
547 else if(!strcmp(cmd
, "h")) uc
= UC_HELP
;
549 dprintf(2, "unknown command\n");
552 execute_user_command_i(uc
);
555 int main(int argc
, char** argv
) {
556 if(argc
!= 1) return usage(2, argv
[0]);
557 char buf
[1024], *sym
;
558 char convbuf
[sizeof(buf
)]; /* to convert escaped string into non-escaped version */
562 printf(ADS
" - type !h for help\n");
563 while(fgets(buf
, sizeof buf
, stdin
)) {
567 char* p
= buf
, *pend
= buf
+ sizeof buf
;
568 if(*p
== '\n' && lastcommand
) {
569 execute_user_command_i(lastcommand
);
572 if(*p
== '#' || *p
== ';') continue;
574 char *n
= strchr(p
, '\n');
576 execute_user_command(p
+1);
579 while(isspace(*p
) && p
< pend
) p
++;
583 while(!isspace(*p
) && p
< pend
) p
++;
585 size_t l
= strlen(sym
);
586 if(l
> 1 && sym
[l
-1] == ':') {
587 // functionstart or label
589 int *loff
= get_label_offset(sym
);
590 if(loff
) dprintf(2, "warning: label %s overwritten\n");
591 add_label(sym
, text
.len
);
594 unsigned instr
= find_insn(sym
);
596 dprintf(2, "line %zu: error: unknown instruction '%s'\n", lineno
, sym
);
601 for(arg
= 0; arg
< opcodes
[instr
].argcount
; arg
++) {
602 sym
= finalize_arg(&p
, pend
, convbuf
, sizeof(convbuf
));
604 dprintf(2, "line %zu: error: expected \"\n", lineno
);
608 if(arg
< opcodes
[instr
].regcount
) {
610 if(value
== AR_NULL
) {
612 dprintf(2, "line %zu: error: expected register name!\n", lineno
);
615 if(instr
== SCMD_REGTOREG
) {
616 /* fix reversed order of arguments */
619 while(p
< pend
&& *p
!= ',' && !isspace(*p
)) p
++;
623 if(value
== AR_NULL
) goto needreg_err
;
631 /* immediate can be function name, string,
632 * variable name, stack fixup, or numeric value */
634 dprintf(2, "error: string handling not implemented\n");
636 } else if(sym
[0] == '@') {
637 dprintf(2, "error: global variable handling not implemented\n");
639 } else if(sym
[0] == '.') {
640 if(memcmp(sym
+1, "stack", 5)) {
641 dprintf(2, "error: expected stack\n");
644 dprintf(2, "error: stack fixup not implemented\n");
646 } else if(isdigit(sym
[0]) || sym
[0] == '-') {
647 if(sym
[0] == '-') assert(isdigit(sym
[1]));
653 case SCMD_JMP
: case SCMD_JZ
: case SCMD_JNZ
: {
655 unsigned *loff
= get_label_offset(sym
);
657 add_label(sym
, (unsigned)text
.len
| (1<<31));
658 loff
= get_label_offset(sym
);
668 append_code(code
, pos
);