12 #define ADS ":::AGSSim " VERSION " by rofl0r:::"
15 #define MAX(a, b) ((a) > (b) ? (a) : (b))
18 #define MIN(a, b) ((a) < (b) ? (a) : (b))
22 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
25 #define ALIGN(X, A) ((X+(A-1)) & -(A))
27 #define BREAKPOINT_FLAG (1<<31)
28 #define OPCODE_MASK (~(BREAKPOINT_FLAG))
30 #define DEFAULT_STACKSIZE 16384
32 static int interactive
, stacksize
;
39 enum RegisterUsage ru
;
40 } registers
[MAX(AR_MAX
, 256)];
50 #define EIP registers[AR_NULL].i
56 static void vm_signal(int sig
, int param
) {
59 dprintf(2, "illegal instruction at IP %u\n", EIP
);
62 dprintf(2, "segmentation fault: invalid access at %u\n", EIP
);
65 dprintf(2, "aborted (assertlte check failed at IP %u)\n", EIP
);
68 dprintf(2, "unknown signal\n");
73 #define memory (mem.mem)
75 #define text_end ALIGN(mem.ltext, 4096)
76 #define stack_mem (mem.mem+text_end)
77 #define heap_mem (mem.mem+text_end+mem.lstack)
83 tglist(struct label_ref
) *label_refs
;
84 static void add_label_ref(char *name
, unsigned insoff
) {
85 struct label_ref
new = {.name
= strdup(name
), .insoff
= insoff
};
86 tglist_add(label_refs
, new);
88 static void resolve_label(char* name
, unsigned insoff
) {
90 for(i
=0; i
<tglist_getsize(label_refs
); ) {
91 struct label_ref
*l
= &tglist_get(label_refs
, i
);
92 if(!strcmp(l
->name
, name
)) {
94 memcpy(text
+l
->insoff
, &insoff
, 4);
95 tglist_delete(label_refs
, i
);
100 hbmap(char*, unsigned, 32) *label_map
;
101 static unsigned *get_label_offset(char* name
) {
102 return hbmap_get(label_map
, name
);
104 static int add_label(char* name
, int insoff
) {
105 char* tmp
= strdup(name
);
106 return hbmap_insert(label_map
, tmp
, insoff
) != -1;
108 static int strptrcmp(const void *a
, const void *b
) {
109 const char * const *x
= a
;
110 const char * const *y
= b
;
111 return strcmp(*x
, *y
);
113 static unsigned string_hash(const char* s
) {
119 return h
& 0xfffffff;
121 static void init_labels() {
122 label_map
= hbmap_new(strptrcmp
, string_hash
, 32);
123 label_refs
= tglist_new();
126 /* TODO: move duplicate code from Assembler.c into separate TU */
127 static int get_reg(char* regname
) {
129 for(; i
< AR_MAX
; i
++)
130 if(strcmp(regnames
[i
], regname
) == 0)
135 static size_t mnemolen
[SCMD_MAX
];
136 static int mnemolen_initdone
= 0;
138 static void init_mnemolen(void) {
140 for(; i
< SCMD_MAX
; i
++)
141 mnemolen
[i
] = strlen(opcodes
[i
].mnemonic
);
142 mnemolen_initdone
= 1;
145 static unsigned find_insn(char* sym
) {
146 if(!mnemolen_initdone
) init_mnemolen();
147 size_t i
= 0, l
= strlen(sym
);
148 for(; i
< SCMD_MAX
; i
++)
149 if(l
== mnemolen
[i
] && memcmp(sym
, opcodes
[i
].mnemonic
, l
) == 0)
154 #include "StringEscape.h"
155 /* expects a pointer to the first char after a opening " in a string,
156 * converts the string into convbuf, and returns the length of that string */
157 static size_t get_length_and_convert(char* x
, char* end
, char* convbuf
, size_t convbuflen
) {
159 char* e
= x
+ strlen(x
);
160 assert(e
> x
&& e
< end
&& *e
== 0);
162 while(isspace(*e
)) e
--;
163 if(*e
!= '"') return (size_t) -1;
165 result
= unescape(x
, convbuf
, convbuflen
);
169 /* sets lets char in arg to 0, and advances pointer till the next argstart */
170 static char* finalize_arg(char **p
, char* pend
, char* convbuf
, size_t convbuflen
) {
173 size_t l
= get_length_and_convert(*p
+ 1, pend
, convbuf
+1, convbuflen
- 1);
174 if(l
== (size_t) -1) return 0;
177 *p
= 0; /* make it crash if its accessed again, since a string should always be the last arg */
181 while(*p
< pend
&& **p
!= ',' && !isspace(**p
)) (*p
)++;
184 while(*p
< pend
&& isspace(**p
)) (*p
)++;
190 static int canread(int index
, int cnt
) {
191 return index
>= 0 && index
+cnt
< mem
.capa
;
193 static int canwrite(int index
, int cnt
) {
194 return index
>= text_end
&& index
+cnt
< mem
.capa
;
197 #define ALIGN(X, A) ((X+(A-1)) & -(A))
199 static void vm_setup_startenv(int argc
, char **argv
, char **envp
)
202 size_t l
, i
; char *s
;
203 for(i
= 0; i
< argc
; ++i
) {
206 memcpy(stack_mem
+so
, s
, l
);
209 if(envp
) for(i
= 0; envp
[i
]; ++i
) {
212 memcpy(stack_mem
+so
, s
, l
);
216 int* stack
= (void*)(stack_mem
+ so
);
219 for(i
= 0; i
< argc
; ++i
) {
220 stack
[si
++] = text_end
+ so
;
221 l
= strlen(argv
[i
]) + 1;
225 if(envp
) for(i
= 0; envp
[i
]; ++i
) {
226 stack
[si
++] = text_end
+ so
;
227 l
= strlen(envp
[i
]) + 1;
231 /* op points to start of stack where the argv stuff is stored */
232 registers
[AR_OP
].i
= text_end
+ so
;
234 stack
[si
++] = 0; // auxv not implemented yet
235 /* sp points to usable stack start */
236 registers
[AR_SP
].i
= text_end
+ so
+ si
*4;
239 static char** vm_args
;
241 static void vm_push_arg(char *arg
) {
242 vm_args
= realloc(vm_args
, sizeof(char*)*(++vm_argc
));
243 vm_args
[vm_argc
-1] = arg
;
246 static void vm_setup_startenv_s(void) {
247 vm_setup_startenv(vm_argc
, vm_args
, NULL
);
249 static int vm_init_stack(unsigned size
) {
250 if(mem
.lstack
) return 1;
251 unsigned want
= ALIGN(size
, 4096);
252 unsigned char *p
= realloc(mem
.mem
, mem
.capa
+want
);
254 dprintf(2, "error: could not allocate stack!\n");
260 registers
[AR_SP
].i
= text_end
;
261 registers
[AR_OP
].i
= text_end
;
262 vm_setup_startenv_s();
266 static int grow_text(size_t req
) {
267 /* add 4 more slots than strictly necessary so we can access
268 * at least 1 full-length insn past text end without crash */
269 req
+= 4*sizeof(int);
270 size_t need
= mem
.ltext
+ req
;
271 if(need
> mem
.capa
-mem
.lheap
-mem
.lstack
) {
273 dprintf(2, "error: cannot enlarge text segment once execution started!\n");
276 size_t want
= ALIGN(need
, 4096);
277 unsigned char *p
= realloc(mem
.mem
, want
);
279 dprintf(2, "error: allocating memory failed!\n");
288 static int append_code(int *code
, size_t cnt
) {
289 if(!grow_text((cnt
+1)*4)) return 0;
291 for(i
= 0; i
< cnt
; i
++) {
292 memcpy(text
+mem
.ltext
, &code
[i
], 4);
295 memcpy(text
+mem
.ltext
, "\0\0\0\0", 4);
299 static void vm_reset_register_usage() {
301 for(i
= AR_NULL
+ 1; i
< AR_MAX
; i
++)
302 registers
[i
].ru
= RU_NONE
;
305 static void vm_init() {
307 /* initialize registers to an easily recognisable junk value */
308 for(i
= AR_NULL
+ 1; i
< ARRAY_SIZE(registers
); i
++) {
311 vm_reset_register_usage();
312 registers
[AR_SP
].i
= -1;
313 registers
[AR_NULL
].i
= 0;
314 int was_null
= text
== 0;
315 /* set up EIP so vm_state() doesn't crash */
317 /* put NULL insn as first instruction so VM doesn't execute
318 random garbage in mem */
319 if(was_null
) memcpy(text
, "\0\0\0\0", 4);
322 static inline int consume_int(int **eip
) {
327 static void change_reg_usage(int regno
, enum RegisterAccess ra
) {
328 if(regno
>= AR_MAX
) {
329 vm_signal(VM_SIGSEGV
, 0);
332 registers
[regno
].ru
= get_reg_usage(regno
, registers
[regno
].ru
, ra
);
335 static void vm_update_register_usage(int *eip
) {
336 const struct regaccess_info
*ri
= ®access_info
[*eip
];
337 if(ri
->ra_reg1
) change_reg_usage(eip
[1], ri
->ra_reg1
);
338 if(ri
->ra_reg2
) change_reg_usage(eip
[2], ri
->ra_reg2
);
339 if(ri
->ra_mar
) change_reg_usage(AR_MAR
, ri
->ra_mar
);
340 if(ri
->ra_sp
) change_reg_usage(AR_SP
, ri
->ra_sp
);
343 static void write_mem1(int off
, int val
) {
344 unsigned char *m
= memory
+off
;
347 static void write_mem2(int off
, int val
) {
348 unsigned short *m
= (void*) (memory
+off
);
351 static void write_mem(int off
, int val
) {
352 int *m
= (void*) (memory
+off
);
356 static int read_mem(int off
) {
358 memcpy(&ret
, memory
+off
, 4);
362 static int vm_push(int value
) {
363 if(!canwrite(registers
[AR_SP
].i
, 4)) return 0;
364 write_mem(registers
[AR_SP
].i
, value
);
365 registers
[AR_SP
].i
+= 4;
369 static int vm_pop(int *value
) {
370 if((int) registers
[AR_SP
].i
>= 4) {
371 registers
[AR_SP
].i
-= 4;
372 *value
= read_mem(registers
[AR_SP
].i
);
378 static int vm_syscall(void) {
380 scno
= registers
[AR_AX
].i
,
381 arg1
= registers
[AR_BX
].i
,
382 arg2
= registers
[AR_CX
].i
,
383 arg3
= registers
[AR_DX
].i
;
384 /* we follow linux x86_64 syscall numbers for simplicity */
386 case 0: /* SYS_read (fd, buf, size) */
388 case 1: /* SYS_write (fd, buf, size) */
389 if(!canread(arg2
, arg3
)) return -EFAULT
;
391 ret
= read(arg1
, ((char*)memory
)+arg2
, arg3
);
393 ret
= write(arg1
, ((char*)memory
)+arg2
, arg3
);
394 if(ret
== -1) return -errno
;
396 case 60: /* SYS_exit (exitcode) */
398 default: return -ENOSYS
;
402 static int label_check() {
403 if(tglist_getsize(label_refs
)) {
404 dprintf(2, "error: unresolved label refs!\n");
405 size_t i
; struct label_ref
*l
;
406 for(i
=0; i
<tglist_getsize(label_refs
); ++i
) {
407 l
= &tglist_get(label_refs
, i
);
408 dprintf(2, "%s@%u\n", l
->name
, l
->insoff
);
415 #define CODE_INT(X) eip[X]
416 #define CODE_FLOAT(X) ((float*)eip)[X]
417 #define REGI(X) registers[CODE_INT(X)&0xff].i
418 #define REGF(X) registers[CODE_INT(X)&0xff].f
420 static int vm_step(int run_context
) {
421 /* we use register AR_NULL as instruction pointer */
422 int *eip
= (void*)(text
+ EIP
);
425 // breakpoints can be set only in interactive mode
428 vm_signal(VM_SIGILL
, 0);
432 if(*eip
& BREAKPOINT_FLAG
) {
433 *eip
&= ~BREAKPOINT_FLAG
;
436 if(!run_context
) vm_reset_register_usage();
437 vm_update_register_usage(eip
);
438 } else if(op
>= SCMD_MAX
) {
439 vm_signal(VM_SIGILL
, 0);
442 int eip_inc
= 1 + opcodes
[op
].argcount
;
447 /* don't modify IP */
449 dprintf(2, "no code at IP %u.\n", EIP
);
452 REGI(1) += CODE_INT(2);
455 REGI(1) -= CODE_INT(2);
461 REGI(1) = CODE_INT(2);
482 REGI(1) = !!(REGI(1) == REGI(2));
485 REGI(1) = !!(REGI(1) != REGI(2));
488 REGI(1) = !!(REGI(1) > REGI(2));
491 REGI(1) = !!(REGI(1) < REGI(2));
494 REGI(1) = !!(REGI(1) >= REGI(2));
497 REGI(1) = !!(REGI(1) <= REGI(2));
500 REGI(1) = !!(REGI(1) && REGI(2));
503 REGI(1) = !!(REGI(1) || REGI(2));
505 case SCMD_LOADSPOFFS
:
506 registers
[AR_MAR
].i
= registers
[AR_SP
].i
- CODE_INT(1);
509 if(!vm_push(REGI(1))) goto oob
;
512 if(!vm_pop(®I(1))) goto oob
;
515 REGI(1) *= CODE_INT(2);
532 case SCMD_SHIFTRIGHT
:
536 REGF(1) += CODE_FLOAT(2);
539 REGF(1) -= CODE_FLOAT(2);
554 REGI(1) = !!(REGF(1) > REGF(2));
557 REGI(1) = !!(REGF(1) < REGF(2));
560 REGI(1) = !!(REGF(1) >= REGF(2));
563 REGI(1) = !!(REGF(1) <= REGF(2));
565 case SCMD_ZEROMEMORY
:
567 if(canwrite(registers
[AR_MAR
].i
, tmp
)) {
568 memset(((char*)memory
)+registers
[AR_MAR
].i
,0,tmp
);
573 if(tmp
<= 0 || tmp
> 4 || tmp
== 3) {
574 dprintf(2, "VM: invalid memcpy use at IP %u\n", EIP
);
591 if(canwrite(registers
[AR_MAR
].i
, tmp
)) {
593 case 4: write_mem (registers
[AR_MAR
].i
, val
); break;
594 case 2: write_mem2(registers
[AR_MAR
].i
, val
); break;
595 case 1: write_mem1(registers
[AR_MAR
].i
, val
); break;
599 vm_signal(VM_SIGSEGV
, 0);
612 if(canread(registers
[AR_MAR
].i
, tmp
)) {
614 memcpy(&val
, memory
+registers
[AR_MAR
].i
, 4);
616 case 4: REGI(1) = val
; break;
617 case 2: REGI(1) = val
& 0xffff; break;
618 case 1: REGI(1) = val
& 0xff; break;
623 if(registers
[AR_AX
].i
== 0) goto jump
;
626 if(registers
[AR_AX
].i
== 0) break;
632 if((unsigned)tmp
< text_end
&& !(tmp
&3))
633 registers
[AR_NULL
].i
= tmp
;
635 vm_signal(VM_SIGSEGV
, tmp
);
641 if(!vm_push(registers
[AR_NULL
].i
+ eip_inc
*4)) goto oob
;
645 registers
[AR_SP
].i
-= 4;
646 tmp
= read_mem(registers
[AR_SP
].i
);
649 /* we re-purpose "callscr" mnemonic to mean syscall,
650 as it is unused in ags-emitted bytecode.
651 using it is unportable, it works only in agssim.
652 the register arg for callscr instruction is ignored.
653 the arguments are passed in regs ax,bx,cx,dx,op
654 in this order, where the first arg is the syscall
655 number. return value is put in ax. */
656 registers
[AR_AX
].i
= vm_syscall();
658 case SCMD_CHECKBOUNDS
:
659 if(REGI(1) > CODE_INT(2)) vm_signal(VM_SIGABRT
, 0);
662 case SCMD_DYNAMICBOUNDS
:
663 case SCMD_MEMZEROPTRND
:
664 case SCMD_LOOPCHECKOFF
:
665 case SCMD_CHECKNULLREG
:
666 case SCMD_STRINGSNOTEQ
:
667 case SCMD_STRINGSEQUAL
:
668 case SCMD_CREATESTRING
:
670 case SCMD_MEMINITPTR
:
671 case SCMD_MEMZEROPTR
:
672 case SCMD_MEMREADPTR
:
673 case SCMD_MEMWRITEPTR
:
675 case SCMD_NUMFUNCARGS
:
676 case SCMD_SUBREALSTACK
:
679 dprintf(2, "info: %s not implemented yet\n", opcodes
[*eip
].mnemonic
);
681 size_t i
, l
= opcodes
[*eip
].argcount
;
682 for(i
= 0; i
< l
; i
++) ++(*eip
);
686 vm_signal(VM_SIGILL
, 0);
689 registers
[AR_NULL
].i
+= eip_inc
*4;
693 static inline char *int_to_str(int value
, char* out
) {
694 sprintf(out
, "%d", value
);
698 static int* get_next_ip(int *eip
, int off
) {
699 int *ret
= eip
, i
, op
;
700 for(i
=0; i
<off
; ++i
) {
701 op
= *ret
& OPCODE_MASK
;
703 ret
+=1+opcodes
[op
].argcount
;
710 static const char *get_regname(unsigned regno
) {
711 if(regno
< AR_MAX
) return regnames
[regno
];
715 static void vm_state() {
716 if(!interactive
) return;
717 static const char ru_strings
[][3] = {
719 [RU_READ
] = {'R', 0},
720 [RU_WRITE
] = {'W', 0},
721 [RU_WRITE_AFTER_READ
] = {'R', 'W', 0},
723 static const char regorder
[] = {
724 0, AR_MAR
, AR_OP
, AR_SP
, -1,
725 AR_AX
, AR_BX
, AR_CX
, AR_DX
, -1, -1};
727 for(j
=0; j
< ARRAY_SIZE(regorder
)-1; ++j
) {
729 if(i
== -1) printf("\n");
731 printf("%-3s: %-2s %-11d", i
== 0 ? "eip" : regnames
[i
], ru_strings
[registers
[i
].ru
], registers
[i
].i
);
732 if(regorder
[j
+1] != -1) printf(" ");
735 char stackview
[5][24];
739 for(j
=0,i
= MIN(registers
[AR_SP
].i
+2*4, text_end
+mem
.lstack
);
740 i
>= MAX(registers
[AR_SP
].i
-2*4, text_end
);
742 sprintf(stackview
[j
],
743 "SL %s %3zu %d", i
== registers
[AR_SP
].i
? ">" : " ", i
, read_mem(i
));
746 int *eip
= (void*)(text
+ registers
[AR_NULL
].i
), wasnull
= 0;
747 for(i
= 0; i
<5; i
++) {
748 char a1b
[32], a2b
[32], a3b
[32], inst
[48];
750 int *nip
= get_next_ip(eip
, i
-2),
751 op
= *nip
& OPCODE_MASK
;
753 const char *arg1
= opcodes
[op
].argcount
== 0 ? "" : \
754 (opcodes
[op
].regcount
> 0 ? get_regname(nip
[1]) : int_to_str(nip
[1], a1b
));
755 const char *arg2
= opcodes
[op
].argcount
< 2 ? "" : \
756 (opcodes
[op
].regcount
> 1 ? get_regname(nip
[2]) : int_to_str(nip
[2], a2b
));
757 const char *arg3
= opcodes
[op
].argcount
< 3 ? "" : \
758 (opcodes
[op
].regcount
> 2 ? get_regname(nip
[3]) : int_to_str(nip
[2], a3b
));
759 if(op
== SCMD_REGTOREG
) {
760 const char* tmp
= arg1
;
761 arg1
= arg2
; arg2
= tmp
;
764 sprintf(inst
, " %s %s %s %s", i
==2?">":" ", opcodes
[op
].mnemonic
, arg1
, arg2
);
767 sprintf(inst
, "%d", *nip
);
771 printf("%-52s %s\n", inst
, stackview
[i
]);
776 if(!label_check()) return;
778 if(!vm_step(1) || vm_return
) break;
782 void vm_trace(void) {
783 if(!label_check()) return;
785 if(!vm_step(0) || vm_return
) break;
790 static int usage(int fd
, char *a0
) {
792 "%s [OPTIONS] [filename.s] [-- arguments for ags program]\n"
793 "simple ags vm simulator\n"
794 "useful to examine how a chunk of code modifies VM state\n"
796 "-s stacksize : specify stacksize in KB (default: 16)\n"
797 "-i : interpreter mode - don't print anything, run and exit\n\n"
798 "by default, mode is interactive, sporting the following commands:\n"
799 "!i - reset VM state and IP\n"
803 "!t - trace - run till bp or end with state printed for every insn\n"
804 "!b ADDR - set a breakpoint on ADDR (address or label)\n\n"
805 "example: %s -i printargs.s -- hello world\n"
810 static int lastcommand
;
813 UC_NEXT
, /* step-over */
821 static void execute_user_command_i(int uc
, char* param
) {
823 case UC_STEP
: if(label_check()) vm_step(0); break;
826 if(isdigit(param
[0]))
829 ptr
= get_label_offset(param
);
831 dprintf(2, "label %s not found!\n", param
);
836 if(addr
>= text_end
) {
837 dprintf(2, "breakpoint offset %d out of bounds\n", addr
);
841 memcpy(&insn
, text
+addr
, 4);
842 insn
|= BREAKPOINT_FLAG
;
843 memcpy(text
+addr
, &insn
, 4);
846 case UC_NEXT
: *get_next_ip((void*)(text
+EIP
), 1) |= BREAKPOINT_FLAG
;
848 case UC_RUN
: vm_run(); break;
849 case UC_TRACE
: vm_trace(); break;
850 case UC_INIT
: vm_init(); break;
851 case UC_QUIT
: exit(0); break;
852 case UC_HELP
: usage(1, "agssim"); break;
857 static void execute_user_command(char *cmd
) {
858 if(!vm_init_stack(stacksize
)) return;
861 while(!isspace(*param
)) param
++;
862 while(isspace(*param
)) param
++;
864 else if(!strcmp(cmd
, "s")) uc
= UC_STEP
;
865 else if(!strcmp(cmd
, "r")) uc
= UC_RUN
;
866 else if(!strcmp(cmd
, "i")) uc
= UC_INIT
;
867 else if(!strcmp(cmd
, "q")) uc
= UC_QUIT
;
868 else if(!strcmp(cmd
, "h")) uc
= UC_HELP
;
869 else if(!strcmp(cmd
, "n")) uc
= UC_NEXT
;
870 else if(*cmd
== 't') uc
= UC_TRACE
;
871 else if(*cmd
== 'b') uc
= UC_BP
;
873 dprintf(2, "unknown command\n");
876 execute_user_command_i(uc
, param
);
879 int main(int argc
, char** argv
) {
881 stacksize
= DEFAULT_STACKSIZE
;
884 while((c
= getopt(argc
, argv
, "is:")) != EOF
) switch(c
) {
885 case 'i': interactive
= 0; break;
886 case 's': stacksize
= ALIGN(atoi(optarg
) * 1024, 4096); break;
887 default: return usage(2, argv
[0]);
889 if(argv
[optind
] && optind
>= 1 && strcmp(argv
[optind
-1], "--")) {
890 in
= fopen(argv
[optind
], "r");
892 dprintf(2, "error opening %s\n", argv
[optind
]);
900 vm_push_arg(argv
[0]);
901 if(argv
[optind
] && !strcmp(argv
[optind
], "--"))
903 while(argv
[optind
]) vm_push_arg(argv
[optind
++]);
905 char buf
[1024], *sym
;
906 char convbuf
[sizeof(buf
)]; /* to convert escaped string into non-escaped version */
908 if(interactive
) printf(ADS
" - type !h for help\n");
911 while(fgets(buf
, sizeof buf
, in
)) {
915 char* p
= buf
, *pend
= buf
+ sizeof buf
;
916 if(*p
== '\n' && lastcommand
) {
917 execute_user_command_i(lastcommand
, "");
920 if(*p
== '#' || *p
== ';') continue;
922 char *n
= strchr(p
, '\n');
924 execute_user_command(p
+1);
927 while(isspace(*p
) && p
< pend
) p
++;
931 while(!isspace(*p
) && p
< pend
) p
++;
933 size_t l
= strlen(sym
);
934 if(l
> 1 && sym
[l
-1] == ':') {
935 // functionstart or label
937 resolve_label(sym
, mem
.ltext
);
938 unsigned *loff
= get_label_offset(sym
);
939 if(loff
) dprintf(2, "warning: label %s overwritten\n", sym
);
940 add_label(sym
, mem
.ltext
);
943 unsigned instr
= find_insn(sym
);
945 dprintf(2, "line %zu: error: unknown instruction '%s'\n", lineno
, sym
);
950 for(arg
= 0; arg
< opcodes
[instr
].argcount
; arg
++) {
951 sym
= finalize_arg(&p
, pend
, convbuf
, sizeof(convbuf
));
953 dprintf(2, "line %zu: error: expected \"\n", lineno
);
957 if(arg
< opcodes
[instr
].regcount
) {
959 if(value
== AR_NULL
) {
961 dprintf(2, "line %zu: error: expected register name!\n", lineno
);
964 if(instr
== SCMD_REGTOREG
) {
965 /* fix reversed order of arguments */
968 while(p
< pend
&& *p
!= ',' && !isspace(*p
)) p
++;
972 if(value
== AR_NULL
) goto needreg_err
;
980 /* immediate can be function name, string,
981 * variable name, stack fixup, or numeric value */
983 size_t l
= strlen(sym
)-1, tl
= mem
.ltext
;
984 if(!append_code((int[2]){SCMD_JMP
, tl
+8+ALIGN(l
, 4)}, 2)) goto loop_footer
;
987 while((ssize_t
)l
>= 0) {
989 memcpy(&x
, p
, l
>=4?4:l
);
990 if(!append_code(&x
, 1)) goto loop_footer
;
995 } else if(sym
[0] == '@') {
996 dprintf(2, "error: global variable handling not implemented\n");
998 } else if(sym
[0] == '.') {
999 if(memcmp(sym
+1, "stack", 5)) {
1000 dprintf(2, "error: expected stack\n");
1003 dprintf(2, "error: stack fixup not implemented\n");
1005 } else if(isdigit(sym
[0]) || sym
[0] == '-') {
1006 if(sym
[0] == '-') assert(isdigit(sym
[1]));
1012 case SCMD_JMP
: case SCMD_JZ
: case SCMD_JNZ
: {
1014 unsigned *loff
= get_label_offset(sym
);
1016 add_label_ref(sym
, mem
.ltext
+pos
*4);
1018 } else value
= *loff
;
1021 if(!isdigit(sym
[0])) {
1022 dprintf(2, "line %zu: error: expected number\n", lineno
);
1028 code
[pos
++] = value
;
1030 append_code(code
, pos
);
1033 if(!interactive
) execute_user_command("r");
1034 else if(in
!= stdin
) {