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 } __attribute__((packed
));
23 static const struct regaccess_info regaccess_info
[] = {
24 [0] = {RA_NONE
, RA_NONE
},
25 [SCMD_ADD
] = {RA_READWRITE
, RA_NONE
},
26 [SCMD_SUB
] = {RA_READWRITE
, RA_NONE
},
27 [SCMD_REGTOREG
] = {RA_READ
, RA_WRITE
},
28 [SCMD_WRITELIT
] = {RA_NONE
, RA_NONE
}, // TODO
29 [SCMD_RET
] = {RA_NONE
, RA_NONE
},
30 [SCMD_LITTOREG
] = {RA_WRITE
, RA_NONE
},
31 [SCMD_MEMREAD
] = {RA_WRITE
, RA_NONE
},
32 [SCMD_MEMWRITE
] = {RA_READ
, RA_NONE
},
33 [SCMD_MULREG
] = {RA_READWRITE
, RA_READ
},
34 [SCMD_DIVREG
] = {RA_READWRITE
, RA_READ
},
35 [SCMD_ADDREG
] = {RA_READWRITE
, RA_READ
},
36 [SCMD_SUBREG
] = {RA_READWRITE
, RA_READ
},
37 [SCMD_BITAND
] = {RA_READWRITE
, RA_READ
},
38 [SCMD_BITOR
] = {RA_READWRITE
, RA_READ
},
39 [SCMD_ISEQUAL
] = {RA_READWRITE
, RA_READ
},
40 [SCMD_NOTEQUAL
] = {RA_READWRITE
, RA_READ
},
41 [SCMD_GREATER
] = {RA_READWRITE
, RA_READ
},
42 [SCMD_LESSTHAN
] = {RA_READWRITE
, RA_READ
},
43 [SCMD_GTE
] = {RA_READWRITE
, RA_READ
},
44 [SCMD_LTE
] = {RA_READWRITE
, RA_READ
},
45 [SCMD_AND
] = {RA_READWRITE
, RA_READ
}, /*logical*/
46 [SCMD_OR
] = {RA_READWRITE
, RA_READ
},
47 [SCMD_CALL
] = {RA_READ
, RA_NONE
},
48 [SCMD_MEMREADB
] = {RA_WRITE
, RA_NONE
},
49 [SCMD_MEMREADW
] = {RA_WRITE
, RA_NONE
},
50 [SCMD_MEMWRITEB
] = {RA_READ
, RA_NONE
},
51 [SCMD_MEMWRITEW
] = {RA_READ
, RA_NONE
},
52 [SCMD_JZ
] = {RA_READ
, RA_NONE
},
53 [SCMD_PUSHREG
] = {RA_READ
, RA_NONE
},
54 [SCMD_POPREG
] = {RA_WRITE
, RA_NONE
},
55 [SCMD_JMP
] = {RA_READ
, RA_NONE
},
56 [SCMD_MUL
] = {RA_READWRITE
, RA_NONE
},
57 [SCMD_CALLEXT
] = {RA_READ
, RA_NONE
},
58 [SCMD_PUSHREAL
] = {RA_READ
, RA_NONE
},
59 [SCMD_SUBREALSTACK
] = {RA_READ
, RA_NONE
},
60 [SCMD_LINENUM
] = {RA_NONE
, RA_NONE
},
61 [SCMD_CALLAS
] = {RA_READ
, RA_NONE
},
62 [SCMD_THISBASE
] = {RA_NONE
, RA_NONE
},
63 [SCMD_NUMFUNCARGS
] = {RA_NONE
, RA_NONE
},
64 [SCMD_MODREG
] = {RA_READWRITE
, RA_READ
},
65 [SCMD_XORREG
] = {RA_READWRITE
, RA_READ
},
66 [SCMD_NOTREG
] = {RA_READWRITE
, RA_READ
},
67 [SCMD_SHIFTLEFT
] = {RA_READWRITE
, RA_READ
},
68 [SCMD_SHIFTRIGHT
] = {RA_READWRITE
, RA_READ
},
69 [SCMD_CALLOBJ
] = {RA_READ
, RA_NONE
},
70 [SCMD_CHECKBOUNDS
] = {RA_READ
, RA_NONE
},
71 [SCMD_MEMWRITEPTR
] = {RA_NONE
, RA_NONE
}, //TODO
72 [SCMD_MEMREADPTR
] = {RA_NONE
, RA_NONE
}, //TODO
73 [SCMD_MEMZEROPTR
] = {RA_NONE
, RA_NONE
},
74 [SCMD_MEMINITPTR
] = {RA_NONE
, RA_NONE
}, //TODO
75 [SCMD_LOADSPOFFS
] = {RA_NONE
, RA_NONE
},
76 [SCMD_CHECKNULL
] = {RA_NONE
, RA_NONE
},
77 [SCMD_FADD
] = {RA_READWRITE
, RA_NONE
},
78 [SCMD_FSUB
] = {RA_READWRITE
, RA_NONE
},
79 [SCMD_FMULREG
] = {RA_READWRITE
, RA_READ
},
80 [SCMD_FDIVREG
] = {RA_READWRITE
, RA_READ
},
81 [SCMD_FADDREG
] = {RA_READWRITE
, RA_READ
},
82 [SCMD_FSUBREG
] = {RA_READWRITE
, RA_READ
},
83 [SCMD_FGREATER
] = {RA_READWRITE
, RA_READ
},
84 [SCMD_FLESSTHAN
] = {RA_READWRITE
, RA_READ
},
85 [SCMD_FGTE
] = {RA_READWRITE
, RA_READ
},
86 [SCMD_FLTE
] = {RA_READWRITE
, RA_READ
},
87 [SCMD_ZEROMEMORY
] = {RA_NONE
, RA_NONE
},
88 [SCMD_CREATESTRING
] = {RA_NONE
, RA_NONE
}, //TODO
89 [SCMD_STRINGSEQUAL
] = {RA_READWRITE
, RA_READ
},
90 [SCMD_STRINGSNOTEQ
] = {RA_READWRITE
, RA_READ
},
91 [SCMD_CHECKNULLREG
] = {RA_NONE
, RA_NONE
}, //TODO
92 [SCMD_LOOPCHECKOFF
] = {RA_NONE
, RA_NONE
},
93 [SCMD_MEMZEROPTRND
] = {RA_NONE
, RA_NONE
},
94 [SCMD_JNZ
] = {RA_NONE
, RA_NONE
},
95 [SCMD_DYNAMICBOUNDS
] = {RA_NONE
, RA_NONE
}, //TODO
96 [SCMD_NEWARRAY
] = {RA_NONE
, RA_NONE
}, //TODO
100 #define MAX(a, b) ((a) > (b) ? (a) : (b))
101 #define MIN(a, b) ((a) < (b) ? (a) : (b))
104 /* TODO: move duplicate code from Assembler.c into separate TU */
105 static int get_reg(char* regname
) {
107 for(; i
< AR_MAX
; i
++)
108 if(strcmp(regnames
[i
], regname
) == 0)
113 static size_t mnemolen
[SCMD_MAX
];
114 static int mnemolen_initdone
= 0;
116 static void init_mnemolen(void) {
118 for(; i
< SCMD_MAX
; i
++)
119 mnemolen
[i
] = strlen(opcodes
[i
].mnemonic
);
120 mnemolen_initdone
= 1;
123 static unsigned find_insn(char* sym
) {
124 if(!mnemolen_initdone
) init_mnemolen();
125 size_t i
= 0, l
= strlen(sym
);
126 for(; i
< SCMD_MAX
; i
++)
127 if(l
== mnemolen
[i
] && memcmp(sym
, opcodes
[i
].mnemonic
, l
) == 0)
132 #include "StringEscape.h"
133 /* expects a pointer to the first char after a opening " in a string,
134 * converts the string into convbuf, and returns the length of that string */
135 static size_t get_length_and_convert(char* x
, char* end
, char* convbuf
, size_t convbuflen
) {
137 char* e
= x
+ strlen(x
);
138 assert(e
> x
&& e
< end
&& *e
== 0);
140 while(isspace(*e
)) e
--;
141 if(*e
!= '"') return (size_t) -1;
143 result
= unescape(x
, convbuf
, convbuflen
);
147 /* sets lets char in arg to 0, and advances pointer till the next argstart */
148 static char* finalize_arg(char **p
, char* pend
, char* convbuf
, size_t convbuflen
) {
151 size_t l
= get_length_and_convert(*p
+ 1, pend
, convbuf
+1, convbuflen
- 1);
152 if(l
== (size_t) -1) return 0;
155 *p
= 0; /* make it crash if its accessed again, since a string should always be the last arg */
159 while(*p
< pend
&& **p
!= ',' && !isspace(**p
)) (*p
)++;
162 while(*p
< pend
&& isspace(**p
)) (*p
)++;
169 static struct text_segment
{
179 RU_WRITE_AFTER_READ
= 1 << 2,
187 enum RegisterUsage ru
;
190 static unsigned char stack_mem
[1000*4];
191 #define memory stack_mem
193 static int canread(int index
, int cnt
) {
194 return index
>= 0 && index
+cnt
< sizeof(memory
)/sizeof(memory
[0]);
197 static void grow_text(size_t req
) {
198 if(text
.len
+ req
> text
.capa
) {
199 text
.code
= realloc(text
.code
, (text
.capa
+1024)*sizeof(int));
204 static void append_code(int *code
, size_t cnt
) {
207 for(i
= 0; i
< cnt
; i
++) {
208 text
.code
[text
.len
++] = code
[i
];
212 static void vm_init() {
214 /* initialize registers to an easily recognisable junk value */
215 for(i
= AR_NULL
+ 1; i
< AR_MAX
; i
++) {
216 registers
[i
].i
= 2222222222;
217 registers
[i
].ru
= RU_NONE
;
219 registers
[AR_SP
].i
= 0;
220 registers
[AR_NULL
].i
= 0;
221 /* set up EIP so vm_state() doesn't crash */
225 static inline int consume_int(int **eip
) {
230 static void change_reg_usage(int regno
, enum RegisterAccess ra
) {
231 enum RegisterUsage ru
= registers
[regno
].ru
;
234 if(ru
== RU_NONE
|| ru
== RU_READ
) ru
= RU_READ
;
235 else if(ru
== RU_WRITE
);
236 else if(ru
== RU_WRITE_AFTER_READ
);
239 if(ru
== RU_NONE
|| ru
== RU_WRITE
) ru
= RU_WRITE
;
240 else if(ru
== RU_READ
) ru
= RU_WRITE_AFTER_READ
;
241 else if(ru
== RU_WRITE_AFTER_READ
);
244 if(ru
== RU_NONE
|| ru
== RU_READ
) ru
= RU_WRITE_AFTER_READ
;
245 else if(ru
== RU_WRITE
);
246 else if(ru
== RU_WRITE_AFTER_READ
);
249 registers
[regno
].ru
= ru
;
252 static void vm_update_register_usage(int *eip
) {
253 const struct regaccess_info
*ri
= ®access_info
[*eip
];
254 if(ri
->ra_reg1
) change_reg_usage(eip
[1], ri
->ra_reg1
);
255 if(ri
->ra_reg2
) change_reg_usage(eip
[2], ri
->ra_reg2
);
258 static void write_mem1(int off
, int val
) {
259 unsigned char *m
= (void*) memory
;
262 static void write_mem2(int off
, int val
) {
263 unsigned short *m
= (void*) memory
;
264 m
[off
/2] = val
&0xffff;
266 static void write_mem(int off
, int val
) {
267 int *m
= (void*) memory
;
271 static int read_mem(int off
) {
272 int *m
= (void*) memory
;
276 #define CODE_INT(X) eip[X]
277 #define CODE_FLOAT(X) ((float*)eip)[X]
278 #define REGI(X) registers[CODE_INT(X)].i
279 #define REGF(X) registers[CODE_INT(X)].f
281 static void vm_step() {
282 /* we use register AR_NULL as instruction pointer */
283 int *eip
= &text
.code
[registers
[AR_NULL
].i
];
284 int eip_inc
= 1 + opcodes
[*eip
].argcount
;
286 vm_update_register_usage(eip
);
290 REGI(1) += CODE_INT(2);
293 REGI(1) -= CODE_INT(2);
299 REGI(1) = CODE_INT(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));
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));
343 case SCMD_LOADSPOFFS
:
344 registers
[AR_MAR
].i
= registers
[AR_SP
].i
- CODE_INT(1);
347 write_mem(registers
[AR_SP
].i
, REGI(1));
348 registers
[AR_SP
].i
+= 4;
351 registers
[AR_SP
].i
-= 4;
352 REGI(1) = read_mem(registers
[AR_SP
].i
);
355 REGI(1) *= CODE_INT(2);
372 case SCMD_SHIFTRIGHT
:
376 REGF(1) += CODE_FLOAT(2);
379 REGF(1) -= CODE_FLOAT(2);
394 REGI(1) = !!(REGF(1) > REGF(2));
397 REGI(1) = !!(REGF(1) < REGF(2));
400 REGI(1) = !!(REGF(1) >= REGF(2));
403 REGI(1) = !!(REGF(1) <= REGF(2));
414 if(canread(registers
[AR_MAR
].i
, tmp
)) {
416 case 4: write_mem(registers
[AR_MAR
].i
, REGI(1)); break;
417 case 2: write_mem2(registers
[AR_MAR
].i
, REGI(1)); break;
418 case 1: write_mem1(registers
[AR_MAR
].i
, REGI(1)); break;
421 dprintf(2, "info: caught OOB memwrite\n");
433 if(canread(registers
[AR_MAR
].i
, tmp
)) {
434 int val
= memory
[registers
[AR_MAR
].i
];
436 case 4: REGI(1) = val
; break;
437 case 2: REGI(1) = val
& 0xffff; break;
438 case 1: REGI(1) = val
& 0xff; break;
441 dprintf(2, "info: caught OOB memread\n");
445 case SCMD_DYNAMICBOUNDS
:
447 case SCMD_MEMZEROPTRND
:
448 case SCMD_LOOPCHECKOFF
:
449 case SCMD_CHECKNULLREG
:
450 case SCMD_STRINGSNOTEQ
:
451 case SCMD_STRINGSEQUAL
:
452 case SCMD_CREATESTRING
:
453 case SCMD_ZEROMEMORY
:
455 case SCMD_MEMINITPTR
:
456 case SCMD_MEMZEROPTR
:
457 case SCMD_MEMREADPTR
:
458 case SCMD_MEMWRITEPTR
:
459 case SCMD_CHECKBOUNDS
:
461 case SCMD_NUMFUNCARGS
:
463 case SCMD_SUBREALSTACK
:
472 dprintf(2, "info: %s not implemented yet\n", opcodes
[*eip
].mnemonic
);
474 size_t i
, l
= opcodes
[*eip
].argcount
;
475 for(i
= 0; i
< l
; i
++) ++(*eip
);
479 registers
[AR_NULL
].i
+= eip_inc
;
482 static inline char *int_to_str(int value
, char* out
) {
483 sprintf(out
, "%d", value
);
487 static void vm_state() {
488 static const char ru_strings
[][3] = {
490 [RU_READ
] = {'R', 0},
491 [RU_WRITE
] = {'W', 0},
492 [RU_WRITE_AFTER_READ
] = {'R', 'W', 0},
495 for(i
=0; i
< AR_MAX
; i
++)
496 printf("%s: %2s %d\n", i
== 0 ? "eip" : regnames
[i
], ru_strings
[registers
[i
].ru
], registers
[i
].i
);
498 for( i
= MIN(registers
[AR_SP
].i
+2*4, sizeof(stack_mem
)/4);
499 i
>= MAX(registers
[AR_SP
].i
-2*4, 0);
501 printf("SL %s %3zu %d\n", i
== registers
[AR_SP
].i
? ">" : " ", i
, read_mem(i
));
505 int *eip
= &text
.code
[registers
[AR_NULL
].i
];
506 char arg1buf
[32], arg2buf
[32];
507 const char *arg1
= opcodes
[*eip
].argcount
== 0 ? "" : \
508 (opcodes
[*eip
].regcount
> 0 ? regnames
[eip
[1]] : int_to_str(eip
[1], arg1buf
));
509 const char *arg2
= opcodes
[*eip
].argcount
< 2 ? "" : \
510 (opcodes
[*eip
].regcount
> 1 ? regnames
[eip
[2]] : int_to_str(eip
[2], arg2buf
));
511 printf(" > %s %s %s\n", opcodes
[*eip
].mnemonic
, arg1
, arg2
);
516 int *eip
= &text
.code
[registers
[AR_NULL
].i
];
522 static int usage(int fd
, char *a0
) {
524 "%s - simple ags vm simulator\n"
525 "implements the ALU and a small stack\n"
526 "useful to examine how a chunk of code modifies VM state\n"
527 "not implemented: memory access apart from stack, jumps, functions\n"
528 "supply the assembly code via stdin, then type one of the following\n"
530 "!i - reset VM state and IP\n"
537 static int lastcommand
;
545 static void execute_user_command_i(int uc
) {
547 case UC_STEP
: vm_step(); break;
548 case UC_RUN
: vm_run(); break;
549 case UC_INIT
: vm_init(); break;
550 case UC_QUIT
: exit(0); break;
551 case UC_HELP
: usage(1, "agssim"); break;
556 static void execute_user_command(char *cmd
) {
559 else if(!strcmp(cmd
, "s")) uc
= UC_STEP
;
560 else if(!strcmp(cmd
, "r")) uc
= UC_RUN
;
561 else if(!strcmp(cmd
, "i")) uc
= UC_INIT
;
562 else if(!strcmp(cmd
, "q")) uc
= UC_QUIT
;
563 else if(!strcmp(cmd
, "h")) uc
= UC_HELP
;
565 dprintf(2, "unknown command\n");
568 execute_user_command_i(uc
);
571 int main(int argc
, char** argv
) {
572 if(argc
!= 1) return usage(2, argv
[0]);
573 char buf
[1024], *sym
;
574 char convbuf
[sizeof(buf
)]; /* to convert escaped string into non-escaped version */
577 printf(ADS
" - type !h for help\n");
578 while(fgets(buf
, sizeof buf
, stdin
)) {
582 char* p
= buf
, *pend
= buf
+ sizeof buf
;
583 if(*p
== '\n' && lastcommand
) {
584 execute_user_command_i(lastcommand
);
587 if(*p
== '#' || *p
== ';') continue;
589 char *n
= strchr(p
, '\n');
591 execute_user_command(p
+1);
594 while(isspace(*p
) && p
< pend
) p
++;
598 while(!isspace(*p
) && p
< pend
) p
++;
600 size_t l
= strlen(sym
);
601 if(l
> 1 && sym
[l
-1] == ':') {
602 // functionstart or label
604 // we currently ignore that
607 unsigned instr
= find_insn(sym
);
609 dprintf(2, "line %zu: error: unknown instruction '%s'\n", lineno
, sym
);
614 for(arg
= 0; arg
< opcodes
[instr
].argcount
; arg
++) {
615 sym
= finalize_arg(&p
, pend
, convbuf
, sizeof(convbuf
));
617 dprintf(2, "line %zu: error: expected \"\n", lineno
);
621 if(arg
< opcodes
[instr
].regcount
) {
623 if(instr
== SCMD_REGTOREG
) {
624 /* fix reversed order of arguments */
627 while(p
< pend
&& *p
!= ',' && !isspace(*p
)) p
++;
638 /* immediate can be function name, string,
639 * variable name, stack fixup, or numeric value */
641 dprintf(2, "error: string handling not implemented\n");
643 } else if(sym
[0] == '@') {
644 dprintf(2, "error: global variable handling not implemented\n");
646 } else if(sym
[0] == '.') {
647 if(memcmp(sym
+1, "stack", 5)) {
648 dprintf(2, "error: expected stack\n");
651 dprintf(2, "error: stack fixup not implemented\n");
653 } else if(isdigit(sym
[0]) || sym
[0] == '-') {
654 if(sym
[0] == '-') assert(isdigit(sym
[1]));
657 dprintf(2, "error: function refs not implemented yet\n");
662 case SCMD_JMP: case SCMD_JZ: case SCMD_JNZ:
663 add_label_ref(a, sym, pos);
672 append_code(code
, pos
);