separated vm-dcpu16.c cli driver wrapper from vm emulator core
[dcpu16] / as-dcpu16.c
1 #include <stdlib.h>
2 #include <unistd.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include <errno.h>
6 #include <sysexits.h>
7 #include <assert.h>
8
9 #include "as-dcpu16.h"
10
11 /*
12 * quick and dirty assembler for dcpu16
13 *
14 * Justin Wind <justin.wind@gmail.com>
15 * 2012 04 07 - implementation started
16 * 2012 04 10 - functional
17 *
18 * TODO
19 * needs ability to specify location for code or data
20 */
21
22 static const char * const src_id_ = "$Id$";
23
24 const char const out_filename_default_[] = "a.out";
25 unsigned int verbose_ = 0;
26 unsigned int dryrun_ = 0;
27
28
29 #define DEBUG_PRINTF(...) do { if (verbose_ > 2) printf(__VA_ARGS__); } while (0)
30 #define VERBOSE_PRINTF(...) do { if (verbose_) printf(__VA_ARGS__); } while (0)
31
32
33 static
34 void usage_(char *prog, unsigned int full) {
35 FILE *f = full ? stdout : stderr;
36 char *x = strrchr(prog, '/');
37
38 if (x && *(x + 1))
39 prog = x + 1;
40
41 if (full)
42 fprintf(f, "%s -- \n\n",
43 prog);
44
45 fprintf(f, "Usage: %s [-h] [-v] [-s] [-o file] file [file [...]]\n",
46 prog);
47
48 if (full) {
49 fprintf(f, "\nOptions:\n"
50 "\t-h -- this screen\n"
51 "\t-o <file> -- output to <file> [default: %s]\n"
52 "\t-s -- allow short labels in instruction words\n"
53 "\t-d -- dry run, print results, do not write to file\n"
54 "\t-v -- verbose output\n",
55 out_filename_default_);
56
57 fprintf(f, "\n%78s\n",
58 src_id_);
59 }
60 }
61
62
63 /* maintain an array of the instructions we have parsed */
64 static
65 struct instruction_list_ *instr_list_new(void) {
66 size_t init_size = 1024;
67 struct instruction_list_ *il = malloc(IL_SIZE(init_size));
68 if (il == NULL) {
69 fprintf(stderr, "%s():%s\n", "malloc", strerror(errno));
70 return NULL;
71 }
72 il->allocated = init_size;
73 il->entries = 0;
74 return il;
75 }
76
77 static
78 int instr_list_insert(struct instruction_list_ **il, struct instruction_ *i) {
79 /* make room make room */
80 if ((*il)->entries - 1 == (*il)->allocated) {
81 size_t new_allocated = (*il)->allocated + 1024;
82 void *tmp_ptr = realloc(*il, IL_SIZE(new_allocated));
83 if (tmp_ptr == NULL) {
84 fprintf(stderr, "%s():%s\n", "realloc", strerror(errno));
85 return -1;
86 }
87 *il = tmp_ptr;
88 (*il)->allocated = new_allocated;
89 }
90
91 (*il)->instr[(*il)->entries] = i;
92 (*il)->entries += 1;
93 return 0;
94 }
95
96 /* also maintain a list of the labels we've seen, indexed back to their instructions. */
97 /* FIXME: ugh, this could all stand to be rewritten cleaner */
98 /* these lists could be rearranged to be a lot easier to wrangle and/or maybe use common interfaces */
99 /* they were thrown together on the fly */
100 static
101 struct label_list_ *label_list_new(void) {
102 size_t init_size = 256;
103 struct label_list_ *ll = malloc(LL_SIZE(init_size));
104 if (ll == NULL) {
105 fprintf(stderr, "%s():%s\n", "malloc", strerror(errno));
106 return NULL;
107 }
108 ll->allocated = init_size;
109 ll->entries = 0;
110 return ll;
111 }
112
113 /* instr here is index into instruction list */
114 static
115 int label_list_insert(struct label_list_ **ll, struct instruction_ **instr) {
116 if ((*ll)->entries - 1 == (*ll)->allocated) {
117 size_t new_allocated = (*ll)->allocated + 256;
118 void *tmp_ptr = realloc(*ll, IL_SIZE(new_allocated));
119 if (tmp_ptr == NULL) {
120 fprintf(stderr, "%s():%s\n", "realloc", strerror(errno));
121 return -1;
122 }
123 *ll = tmp_ptr;
124 (*ll)->allocated = new_allocated;
125 }
126
127 DEBUG_PRINTF("TRACE: adding label '%s'\n", (*instr)->label);
128
129 (*ll)->label[(*ll)->entries].label = (*instr)->label;
130 (*ll)->label[(*ll)->entries].instr = instr;
131 (*ll)->entries += 1;
132 return 0;
133 }
134
135 /* locate the index of a labelled instruction within the instruction list */
136 static
137 struct instruction_ **label_list_find_instr(struct label_list_ *ll, char *label) {
138 size_t x;
139
140 for (x = 0; x < ll->entries; x++) {
141 if (strcmp(ll->label[x].label, label) == 0)
142 return ll->label[x].instr;
143 }
144 return NULL;
145 }
146
147 /* look up the address of a calculated address */
148 static
149 int label_list_find_addr(struct label_list_ *ll, char *label, DCPU16_WORD *addr) {
150 size_t x;
151
152 for (x = 0; x < ll->entries; x++) {
153 if (strcmp(ll->label[x].label, label) == 0) {
154 if (ll->label[x].ready == 1) {
155 *addr = ll->label[x].addr;
156 return 0;
157 }
158 }
159 }
160 return -1;
161 }
162
163 /* attempt to determine the addresses of labels */
164 static
165 void label_addr_calculate_(struct instruction_list_ *il, struct label_list_ *ll) {
166 size_t i;
167
168 /* walk through labels */
169 for (i = 0; i < ll->entries; i++) {
170 struct instruction_ **instr;
171 unsigned int word_count = 0;
172
173 if (ll->label[i].ready)
174 continue;
175
176 /*
177 * walk backwards through the list of instructions
178 * until we get to the start or a known prior label address
179 * update our label
180 */
181 for (instr = ll->label[i].instr; instr >= il->instr; instr--) {
182
183 word_count += (*instr)->length;
184
185 if ((*instr)->label
186 && strcmp((*instr)->label, ll->label[i].label)) {
187 DCPU16_WORD addr;
188
189 if (label_list_find_addr(ll, (*instr)->label, &addr)) {
190 fprintf(stderr, "internal error: incomplete prior address for '%s' while calculating '%s'\n",
191 (*instr)->label,
192 ll->label[i].label);
193 continue;
194 }
195 word_count += addr;
196 break;
197 }
198 }
199 ll->label[i].addr = word_count;
200 ll->label[i].ready = 1;
201 DEBUG_PRINTF("label '%s' has addr of 0x%04x\n", ll->label[i].label, word_count);
202 }
203 }
204
205 static
206 void instr_free_(struct instruction_ *i) {
207 if (i->label)
208 free(i->label);
209 if (i->opcode)
210 free(i->opcode);
211 while (i->operands) {
212 struct operand_ *o = i->operands;
213
214 i->operands = o->next;
215 free(o);
216 }
217
218 free(i);
219 }
220
221 /* generate the nibble for a given basic opcode */
222 static
223 int opcode_bits_(char *opcode) {
224 static struct {
225 char op[4];
226 char value;
227 } opcodes_lower_nibble[] = {
228 { "JSR", 0x00 },
229 /* { "future nbi instruction", 0x00 }, */
230 { "SET", 0x01 },
231 { "ADD", 0x02 },
232 { "SUB", 0x03 },
233 { "MUL", 0x04 },
234 { "DIV", 0x05 },
235 { "MOD", 0x06 },
236 { "SHL", 0x07 },
237 { "SHR", 0x08 },
238 { "AND", 0x09 },
239 { "BOR", 0x0a },
240 { "XOR", 0x0b },
241 { "IFE", 0x0c },
242 { "IFN", 0x0d },
243 { "IFG", 0x0e },
244 { "IFB", 0x0f },
245 { "", 0x00 }
246 }, *o;
247
248 for (o = opcodes_lower_nibble; o->op[0]; o++) {
249 if (strcasecmp(o->op, opcode) == 0)
250 break;
251 }
252
253 if (o->op[0] == '\0') {
254 fprintf(stderr, "unknown instruction '%s'\n", opcode);
255 return -1;
256 }
257
258 return o->value;
259 }
260
261 /* generate the six bits for a given nbi opcode (aka first operand to opcode 0x00) */
262 static
263 int nbi_opcode_bits_(char *nbi_opcode) {
264 static struct {
265 char op[4];
266 char value;
267 } nbi_opcodes_bits[] = {
268 { " ", 0x00 }, /* reserved for future */
269 { "JSR", 0x01 },
270 { "", 0x00 }
271 }, *o;
272
273 for (o = nbi_opcodes_bits; o->op[0]; o++) {
274 if (strcasecmp(o->op, nbi_opcode) == 0)
275 break;
276 }
277
278 if (o->op[0] == '\0') {
279 fprintf(stderr, "unknown nbi instruction '%s'\n", o->op);
280 return -1;
281 }
282
283 return o->value;
284 }
285
286 /* convert register character like 'x' to value like 0x03 */
287 static inline
288 unsigned int register_enumerate_(char r) {
289 const char regs[] = "AaBbCcXxYyZzIiJj";
290 const char *x = strchr(regs, r);
291
292 if (x)
293 return (x - regs)/2;
294
295 fprintf(stderr, "internal error, unknown register character 0x%02x\n", r);
296 return -1;
297 }
298
299 /* removes all occurences of chars from buf */
300 static inline
301 void buf_strip_chars_(char *buf, char *chars) {
302 char *s, *d;
303
304 for (s = d = buf; *s; s++, d++) {
305 while (*s && strchr(chars, *s)) {
306 s++;
307 }
308 if (!*s)
309 break;
310 *d = *s;
311 }
312 *d = *s;
313 }
314
315 /* value_bits_
316 * generate the six bits for a given operand string
317 * returns -1 if it could not parse the operand
318 * returns -2 if it could not parse the operand due to an unresolved label
319 * notes: nextword may be overwritten even if it's not used in final instruction
320 */
321 static
322 int value_bits_(struct label_list_ *ll, char *operand_orig, DCPU16_WORD *nextword, unsigned int *nextwordused, unsigned int allow_short_labels) {
323 static char *operand = NULL;
324 static size_t operand_sz = 0;
325
326 unsigned long l;
327 char *o, *ep;
328
329 /*
330 Our operand working buffer shouldn't ever need to be too big,
331 but DAT might blow that assumption.
332 */
333 if (operand_sz <= strlen(operand_orig)) {
334 void *tmp_ptr;
335 size_t new_sz = strlen(operand_orig);
336
337 if (new_sz < 256)
338 new_sz = 256;
339 new_sz += 256;
340
341 DEBUG_PRINTF("%s: allocating buffer of size %zu\n", __func__, new_sz);
342 tmp_ptr = realloc(operand, new_sz);
343 if (tmp_ptr == NULL) {
344 fprintf(stderr, "%s(%zu):%s\n", "realloc", new_sz, strerror(errno));
345 return -1;
346 }
347 operand = tmp_ptr;
348 operand_sz = new_sz;
349 }
350
351 o = strcpy(operand, operand_orig);
352
353 DEBUG_PRINTF("%s: operand '%s' ", __func__, operand); /* completed later */
354
355 /* this is a very stupid parser */
356
357 /* first, let's trim all whitespace out of string at once to make parsing easier */
358 buf_strip_chars_(operand, " \t\n");
359
360 /* single character might match a register */
361 if (strlen(operand) == 1
362 && strchr("AaBbCcXxYyZzIiJj", *operand)) {
363 DEBUG_PRINTF("is register %c\n", *operand);
364 return register_enumerate_(*operand);
365 }
366
367 /* easy matches */
368 if (strcasecmp(operand, "POP") == 0) {
369 DEBUG_PRINTF("is POP\n");
370 return 0x18;
371 }
372 if (strcasecmp(operand, "PUSH") == 0) {
373 DEBUG_PRINTF("is PUSH\n");
374 return 0x19;
375 }
376 if (strcasecmp(operand, "PEEK") == 0) {
377 DEBUG_PRINTF("is PEEK\n");
378 return 0x1a;
379 }
380 if (strcasecmp(operand, "SP") == 0) {
381 DEBUG_PRINTF("is register SP\n");
382 return 0x1b;
383 }
384 if (strcasecmp(operand, "PC") == 0) {
385 DEBUG_PRINTF("is register PC\n");
386 return 0x1c;
387 }
388 if (strcasecmp(operand, "O") == 0) {
389 DEBUG_PRINTF("is register O\n");
390 return 0x1d;
391 }
392
393 /* is the operand [bracketed]? */
394 if (operand[0] == '[' && operand[strlen(operand) - 1] == ']') {
395 /* eat the brackets */
396 operand[strlen(operand) - 1] = '\0';
397 operand++;
398
399 /* is it [register]? */
400 if (strlen(operand) == 1
401 && strchr("AaBbCcXxYyZzIiJj", *operand)) {
402 DEBUG_PRINTF("is dereferenced register %c\n", *operand);
403 return 0x08 | register_enumerate_(*operand);
404 }
405
406 /* is it [register+something]? */
407 if ( (ep = strchr(operand, '+')) ) {
408 char *reg;
409 char *constant;
410
411 /* eat the plus */
412 *ep = '\0';
413 ep++;
414
415 /* figure out which one is which */
416 if (strlen(ep) == 1
417 && strchr("AaBbCcXxYyZzIiJj", *ep)) {
418 reg = ep;
419 constant = operand;
420 } else if (strlen(operand) == 1
421 && strchr("AaBbCcXxYyZzIiJj", *operand) ) {
422 reg = operand;
423 constant = ep;
424 } else {
425 DEBUG_PRINTF("is unparsable\n");
426 fprintf(stderr, "couldn't parse operand '%s'\n", operand_orig);
427 return -1;
428 }
429
430 /* check if something is understandable as a value */
431 errno = 0;
432 l = strtoul(constant, &ep, 0);
433 if (errno == 0
434 && (*constant && (*ep == '\0')) ) {
435 /* string conversion went without issue */
436 /* validate it will fit in a word */
437 if (l > 0xffff) {
438 DEBUG_PRINTF("is out of range\n");
439 fprintf(stderr, "constant invalid in operand '%s'\n", operand_orig);
440 return -1;
441 }
442
443 /* seems fine */
444 *nextword = l & 0xffff;
445 *nextwordused += 1;
446 DEBUG_PRINTF("is a dereferenced register (%c) + constant (%hu)\n", *reg, *nextword);
447 return 0x10 | register_enumerate_(*reg);
448 } else if (errno) {
449 DEBUG_PRINTF("is out of range\n");
450 fprintf(stderr, "trouble with operand '%s': %s\n", operand_orig, strerror(errno));
451 return -1;
452 }
453
454 /* what? still here? assume it's a label, I guess */
455 /* try to populate nextword with label address */
456 if (label_list_find_addr(ll, operand, nextword)) {
457 DEBUG_PRINTF("(deferred label resolution)\n");
458 *nextwordused += 1;
459 return -2;
460 }
461 DEBUG_PRINTF("is a dereferenced register (%c) + label\n", *reg);
462 *nextwordused += 1;
463 return 0x10 | register_enumerate_(*reg);
464 }
465
466 /* it must just be a dereferenced literal then */
467
468 errno = 0;
469 l = strtoul(operand, &ep, 0);
470 if (errno == 0
471 && (*operand && (*ep == '\0')) ) {
472 /* string conversion went without issue */
473 /* validate it will fit in a word */
474 if (l > 0xffff) {
475 DEBUG_PRINTF("is out of range\n");
476 fprintf(stderr, "constant invalid in operand '%s'\n", operand_orig);
477 return -1;
478 }
479
480 DEBUG_PRINTF("is a dereferenced literal value (%hu)\n", *nextword);
481 *nextword = l & 0xffff;
482 *nextwordused += 1;
483 return 0x1e;
484 } else if (errno) {
485 DEBUG_PRINTF("is out of range\n");
486 fprintf(stderr, "trouble with operand '%s': %s\n", operand_orig, strerror(errno));
487 }
488
489 /* not a number? try a label */
490 if (label_list_find_addr(ll, operand, nextword)) {
491 DEBUG_PRINTF("(deferred label resolution)\n");
492 *nextwordused += 1;
493 return -2;
494 }
495 DEBUG_PRINTF("is a dereferenced label\n");
496 *nextwordused += 1;
497 return 0x1e;
498 }
499
500 /* left with a literal or a label, then */
501
502 errno = 0;
503 l = strtoul(operand, &ep, 0);
504 if (errno == 0
505 || (*operand && (*ep == '\0')) ) {
506 if (l > 0xffff) {
507 DEBUG_PRINTF("is out of range\n");
508 fprintf(stderr, "constant invalid in operand '%s'\n", operand_orig);
509 return -1;
510 }
511
512 DEBUG_PRINTF("is literal value (%lu)\n", l);
513 if (l < 0x20) {
514 return l + 0x20;
515 }
516
517 *nextword = l & 0xffff;
518 *nextwordused += 1;
519 return 0x1f;
520 }
521
522 /* try to populate nextword with label address */
523 if (label_list_find_addr(ll, operand, nextword)) {
524 DEBUG_PRINTF("(deferred label resolution)\n");
525 /* assume non-small literal value */
526 *nextwordused += 1;
527 return -2;
528 }
529
530 DEBUG_PRINTF("is label '%s' (0x%02hx)\n", operand, *nextword);
531 if (*nextword < 0x20 && allow_short_labels) {
532 DEBUG_PRINTF("small value label win\n");
533 return (0x20 + *nextword) & 0x3f;
534 }
535
536 *nextwordused += 1;
537 return 0x1f;
538 }
539
540 static inline
541 int instruction_print_(struct instruction_ *i, unsigned int with_label) {
542 struct operand_ *o;
543 int r;
544
545 if (with_label)
546 r = printf("%-16s %3s", i->label ? i->label : "", i->opcode);
547 else
548 r = printf("%3s", i->opcode);
549
550 for (o = i->operands; o; o = o->next)
551 r += printf(" %s%s", o->operand, o->next ? "," : "");
552
553 return r;
554 }
555
556 /* parse an instruction out of buf, create new instruction struct if seemingly valid */
557 /* does not actually check if instruction is valid yet */
558 /* buf must be 0-terminated */
559 static
560 int buf_tokenize_(char *buf, struct instruction_ **next_instr) {
561 const char const *sep = " \t\n";
562 struct instruction_ *instr = NULL;
563 char *label = NULL,
564 *opcode = NULL,
565 *operand = NULL;
566
567 char *x,
568 *y,
569 *st;
570
571 assert(buf != NULL);
572 assert(next_instr != NULL);
573
574 *next_instr = NULL;
575
576 /* kill comments */
577 if ((x = strchr(buf, ';')) != NULL)
578 *x = '\0';
579 /* kill leading whitespace */
580 buf += strspn(buf, " \t\n");
581 /* kill trailing whitespace */
582 if (*buf) {
583 x = buf + strlen(buf);
584 while (strchr(" \t\n", *x)) {
585 *x = '\0';
586 x--;
587 }
588 }
589
590 if ((x = strrchr(buf, '\n')) != NULL)
591 *x = '\0';
592
593 /* determine if first token is label, opcode, or we just have a blank line to ignore */
594 x = strtok_r(buf, sep, &st);
595
596 /* empty line? nothing to do here. */
597 if (x == NULL)
598 return 0;
599
600 #ifdef OTHER_LABELS
601 /* labels end with :, otherwise its an opcode */
602 y = x + strlen(x) - 1;
603 if (*y == ':') {
604 *y = '\0';
605 label = x;
606 opcode = strtok_r(NULL, sep, &st);
607 }
608 #else /* OTHER_LABELS */
609 /* labels.. begin? with ':' ? okay, I guess. Whatever. */
610 /* otherwise, it's an opcode */
611 if (*x == ':') {
612 label = x + 1;
613 opcode = strtok_r(NULL, sep, &st);
614 } else {
615 label = NULL;
616 opcode = x;
617 }
618 #endif /* OTHER_LABELS */
619
620 if (opcode) {
621 operand = st;
622 }
623
624 /* extra room for assembled words */
625 instr = calloc(1, 3 + sizeof *instr);
626 if (instr == NULL) {
627 fprintf(stderr, "%s():%s\n", "calloc", strerror(errno));
628 return -1;
629 }
630
631 instr->label = label ? strdup(label) : NULL;
632 instr->opcode = opcode ? strdup(opcode) : NULL;
633
634 if (operand) {
635 struct operand_ **o_next = &instr->operands;
636
637 for (x = strtok_r(operand, ",", &st);
638 x;
639 x = strtok_r(NULL, ",", &st) ) {
640 *o_next = malloc(3 + sizeof **o_next); /* FIXME: handle this on the fly later */
641
642 if (*o_next == NULL) {
643 fprintf(stderr, "%s():%s\n", "calloc", strerror(errno));
644 instr_free_(instr);
645 return -1;
646 }
647
648 /* trim */
649 x += strspn(x, " \t\n");
650 if (*x) {
651 y = x + strlen(x) - 1;
652 while (strchr(" \t\n", *y)) {
653 *y = '\0';
654 y--;
655 }
656 }
657
658 (*o_next)->operand = strdup(x);
659 (*o_next)->next = NULL;
660 o_next = &((*o_next)->next);
661 }
662 }
663
664 *next_instr = instr;
665
666 return 0;
667 }
668
669 /* try to generate bytecode for an instruction */
670 static
671 int instr_assemble_(struct label_list_ *ll, struct instruction_ *i, unsigned int allow_short_labels) {
672 unsigned int nwu = 0; /* number of words used */
673 unsigned int incomplete = 0;
674 int bits;
675 struct operand_ *o = i->operands;
676
677 if (verbose_ > 2) {
678 printf("%s: assembling ", __func__);
679 instruction_print_(i,1);
680 printf("\n");
681 }
682
683 if (i->ready) {
684 /* already assembled, nothing to do */
685 return 0;
686 }
687
688 /* special case DAT */
689 if (strncasecmp(i->opcode, "DAT", 3) == 0) {
690 /* just dump operands into words, I guess */
691 fprintf(stderr, "FIXME unhandled raw data\n");
692 /* count total length of data.. */
693 /* realloc instruction */
694 /* populate words */
695 return 0;
696 }
697
698 /* start with opcode bits */
699 bits = opcode_bits_(i->opcode);
700 if (bits < 0) {
701 fprintf(stderr, "unrecognized instruction '%s%s", i->opcode, i->operands ? " " : "");
702 for (o = i->operands; o; o = o->next)
703 fprintf(stderr, " %s%s", o->operand, o->next ? "," : "");
704 fprintf(stderr, "'\n");
705 return -1;
706 }
707 i->instr_words[0] |= 0x0f & bits;
708
709 /* in rendered bytecode, all instructions have two operands; nbi instructions take 'first operand' bits. */
710 if ((bits & 0x0f) == 0) {
711 bits = nbi_opcode_bits_(i->opcode);
712 if (bits < 0) {
713 fprintf(stderr, "INTERNAL ERROR: missing instruction in nbi opcode table\n");
714 exit(EX_SOFTWARE);
715 }
716 } else {
717 if (o == NULL) {
718 fprintf(stderr, "'%s' requires more operands\n", i->opcode);
719 return -1;
720 }
721 bits = value_bits_(ll, o->operand, i->instr_words + 1, &nwu, allow_short_labels);
722 if (bits == -1) {
723 fprintf(stderr, "couldn't assemble instruction\n");
724 return -1;
725 } else if (bits == -2) {
726 DEBUG_PRINTF("%s: assembly deferred: unresolved label\n", __func__);
727 /* keep going, but don't finalize until we can calculate label address */
728 incomplete = 1;
729 bits = 0;
730 }
731 o = o->next;
732 }
733 i->instr_words[0] |= (bits & 0x3f) << 4;
734
735 if (o == NULL) {
736 fprintf(stderr, "'%s' requires more operands\n", i->opcode);
737 return -1;
738 }
739
740 bits = value_bits_(ll, o->operand, i->instr_words + nwu + 1, &nwu, allow_short_labels);
741 if (bits == -1) {
742 fprintf(stderr, "couldn't assemble instruction\n");
743 return -1;
744 } else if (bits == -2) {
745 DEBUG_PRINTF("%s: assembly deferred: unresolved label\n", __func__);
746 /* keep going, but don't finalize until we can calculate label address */
747 incomplete = 1;
748 bits = 0;
749 }
750 o = o->next;
751 i->instr_words[0] |= (bits & 0x3f) << 10;
752
753 if (o != NULL) {
754 fprintf(stderr, "too many operands\n");
755 return -1;
756 }
757
758 /* counting labels as words, we now know at least the maximum instruction length */
759
760 i->length = nwu + 1;
761
762 DEBUG_PRINTF("instruction words: [%u]", i->length);
763 for (bits = 0; bits <= (int)nwu; bits++)
764 DEBUG_PRINTF(" %04x", i->instr_words[bits]);
765
766 if (incomplete) {
767 DEBUG_PRINTF(" (preliminary)");
768 } else {
769 i->ready = 1;
770 }
771
772 DEBUG_PRINTF("\n");
773
774 return 0;
775 }
776
777 /* parse_stream_
778 * read lines from stream f
779 * break each line into parts, populate parts into structures
780 */
781 static
782 int parse_stream_(FILE *f, const char *src, struct instruction_list_ **il, struct label_list_ **ll, unsigned int allow_short_labels) {
783 struct instruction_ *instr, **instr_list_entry;
784 unsigned int line = 0;
785 int retval = 0;
786 char buf[0x4000];
787
788 buf[sizeof buf - 1] = '\0';
789
790 while (fgets(buf, sizeof buf, f)) {
791 line++;
792
793 if (buf[sizeof buf - 1] != '\0') {
794 fprintf(stderr, "%s:%u:%s", src, line, "input line too long\n");
795 retval = -1;
796 break;
797 }
798
799 if (buf_tokenize_(buf, &instr)) {
800 fprintf(stderr, "%s:%u:%s", src, line, "trouble tokenizing input\n");
801 retval = -1;
802 break;
803 }
804
805 if (instr) {
806 /* add to list of instructions */
807 if (instr_list_insert(il, instr)) {
808 fprintf(stderr, "%s:%u:%s", src, line, "could not populate instruction list\n");
809 }
810 instr_list_entry = (*il)->instr + (*il)->entries - 1;
811
812 if (instr->label) {
813 if (label_list_find_instr(*ll, instr->label)) {
814 fprintf(stderr, "%s:%u:%s", src, line, "duplicate label\n");
815 break;
816 }
817 if (label_list_insert(ll, instr_list_entry)) {
818 fprintf(stderr, "%s:%u:%s", src, line, "could not populate label list\n");
819 }
820 label_addr_calculate_(*il, *ll);
821 }
822
823 instr_assemble_(*ll, instr, allow_short_labels);
824 }
825 }
826 if (ferror(f)) {
827 fprintf(stderr, "%s():%s\n", "fgets", strerror(errno));
828 return -1;
829 }
830 if (! feof(f)) {
831 fprintf(stderr, "parsing aborted\n");
832 return -1;
833 }
834
835 return retval;
836 }
837
838 /* assemble_check_
839 * make a full pass over instruction list to resolve labels
840 */
841 static
842 int assemble_check_(struct instruction_list_ *il, struct label_list_ *ll, unsigned int allow_short_labels) {
843 int retval = 0;
844 size_t x;
845
846 DEBUG_PRINTF(" final pass of assembler...\n");
847 for (x = 0; x < il->entries; x++) {
848 retval |= instr_assemble_(ll, il->instr[x], allow_short_labels);
849 if (retval) {
850 fprintf(stderr, "instruction failed to assemble\n");
851 }
852 }
853
854 VERBOSE_PRINTF("%3s %6s %-32s %-4s\n", "", "_addr_", "_label_", "_instruction_");
855 for (x = 0; x < ll->entries; x++) {
856 if (! ll->label[x].ready)
857 retval |= -1;
858 if (verbose_) {
859 printf("%3s0x%04x %-32s ",
860 ll->label[x].ready ? "" : "*",
861 ll->label[x].addr,
862 ll->label[x].label);
863 instruction_print_(*(ll->label[x].instr), 0);
864 printf("\n");
865 }
866 }
867
868 VERBOSE_PRINTF("\n");
869
870 if (retval)
871 fprintf(stderr, "some labels could not be resolved\n");
872
873 return retval;
874 }
875
876 static
877 int output_(struct instruction_list_ *il, const char *filename) {
878 FILE *of = NULL;
879 struct instruction_ *instr;
880 size_t i, r, total_words = 0;
881 size_t x;
882
883 if (! dryrun_) {
884 of = fopen(filename, "w");
885 if (of == NULL) {
886 fprintf(stderr, "%s('%s'):%s\n", "fopen", filename, strerror(errno));
887 return -1;
888 }
889 }
890
891 for (i = 0; i < il->entries; i++) {
892 instr = il->instr[i];
893
894 if (verbose_) {
895 int s;
896 s = instruction_print_(instr, 1);
897 printf("%*s;", (44 - s) > 0 ? (44 - s) : 0, "");
898 for (x = 0; x < instr->length; x++) {
899 printf(" %04x", instr->instr_words[x]);
900 }
901 printf("\n");
902 }
903
904 if (of) {
905 r = fwrite(instr->instr_words, sizeof(DCPU16_WORD), instr->length, of);
906 if (r < instr->length) {
907 fprintf(stderr, "%s():%s\n", "fwrite", strerror(errno));
908 return -1;
909 }
910 }
911 total_words += instr->length;
912 }
913
914 fprintf(stderr, "%s 0x%04zx instructions as 0x%04zx words\n",
915 dryrun_ ? "assembled" : "wrote",
916 i,
917 total_words);
918
919 return 0;
920 }
921
922 static struct instruction_list_ *il_;
923 static struct label_list_ *ll_;
924
925 int main(int argc, char *argv[]) {
926 const char *out_filename = NULL;
927 unsigned int allow_short_labels = 0;
928 int c;
929
930 while ( (c = getopt(argc, argv, "dhsvo:")) != EOF ) {
931 switch (c) {
932 case 'd':
933 dryrun_++;
934 break;
935
936 case 's':
937 allow_short_labels++;
938 break;
939
940 case 'o':
941 if (out_filename) {
942 fprintf(stderr, "Sorry, I can only write one file at a time.\n");
943 exit(EX_CANTCREAT);
944 }
945 out_filename = optarg;
946 break;
947
948 case 'v':
949 verbose_++;
950 break;
951
952 case 'h':
953 usage_(argv[0], 1);
954 exit(EX_OK);
955
956 default:
957 usage_(argv[0], 0);
958 exit(EX_USAGE);
959 }
960 }
961
962 argc -= optind;
963 argv += optind;
964
965 if (out_filename == NULL)
966 out_filename = out_filename_default_;
967
968 /* init tables */
969 il_ = instr_list_new();
970 ll_ = label_list_new();
971
972 /* if filenames were specified, parse them instead of stdin */
973 if (argc) {
974 while (argc) {
975 char *filename = *argv;
976 FILE *f = fopen(filename, "r");
977
978 argc--, argv++;
979
980 if (f == NULL) {
981 fprintf(stderr, "%s('%s'):%s\n", "fopen", filename, strerror(errno));
982 continue;
983 }
984
985 VERBOSE_PRINTF("assembling '%s'...\n", filename);
986 parse_stream_(f, filename, &il_, &ll_, allow_short_labels);
987
988 fclose(f);
989 }
990 } else {
991 VERBOSE_PRINTF("assembling '%s'...\n", "stdin");
992 parse_stream_(stdin, "-", &il_, &ll_, allow_short_labels);
993 }
994
995 if (assemble_check_(il_, ll_, allow_short_labels)) {
996 fprintf(stderr, "errors prevented assembly\n");
997 exit(EX_DATAERR);
998 }
999
1000 if (output_(il_, out_filename)) {
1001 fprintf(stderr, "failed to create output\n");
1002 exit(EX_OSERR);
1003 }
1004
1005 exit(EX_OK);
1006 }