Makefile: don't link all objs to all programs
[rofl0r-agsutils.git] / Script.c
blob49334a320d47b355b6d8c6783b2b3c12d46b8444
1 #define _GNU_SOURCE
2 #include "Script.h"
3 #include "Script_internal.h"
4 #include "endianness.h"
5 #include <string.h>
6 #include <stdlib.h>
7 #include <assert.h>
9 #define COMMENT(F, FMT, ...) fprintf(F, "; " FMT, ##__VA_ARGS__)
11 static int dump_sections(AF* a, FILE *f, size_t start, size_t count) {
12 if(count) {
13 AF_set_pos(a, start);
14 fprintf(f, ".sections\n");
15 char buf[300];
16 size_t i = 0;
17 for(; i < count; i++) {
18 if(!AF_read_string(a, buf, sizeof(buf))) return 0;
19 int off = AF_read_int(a);
20 fprintf(f, "\"%s\" = %d\n", buf, off);
22 fprintf(f, "\n");
24 return 1;
27 #include "StringEscape.h"
28 static int dump_strings(AF* a, FILE *f, size_t start, size_t len) {
29 if(!len) return 1;
30 AF_set_pos(a, start);
31 fprintf(f, ".%s\n", "strings");
32 char *buf = malloc(len), *p = buf, escapebuf[4096];
33 if(len != (size_t) AF_read(a, buf, len)) {
34 free(buf);
35 return 0;
37 while(1) {
38 escape(p, escapebuf, sizeof(escapebuf));
39 fprintf(f, "\"%s\"\n", escapebuf);
40 size_t l = strlen(p);
41 p += l+1;
42 len -= l;
43 if(len == 0 || --len == 0) break;
45 free(buf);
46 return 1;
49 static void free_fixup_data(struct fixup_data *ret) {
50 size_t i;
51 for(i = 0; i <= FIXUP_MAX; i++) {
52 free(ret->codeindex_per[i]);
54 free(ret->codeindex);
55 free(ret->types);
58 /* fixup_data needs to be zeroed */
59 static int get_fixups(AF* a, size_t start, size_t count, struct fixup_data *ret) {
60 size_t i;
61 if(!(ret->types = malloc(count))) goto err;
62 if(!(ret->codeindex = malloc(count * sizeof(unsigned)))) goto err;
64 AF_set_pos(a, start);
66 if(count != (size_t) AF_read(a, ret->types, count)) goto err;
67 for(i = 0; i < count; i++) {
68 assert(ret->types[i]>=0 && ret->types[i]<=FIXUP_MAX);
69 ret->count[ret->types[i]]++;
70 ret->codeindex[i] = AF_read_uint(a);
72 for(i = 0; i <= FIXUP_MAX; i++) {
73 ret->codeindex_per[i] = malloc(ret->count[i] * sizeof(unsigned));
74 if(!ret->codeindex_per[i]) goto err;
75 ret->count[i] = 0; /* reset to 0 to use as index i.t. next loop */
78 for(i = 0; i < count; i++) {
79 ret->codeindex_per[ret->types[i]][ret->count[ret->types[i]]++] = ret->codeindex[i];
82 return 1;
83 err:
84 free_fixup_data(ret);
85 return 0;
88 static int dump_fixups(FILE *f, size_t count, struct fixup_data *fxd) {
89 static const char* typenames[] = {
90 [FIXUP_GLOBALDATA] = "FIXUP_GLOBALDATA",
91 [FIXUP_FUNCTION] = "FIXUP_FUNCTION",
92 [FIXUP_STRING] = "FIXUP_STRING",
93 [FIXUP_IMPORT] = "FIXUP_IMPORT",
94 [FIXUP_DATADATA] = "FIXUP_DATADATA",
95 [FIXUP_STACK] = "FIXUP_STACK",
98 fprintf(f, ".%ss\n", "fixup");
99 size_t i;
100 for(i = 0; i < count; i++) {
101 fprintf(f, "%s: %.12u\n", typenames[(int)fxd->types[i]], fxd->codeindex[i]);
103 return 1;
107 static int dump_import_export(AF* a, FILE *f, size_t start, size_t count, int import) {
108 static const char* secnames[2] = { "export", "import" };
109 const char* secname = secnames[import];
110 size_t i;
111 char buf[256]; /* arbitrarily chosen */
112 if(!count) return 1;
113 AF_set_pos(a, start);
114 fprintf(f, ".%ss\n", secname);
115 for(i = 0; i < count; i++) {
116 if(!AF_read_string(a, buf, sizeof(buf))) return 0;
117 fprintf(f, "%.12zu\"%s\"\n", i, buf);
118 if(!import) {
119 unsigned int addr = AF_read_uint(a);
120 fprintf(f, "%d:%.12u\n", addr >> 24, addr & 0x00FFFFFF);
123 return 1;
126 static struct export* get_exports(AF* a, size_t start, size_t count) {
127 if(!count) return 0;
128 struct export* fl = malloc(count * sizeof(struct export));
129 AF_set_pos(a, start);
130 char buf[4096]; size_t i;
131 for(i = 0; i < count; i++) {
132 if(!AF_read_string(a, buf, sizeof(buf))) return 0;
133 fl[i].fn = strdup(buf);
134 unsigned int addr = AF_read_uint(a);
135 fl[i].type = addr >> 24;
136 fl[i].instr = (addr & 0x00FFFFFF);
138 return fl;
141 static struct importlist get_imports(AF* a, size_t start, size_t count) {
142 struct importlist ret = {0};
143 if(!count) return ret;
144 ret.names = malloc(count * sizeof(char*));
145 if(!ret.names) return ret;
146 AF_set_pos(a, start);
147 char buf[4096]; size_t i;
148 for(i = 0; i < count; i++) {
149 if(!AF_read_string(a, buf, sizeof(buf))) {
150 free(ret.names);
151 return (struct importlist) {0};
153 ret.names[i] = strdup(buf);
155 return ret;
158 struct labels {
159 unsigned count;
160 unsigned *insno;
163 static int sort_comp(const void* xp, const void *yp) {
164 const unsigned *x = xp, *y = yp;
165 if(*x == *y) return 0;
166 else if(*x > *y) return 1;
167 else return -1;
170 #include "ags_cpu.h"
171 static struct labels get_labels(unsigned *code, size_t count) {
172 struct labels ret = {0, 0};
173 if(!count) return ret;
174 size_t capa = 0;
175 unsigned *p = 0;
176 size_t insno = 0;
177 unsigned insn;
178 while(insno < count) {
179 if(ret.count + 1 > capa) {
180 capa = capa ? capa * 2 : 16;
181 if(!(p = realloc(ret.insno, capa * sizeof(unsigned*)))) {
182 if(ret.insno) free(ret.insno);
183 ret.count = 0;
184 ret.insno = 0;
185 return ret;
186 } else ret.insno = p;
188 insn = code[insno] & 0x00ffffff;
189 insno++;
190 int isjmp = 0;
191 assert(insn < SCMD_MAX);
192 switch(insn) {
193 case SCMD_JZ: case SCMD_JMP: case SCMD_JNZ:
194 isjmp = 1;
195 default:
196 break;
198 size_t i = 0;
199 for(; i < opcodes[insn].argcount; i++) {
200 int val = code[insno];
201 insno++;
202 if(isjmp) {
203 if((int) insno + val < 0 || insno + val >= count || code[insno + val] > SCMD_MAX) {
204 dprintf(2, "error: label referenced from jump at %zu is out of bounds\n"
205 "or points to non-instruction start code.\n", insno);
206 assert(0);
208 ret.insno[ret.count] = insno + val;
209 ret.count++;
213 qsort(ret.insno, ret.count, sizeof(unsigned), sort_comp);
214 return ret;
217 static struct strings get_strings(AF* a, size_t start, size_t size) {
218 struct strings ret = {0,0,0};
219 if(!size) return ret;
220 if(!(ret.data = malloc(size))) return ret;
221 AF_set_pos(a, start);
222 if(size != (size_t) AF_read(a, ret.data, size)) {
223 free1:
224 free(ret.data);
225 retrn:
226 return ret;
228 size_t i, strcnt = 0;
229 for(i = 0; i < size; i++) {
230 if(!ret.data[i]) strcnt++;
232 if(!(ret.strings = malloc(strcnt * sizeof(char*)))) goto free1;
233 char* p = ret.data;
234 size_t l = 0;
235 strcnt = 0;
237 for(i = 0; i < size; i++) {
238 if(!ret.data[i]) {
239 ret.strings[strcnt] = p;
240 p += l + 1;
241 l = 0;
242 strcnt++;
243 } else
244 l++;
246 ret.count = strcnt;
247 goto retrn;
250 static char* get_varname(struct export* exp, size_t expcount, unsigned globaloffset) {
251 size_t i = 0;
252 for(; i < expcount; i++)
253 if(exp[i].type == EXPORT_DATA && exp[i].instr == globaloffset)
254 return exp[i].fn;
255 return 0;
258 unsigned *get_code(AF *a, size_t start, size_t count) {
259 unsigned *ret = malloc(count * sizeof(unsigned));
260 if(!ret) return 0;
261 AF_set_pos(a, start);
262 size_t i;
263 for(i=0; i<count; i++)
264 ret[i] = AF_read_uint(a);
265 return ret;
268 static unsigned get_varsize_from_instr(unsigned* code, size_t codecount, size_t index) {
269 assert(index < codecount);
270 switch(code[index]) {
271 case SCMD_MEMREADB: case SCMD_MEMWRITEB:
272 return 1;
273 case SCMD_MEMREADW: case SCMD_MEMWRITEW:
274 return 2;
275 case SCMD_MEMWRITEPTR:
276 case SCMD_MEMREADPTR:
277 case SCMD_MEMZEROPTR:
278 case SCMD_MEMINITPTR:
279 case SCMD_MEMREAD: case SCMD_MEMWRITE:
280 return 4;
281 default:
282 return 0;
286 struct fixup_resolved {
287 unsigned code;
288 unsigned offset;
291 static int fixup_cmp(const void *a, const void *b) {
292 const struct fixup_resolved *u1 = a;
293 const struct fixup_resolved *u2 = b;
294 return (u1->code - u2->code);
297 /* assumes list members are sorted. set iter to -1 for first call */
298 int find_next_match(struct fixup_resolved *list, size_t nel, unsigned value, size_t *iter) {
299 struct fixup_resolved comparer = {
300 .code = value
302 if(*iter == (size_t)-1) {
303 struct fixup_resolved* ret = bsearch(&comparer, list, nel, sizeof(list[0]), fixup_cmp);
304 if(!ret) return 0;
305 *iter = ret - list;
306 while(*iter>=1 && list[*iter-1].code == value)
307 --(*iter);
308 return 1;
309 } else {
310 ++(*iter);
311 if(*iter < nel && list[*iter].code == value)
312 return 1;
313 return 0;
317 static int has_datadata_fixup(unsigned gdoffset, struct fixup_data *fxd) {
318 size_t i;
319 for(i = 0; i < fxd->count[FIXUP_DATADATA]; i++)
320 if(fxd->codeindex_per[FIXUP_DATADATA][i] == gdoffset)
321 return 1;
322 return 0;
325 struct varinfo find_fixup_for_globaldata(FILE *f, size_t offset,
326 struct fixup_resolved *fxlist_resolved, size_t fxlist_cnt,
327 unsigned* code, size_t codecount)
330 size_t iter = (size_t)-1, x;
331 struct varinfo ret = {0,0};
332 while(find_next_match(fxlist_resolved, fxlist_cnt, offset, &iter)) {
333 x = fxlist_resolved[iter].offset;
334 if(1) {
335 assert(x + 1 < codecount);
336 if(1) {
337 ret.numrefs++;
338 unsigned oldvarsize = ret.varsize;
339 ret.varsize = get_varsize_from_instr(code, codecount, x+1);
340 if(!ret.varsize) switch(code[x+1]) {
341 case SCMD_REGTOREG:
342 // li mar, @Obj; mr ax, mar; callobj ax
343 if(x+5 < codecount &&
344 code[x+2] == AR_MAR &&
345 code[x+4] == SCMD_CALLOBJ &&
346 code[x+3] == code[x+5]) {
347 ret.varsize = 4;
349 break;
350 case SCMD_MUL:
351 // li mar, @var; muli dx, 4; ptrget mar; ptrassert; dynamicbounds dx
352 if(x+5 < codecount &&
353 code[x+2] != AR_MAR &&
354 code[x+4] == SCMD_MEMREADPTR &&
355 code[x+5] == AR_MAR)
356 ret.varsize = 4;
357 // muli is used as an array index like:
358 // muli REG, 4; add MAR, REG; PUSH/POP MAR; ...
359 else if(x+11 < codecount &&
360 code[x+4] == SCMD_ADDREG &&
361 code[x+5] == AR_MAR &&
362 code[x+6] == code[x+2] &&
363 code[x+7] == SCMD_PUSHREG &&
364 code[x+8] == AR_MAR &&
365 code[x+9] == SCMD_POPREG &&
366 code[x+10] == AR_MAR) {
367 ret.varsize = get_varsize_from_instr(code, codecount, x+11);
368 if(!ret.varsize &&
369 x+14 < codecount &&
370 code[x+11] == SCMD_ADDREG &&
371 code[x+12] == AR_MAR &&
372 code[x+13] != code[x+2]
374 /* this variation adds another reg to mar before calling ptrget */
375 ret.varsize = get_varsize_from_instr(code, codecount, x+14);
376 else if(!ret.varsize &&
377 x+13 < codecount &&
378 code[x+11] == SCMD_PUSHREG &&
379 code[x+12] == AR_AX)
380 /* this variation pushes ax on the stack before doing a ptrget ax, which overwrites ax */
381 ret.varsize = get_varsize_from_instr(code, codecount, x+13);
383 /* muli dx, 4; add mar, dx; add mar, cx; ptr... */
384 else if(x+10 < codecount &&
385 code[x+4] == SCMD_ADDREG &&
386 code[x+5] == AR_MAR &&
387 (code[x+6] == AR_CX || code[x+6] == AR_DX) &&
388 code[x+7] == SCMD_ADDREG &&
389 code[x+8] == AR_MAR &&
390 (code[x+9] == AR_CX || code[x+9] == AR_DX))
391 ret.varsize = get_varsize_from_instr(code, codecount, x+10);
392 else if(x+7 < codecount &&
393 code[x+4] == SCMD_ADDREG &&
394 code[x+5] == AR_MAR &&
395 code[x+6] == code[x+2]) {
396 ret.varsize = get_varsize_from_instr(code, codecount, x+7);
397 if(!ret.varsize &&
398 x + 9 < codecount &&
399 code[x+7] == SCMD_PUSHREG &&
400 code[x+8] == AR_AX)
401 ret.varsize = get_varsize_from_instr(code, codecount, x+9);
404 break;
405 case SCMD_ADDREG:
406 // addreg is typically used on an index register into an array
407 // followed by a byteread/store of the desired size
408 // the index register needs to be added to mar reg
409 assert(x+2 < codecount && code[x+2] == AR_MAR);
410 ret.varsize = get_varsize_from_instr(code, codecount, x+4);
411 // a typical method call
412 if(!ret.varsize && x+8 < codecount &&
413 code[x+4] == SCMD_REGTOREG &&
414 code[x+5] == AR_MAR &&
415 code[x+7] == SCMD_CALLOBJ &&
416 code[x+6] == code[x+8])
417 ret.varsize = 4;
418 break;
419 case SCMD_PUSHREG:
420 // ptrget and similar ops are typically preceded by push mar, pop mar
421 if(x+4 < codecount &&
422 code[x+2] == AR_MAR &&
423 code[x+3] == SCMD_POPREG &&
424 code[x+4] == AR_MAR) {
425 ret.varsize = get_varsize_from_instr(code, codecount, x+5);
426 if(!ret.varsize && x+7 < codecount &&
427 code[x+5] == SCMD_ADDREG &&
428 code[x+6] == AR_MAR)
429 ret.varsize = get_varsize_from_instr(code, codecount, x+8);
430 } else if(x+2 < codecount &&
431 code[x+2] == AR_AX)
432 /* ptrget is sometimes preceded by push ax */
433 ret.varsize = get_varsize_from_instr(code, codecount, x+3);
434 break;
436 if(!ret.varsize) {
437 if(oldvarsize) {
438 /* don't bother guessing the varsize if we already determined it */
439 ret.varsize = oldvarsize;
441 dprintf(2, "warning: '%s' globaldata fixup on insno %zu offset %zu\n",
442 opcodes[code[x+1]].mnemonic, x+1, offset);
443 COMMENT(f, "warning: '%s' globaldata fixup on insno %zu offset %zu\n",
444 opcodes[code[x+1]].mnemonic, x+1, offset);
446 if(oldvarsize != 0 && oldvarsize != ret.varsize)
447 assert(0);
451 return ret;
454 static int is_all_zeroes(const char* buf, int len) {
455 while(len--) {
456 if(*buf != 0) return 0;
457 buf++;
459 return 1;
462 static const char* get_varsize_typename(unsigned varsize) {
463 static const char typenames[][6] = {[0]="ERR", [1]="char", [2]="short", [4]="int"};
464 switch(varsize) {
465 case 0: case 1: case 2: case 4:
466 return typenames[varsize];
467 case 200:
468 return "string";
470 return 0;
473 static struct varinfo get_varinfo_from_code(
474 unsigned *code, size_t codesize,
475 size_t offset,
476 struct fixup_data *fxd,
477 struct fixup_resolved *gd_fixups_resolved,
478 FILE *f)
480 struct varinfo vi;
481 if(has_datadata_fixup(offset, fxd))
482 vi = (struct varinfo){0,4};
483 else {
484 vi = find_fixup_for_globaldata(f, offset, gd_fixups_resolved, fxd->count[FIXUP_GLOBALDATA], code, codesize);
485 if(vi.varsize == 0 && has_datadata_fixup(offset+200, fxd))
486 vi.varsize = 200;
488 return vi;
491 int get_varinfo_from_exports(size_t offs, struct export *exp, size_t expcount, struct varinfo *vi)
493 struct export *end = exp + expcount;
494 for(; exp < end; ++exp)
495 if(exp->instr == offs && exp->type == EXPORT_DATA) {
496 vi->varsize = 1; /* unfortunately no size info is available, so we need to default to char for safety */
497 return 1;
499 return 0;
502 static int dump_globaldata(AF *a, FILE *f, size_t start, size_t size,
503 struct export* exp, size_t expcount,
504 struct fixup_data *fxd,
505 unsigned *code, size_t codesize) {
506 if(!size) return 1;
508 size_t fxcount = fxd->count[FIXUP_GLOBALDATA];
509 struct fixup_resolved *gd_fixups_resolved = malloc(sizeof(struct fixup_resolved) * fxcount);
510 if(!gd_fixups_resolved) return 0;
511 size_t i;
512 for(i=0; i < fxcount; i++) {
513 unsigned x = fxd->codeindex_per[FIXUP_GLOBALDATA][i];
514 assert(x < codesize);
515 gd_fixups_resolved[i].code = code[x];
516 gd_fixups_resolved[i].offset = x;
518 qsort(gd_fixups_resolved, fxcount, sizeof(gd_fixups_resolved[0]), fixup_cmp);
520 fprintf(f, ".%s\n", "data");
521 AF_set_pos(a, start);
523 for(i = 0; i < size; ) {
524 struct varinfo vi;
525 vi = get_varinfo_from_code(code, codesize, i, fxd, gd_fixups_resolved, f);
526 if(vi.varsize == 0) get_varinfo_from_exports(i, exp, expcount, &vi);
527 int x;
528 char *comment = "";
529 int is_str = 0;
531 switch(vi.varsize) {
532 case 200:
534 off_t savepos = AF_get_pos(a);
535 if(i + 204 <= size) {
536 char buf[200];
537 assert(200 == AF_read(a, buf, 200));
538 /* read the datadata fixup content*/
539 x = AF_read_int(a);
540 if(x == i && is_all_zeroes(buf, 200)) {
541 x = 0;
542 AF_set_pos(a, savepos + 200);
543 is_str = 1;
544 break;
547 AF_set_pos(a, savepos);
548 vi.varsize = 0;
549 goto sw;
551 case 4:
552 x = AF_read_int(a);
553 break;
554 case 2:
555 x = AF_read_short(a);
556 break;
557 case 1:
558 x = ByteArray_readByte(a->b);
559 break;
560 case 0:
561 vi.varsize = 1;
562 x = ByteArray_readByte(a->b);
563 if(vi.numrefs) comment = " ; warning: couldn't determine varsize, default to 1";
564 else {
565 comment = " ; unreferenced variable, assuming char";
566 if(x) break;
567 struct varinfo vi2;
568 size_t j = i;
569 while(++j < size) {
570 vi2 = get_varinfo_from_code(code, codesize, j, fxd, gd_fixups_resolved, f);
571 if(vi2.varsize || vi.numrefs || get_varinfo_from_exports(j, exp, expcount, &vi2)) break;
572 x = ByteArray_readByte(a->b);
573 if(x) {
574 ByteArray_set_position_rel(a->b, -1);
575 x = 0;
576 break;
578 ++vi.varsize;
581 break;
583 char* vn = get_varname(exp, expcount, i),
584 *tn = get_varsize_typename(vi.varsize), buf[32];
585 if(!tn || (vi.varsize == 200 && !is_str)) {
586 snprintf(buf, sizeof buf, "char[%u]", vi.varsize);
587 tn = buf;
589 if(has_datadata_fixup(i, fxd)) {
590 if(vn) fprintf(f, "export %s %s = .data + %d%s\n", tn, vn, x, comment);
591 else fprintf(f, "%s var%.6zu = .data + %d%s\n", tn, i, x, comment);
592 } else {
593 if(vn) fprintf(f, "export %s %s = %d%s\n", tn, vn, x, comment);
594 else fprintf(f, "%s var%.6zu = %d%s\n", tn, i, x, comment);
596 i += vi.varsize;
598 free(gd_fixups_resolved);
599 return 1;
602 static int disassemble_code_and_data(AF* a, ASI* s, FILE *f, int flags, struct fixup_data *fxd) {
603 int debugmode = getenv("AGSDEBUG") != 0;
604 size_t start = s->codestart;
605 size_t len = s->codesize * sizeof(unsigned);
607 unsigned *code = get_code(a, s->codestart, s->codesize);
609 struct export* fl = get_exports(a, s->exportstart, s->exportcount);
611 dump_globaldata(a, f, s->globaldatastart, s->globaldatasize, fl, s->exportcount, fxd, code, s->codesize);
613 if(!len) return 1; /* its valid for a scriptfile to have no code at all */
616 struct importlist il = get_imports(a, s->importstart, s->importcount);
618 struct labels lbl = get_labels(code, s->codesize);
620 struct strings str = get_strings(a, s->stringsstart, s->stringssize);
622 AF_set_pos(a, start);
623 fprintf(f, ".%s\n", "text");
625 size_t currInstr = 0, currExp = 0, currFixup = 0, currLbl = 0;
626 char *curr_func = 0;
627 /* the data_data fixups appear to be glued separately onto the fixup logic,
628 * they are the only entries not sorted by instrucion number */
629 while(currFixup < s->fixupcount && fxd->types[currFixup] == FIXUP_DATADATA) currFixup++;
630 while(currInstr < s->codesize) {
631 if(flags & DISAS_DEBUG_OFFSETS) COMMENT(f, "offset: %llu (insno %zu)\n", (long long) AF_get_pos(a), currInstr);
632 unsigned regs, args, insn = AF_read_uint(a), op = insn & 0x00ffffff;
633 assert(op < SCMD_MAX);
634 while(currExp < s->exportcount && fl[currExp].type != EXPORT_FUNCTION)
635 currExp++;
636 if(currExp < s->exportcount && fl[currExp].instr == currInstr) {
637 /* new function starts here */
638 curr_func = fl[currExp].fn;
639 char comment[64], *p = strrchr(fl[currExp].fn, '$');
640 comment[0] = 0;
641 if(p) {
642 int n;
643 if((n = atoi(p+1)) >= 100)
644 sprintf(comment, " ; variadic, %d fixed args", n - 100);
645 else
646 sprintf(comment, " ; %d args", n);
648 fprintf(f, "\n%s:%s\n", curr_func, comment);
649 currExp++;
651 if(currLbl < lbl.count) {
652 if(lbl.insno[currLbl] == currInstr) {
653 size_t numrefs = 0;
654 while(currLbl < lbl.count && lbl.insno[currLbl] == currInstr) {
655 currLbl++; numrefs++;
657 fprintf(f, "label%.12zu: ", currInstr);
658 COMMENT(f, "inside %s, ", curr_func ? curr_func : "???");
659 COMMENT(f, "referenced by %zu spots\n", numrefs);
663 currInstr++;
665 regs = opcodes[op].regcount;
666 args = opcodes[op].argcount;
668 if(insn == SCMD_LINENUM && (flags & DISAS_SKIP_LINENO)) {
669 insn = AF_read_uint(a);
670 COMMENT(f, "line %u\n", insn);
671 currInstr++;
672 continue;
675 if(flags & DISAS_DEBUG_BYTECODE) {
676 unsigned char insbuf[16];
677 unsigned iblen = 0, val;
678 val = end_htole32(insn);
679 memcpy(insbuf+iblen, &val, 4); iblen += 4;
681 off_t currpos = AF_get_pos(a);
683 size_t l;
684 for(l = 0; l < args; l++) {
685 assert(iblen+4 <= sizeof(insbuf));
686 val = AF_read_uint(a);
687 val = end_htole32(val);
688 memcpy(insbuf+iblen, &val, 4); iblen += 4;
691 char printbuf[sizeof(insbuf)*2 + 1], *pb = printbuf;
692 for(l = 0; l < iblen; l++, pb+=2)
693 sprintf(pb, "%02x", (int) insbuf[l]);
694 COMMENT(f, "%s\n", printbuf);
696 AF_set_pos(a, currpos);
699 if(debugmode)
700 fprintf(f, "%.12zu""\t%s ", currInstr - 1, opcodes[op].mnemonic);
701 else
702 fprintf(f, /*"%.12zu"*/"\t%s ", /*currInstr - 1, */opcodes[op].mnemonic);
704 if(insn == SCMD_REGTOREG) {
705 /* the "mov" instruction differs from all others in that the source comes first
706 we do not want that. */
707 unsigned src, dst;
708 src = AF_read_uint(a);
709 currInstr++;
710 dst = AF_read_uint(a);
711 currInstr++;
712 fprintf(f, "%s, %s\n", regnames[dst], regnames[src]);
713 continue;
715 size_t l;
716 for (l = 0; l < args; l++) {
717 char escapebuf[4096];
718 if(l) fprintf(f, ", ");
719 insn = AF_read_uint(a);
720 currInstr++;
721 if((!l && regs) || (l == 1 && regs == 2))
722 fprintf(f, "%s", regnames[insn]);
723 else {
724 while(currFixup < s->fixupcount && fxd->types[currFixup] == FIXUP_DATADATA)
725 currFixup++; /* DATADATA fixups are unrelated to the code */
726 if(currFixup < s->fixupcount && fxd->codeindex[currFixup] == currInstr - 1) {
727 switch(fxd->types[currFixup]) {
728 case FIXUP_IMPORT:
729 if(debugmode)
730 fprintf(f, "IMP:%s", il.names[insn]);
731 else
732 fprintf(f, "%s", il.names[insn]);
733 break;
734 case FIXUP_FUNCTION: {
735 size_t x = 0;
736 for(; x < s->exportcount; x++) {
737 if(fl[x].type == EXPORT_FUNCTION && fl[x].instr == insn) {
738 fprintf(f, "%s", fl[x].fn);
739 break;
742 break;
744 case FIXUP_GLOBALDATA: {
745 char *vn = get_varname(fl, s->exportcount, insn);
746 if(vn) fprintf(f, "@%s", vn);
747 else fprintf(f, "@var%.6u", insn);
748 break; }
749 case FIXUP_STACK: /* it is unclear if and where those ever get generated */
750 fprintf(f, ".stack + %d", insn);
751 break;
752 case FIXUP_STRING:
753 escape(str.data + insn, escapebuf, sizeof(escapebuf));
754 fprintf(f, "\"%s\"", escapebuf);
755 default:
756 break;
758 currFixup++;
759 } else {
760 switch(op) {
761 case SCMD_JMP: case SCMD_JZ: case SCMD_JNZ:
762 fprintf(f, "label%.12zu", currInstr + (int) insn);
763 break;
764 default:
765 fprintf(f, "%d", insn);
770 fprintf(f, "\n");
772 free (fl);
773 return 1;
776 int ASI_disassemble(AF* a, ASI* s, char *fn, int flags) {
777 FILE *f;
778 int ret = 1;
779 if((f = fopen(fn, "w")) == 0)
780 return 0;
781 AF_set_pos(a, s->start);
782 struct fixup_data fxd = {0};
783 if(!get_fixups(a, s->fixupstart, s->fixupcount, &fxd)) return 0;
785 //if(!dump_globaldata(a, fd, s->globaldatastart, s->globaldatasize)) goto err_close;
786 if(!disassemble_code_and_data(a, s, f, flags, &fxd)) goto err_close;
787 if(!dump_strings(a, f, s->stringsstart, s->stringssize)) goto err_close;
788 if((flags & DISAS_DEBUG_FIXUPS) && !dump_fixups(f, s->fixupcount, &fxd)) goto err_close;
789 if(!dump_import_export(a, f, s->importstart, s->importcount, 1)) goto err_close;
790 if(!dump_import_export(a, f, s->exportstart, s->exportcount, 0)) goto err_close;
791 if(!dump_sections(a, f, s->sectionstart, s->sectioncount)) goto err_close;
792 ret:
793 free_fixup_data(&fxd);
794 fclose(f);
795 return ret;
796 err_close:
797 ret = 0;
798 goto ret;
801 int ASI_read_script(AF *a, ASI* s) {
802 s->start = AF_get_pos(a);
803 char sig[4];
804 size_t l = 4;
805 if(l != (size_t) AF_read(a, sig, l)) return 0;
806 assert(memcmp("SCOM", sig, 4) == 0);
807 s->version = AF_read_int(a);
808 s->globaldatasize = AF_read_int(a);
809 s->codesize = AF_read_int(a);
810 s->stringssize = AF_read_int(a);
811 if(s->globaldatasize) {
812 s->globaldatastart = AF_get_pos(a);
813 l = s->globaldatasize;
814 if(!AF_read_junk(a, l)) return 0;
815 } else s->globaldatastart = 0;
816 if(s->codesize) {
817 s->codestart = AF_get_pos(a);
818 l = s->codesize * sizeof(int);
819 if(!AF_read_junk(a, l)) return 0;
820 } else s->codestart = 0;
821 if(s->stringssize) {
822 s->stringsstart = AF_get_pos(a);
823 l = s->stringssize;
824 if(!AF_read_junk(a, l)) return 0;
825 } else s->stringsstart = 0;
826 s->fixupcount = AF_read_int(a);
827 if(s->fixupcount) {
828 s->fixupstart = AF_get_pos(a);
829 l = s->fixupcount;
830 if(!AF_read_junk(a, l)) return 0; /* fixup types */
831 l *= sizeof(int);
832 if(!AF_read_junk(a, l)) return 0; /* fixups */
833 } else s->fixupstart = 0;
834 s->importcount = AF_read_int(a);
835 if(s->importcount) {
836 s->importstart = AF_get_pos(a);
837 char buf[300];
838 size_t i = 0;
839 for(; i < s->importcount; i++)
840 if(!AF_read_string(a, buf, sizeof(buf))) return 0;
841 } else s->importstart = 0;
842 s->exportcount = AF_read_int(a);
843 if(s->exportcount) {
844 s->exportstart = AF_get_pos(a);
845 char buf[300];
846 size_t i = 0;
847 for(; i < s->exportcount; i++) {
848 if(!AF_read_string(a, buf, sizeof(buf))) return 0;
849 AF_read_int(a); /* export_addr */
851 } else s->exportstart = 0;
852 s->sectionstart = 0;
853 s->sectioncount = 0;
854 if (s->version >= 83) {
855 s->sectioncount = AF_read_int(a);
856 if(s->sectioncount) {
857 s->sectionstart = AF_get_pos(a);
858 char buf[300];
859 size_t i = 0;
860 for(; i < s->sectioncount; i++) {
861 if(!AF_read_string(a, buf, sizeof(buf))) return 0;
862 AF_read_int(a); /* section offset */
866 if(0xbeefcafe != AF_read_uint(a)) return 0;
867 s->len = AF_get_pos(a) - s->start;
868 return 1;