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