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