9 #define ADS ":::AGSSim " VERSION " by rofl0r:::"
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
101 #define MAX(a, b) ((a) > (b) ? (a) : (b))
102 #define MIN(a, b) ((a) < (b) ? (a) : (b))
105 /* TODO: move duplicate code from Assembler.c into separate TU */
106 static int get_reg(char* regname
) {
108 for(; i
< AR_MAX
; i
++)
109 if(strcmp(regnames
[i
], regname
) == 0)
114 static size_t mnemolen
[SCMD_MAX
];
115 static int mnemolen_initdone
= 0;
117 static void init_mnemolen(void) {
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)
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
) {
138 char* e
= x
+ strlen(x
);
139 assert(e
> x
&& e
< end
&& *e
== 0);
141 while(isspace(*e
)) e
--;
142 if(*e
!= '"') return (size_t) -1;
144 result
= unescape(x
, convbuf
, convbuflen
);
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
) {
152 size_t l
= get_length_and_convert(*p
+ 1, pend
, convbuf
+1, convbuflen
- 1);
153 if(l
== (size_t) -1) return 0;
156 *p
= 0; /* make it crash if its accessed again, since a string should always be the last arg */
160 while(*p
< pend
&& **p
!= ',' && !isspace(**p
)) (*p
)++;
163 while(*p
< pend
&& isspace(**p
)) (*p
)++;
170 static struct text_segment
{
180 RU_WRITE_AFTER_READ
= 1 << 2,
188 enum RegisterUsage ru
;
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));
205 static void append_code(int *code
, size_t cnt
) {
208 for(i
= 0; i
< cnt
; i
++) {
209 text
.code
[text
.len
++] = code
[i
];
213 static void vm_init() {
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 */
226 static inline int consume_int(int **eip
) {
231 static void change_reg_usage(int regno
, enum RegisterAccess ra
) {
232 enum RegisterUsage ru
= registers
[regno
].ru
;
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
);
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
);
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
);
250 registers
[regno
].ru
= ru
;
253 static void vm_update_register_usage(int *eip
) {
254 const struct regaccess_info
*ri
= ®access_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
;
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
;
273 static int read_mem(int off
) {
274 int *m
= (void*) memory
;
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
;
288 vm_update_register_usage(eip
);
292 /* don't modify IP */
293 dprintf(2, "no code at IP.\n");
296 REGI(1) += CODE_INT(2);
299 REGI(1) -= CODE_INT(2);
305 REGI(1) = CODE_INT(2);
326 REGI(1) = !!(REGI(1) == REGI(2));
329 REGI(1) = !!(REGI(1) != REGI(2));
332 REGI(1) = !!(REGI(1) > REGI(2));
335 REGI(1) = !!(REGI(1) < REGI(2));
338 REGI(1) = !!(REGI(1) >= REGI(2));
341 REGI(1) = !!(REGI(1) <= REGI(2));
344 REGI(1) = !!(REGI(1) && REGI(2));
347 REGI(1) = !!(REGI(1) || REGI(2));
349 case SCMD_LOADSPOFFS
:
350 registers
[AR_MAR
].i
= registers
[AR_SP
].i
- CODE_INT(1);
353 write_mem(registers
[AR_SP
].i
, REGI(1));
354 registers
[AR_SP
].i
+= 4;
357 registers
[AR_SP
].i
-= 4;
358 REGI(1) = read_mem(registers
[AR_SP
].i
);
361 REGI(1) *= CODE_INT(2);
378 case SCMD_SHIFTRIGHT
:
382 REGF(1) += CODE_FLOAT(2);
385 REGF(1) -= CODE_FLOAT(2);
400 REGI(1) = !!(REGF(1) > REGF(2));
403 REGI(1) = !!(REGF(1) < REGF(2));
406 REGI(1) = !!(REGF(1) >= REGF(2));
409 REGI(1) = !!(REGF(1) <= REGF(2));
413 if(tmp
<= 0 || tmp
> 4 || tmp
== 3) {
414 dprintf(2, "invalid memcpy use\n");
431 if(canread(registers
[AR_MAR
].i
, 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;
438 dprintf(2, "info: caught OOB memwrite\n");
450 if(canread(registers
[AR_MAR
].i
, tmp
)) {
451 int val
= memory
[registers
[AR_MAR
].i
];
453 case 4: REGI(1) = val
; break;
454 case 2: REGI(1) = val
& 0xffff; break;
455 case 1: REGI(1) = val
& 0xff; break;
458 dprintf(2, "info: caught OOB memread\n");
462 case SCMD_DYNAMICBOUNDS
:
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
:
472 case SCMD_MEMINITPTR
:
473 case SCMD_MEMZEROPTR
:
474 case SCMD_MEMREADPTR
:
475 case SCMD_MEMWRITEPTR
:
476 case SCMD_CHECKBOUNDS
:
478 case SCMD_NUMFUNCARGS
:
480 case SCMD_SUBREALSTACK
:
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
);
495 registers
[AR_NULL
].i
+= eip_inc
;
498 static inline char *int_to_str(int value
, char* out
) {
499 sprintf(out
, "%d", value
);
503 static void vm_state() {
504 static const char ru_strings
[][3] = {
506 [RU_READ
] = {'R', 0},
507 [RU_WRITE
] = {'W', 0},
508 [RU_WRITE_AFTER_READ
] = {'R', 'W', 0},
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);
517 printf("SL %s %3zu %d\n", i
== registers
[AR_SP
].i
? ">" : " ", i
, read_mem(i
));
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
);
532 int *eip
= &text
.code
[registers
[AR_NULL
].i
];
538 static int usage(int fd
, char *a0
) {
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"
546 "!i - reset VM state and IP\n"
553 static int lastcommand
;
561 static void execute_user_command_i(int 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;
572 static void execute_user_command(char *cmd
) {
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
;
581 dprintf(2, "unknown command\n");
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 */
593 printf(ADS
" - type !h for help\n");
594 while(fgets(buf
, sizeof buf
, stdin
)) {
598 char* p
= buf
, *pend
= buf
+ sizeof buf
;
599 if(*p
== '\n' && lastcommand
) {
600 execute_user_command_i(lastcommand
);
603 if(*p
== '#' || *p
== ';') continue;
605 char *n
= strchr(p
, '\n');
607 execute_user_command(p
+1);
610 while(isspace(*p
) && p
< pend
) p
++;
614 while(!isspace(*p
) && p
< pend
) p
++;
616 size_t l
= strlen(sym
);
617 if(l
> 1 && sym
[l
-1] == ':') {
618 // functionstart or label
620 // we currently ignore that
623 unsigned instr
= find_insn(sym
);
625 dprintf(2, "line %zu: error: unknown instruction '%s'\n", lineno
, sym
);
630 for(arg
= 0; arg
< opcodes
[instr
].argcount
; arg
++) {
631 sym
= finalize_arg(&p
, pend
, convbuf
, sizeof(convbuf
));
633 dprintf(2, "line %zu: error: expected \"\n", lineno
);
637 if(arg
< opcodes
[instr
].regcount
) {
639 if(instr
== SCMD_REGTOREG
) {
640 /* fix reversed order of arguments */
643 while(p
< pend
&& *p
!= ',' && !isspace(*p
)) p
++;
654 /* immediate can be function name, string,
655 * variable name, stack fixup, or numeric value */
657 dprintf(2, "error: string handling not implemented\n");
659 } else if(sym
[0] == '@') {
660 dprintf(2, "error: global variable handling not implemented\n");
662 } else if(sym
[0] == '.') {
663 if(memcmp(sym
+1, "stack", 5)) {
664 dprintf(2, "error: expected stack\n");
667 dprintf(2, "error: stack fixup not implemented\n");
669 } else if(isdigit(sym
[0]) || sym
[0] == '-') {
670 if(sym
[0] == '-') assert(isdigit(sym
[1]));
673 dprintf(2, "error: function refs not implemented yet\n");
678 case SCMD_JMP: case SCMD_JZ: case SCMD_JNZ:
679 add_label_ref(a, sym, pos);
688 append_code(code
, pos
);