13 * quick and dirty assembler for dcpu16
15 * Justin Wind <justin.wind@gmail.com>
16 * 2012 04 07 - implementation started
17 * 2012 04 10 - functional
18 * 2012 04 16 - support dat statements
21 * needs ability to specify location for code or data
22 * short labels not correctly computed
25 static const char * const src_id_
= "$Id$";
27 const char const out_filename_default_
[] = "a.out";
29 /* global invocation options */
38 #define DEBUG_PRINTF(...) do { if (opt_.verbose > 2) { printf("DEBUG: "); printf(__VA_ARGS__); } } while (0)
39 #define DEBUG_PRINTFQ(...) do { if (opt_.verbose > 2) printf(__VA_ARGS__); } while (0)
40 #define VERBOSE_PRINTF(...) do { if (opt_.verbose) printf(__VA_ARGS__); } while (0)
43 void usage_(char *prog
, unsigned int full
) {
44 FILE *f
= full
? stdout
: stderr
;
45 char *x
= strrchr(prog
, '/');
51 fprintf(f
, "%s -- \n\n",
54 fprintf(f
, "Usage: %s [-h] [-v] [-s] [-o file] file [file [...]]\n",
58 fprintf(f
, "\nOptions:\n"
59 "\t-h -- this screen\n"
60 "\t-o <file> -- output to <file> [default: %s]\n"
61 "\t-s -- allow short labels in instruction words\n"
62 "\t-d -- dry run, print results, do not write to file\n"
63 "\t-v -- verbose output\n",
64 out_filename_default_
);
66 fprintf(f
, "\n%78s\n",
72 /* instructions have operands */
74 struct operand_
*next
;
75 char *operand
; /* tokenized operand text */
78 /* keep an array of instructions as we read them in */
81 char *label
; /* set if a label points here */
82 char *opcode
; /* tokenized instruction text */
83 struct operand_
*operands
; /* list of operands */
84 unsigned int ready
: 1; /* bytecode computed? */
85 unsigned int length
; /* number of words of bytecode */
86 DCPU16_WORD instr_words
[];
89 /* keep an array of labels, indexed back to their instruction locations */
91 char *label
; /* name of label */
92 struct instruction_
**instr
; /* pointer into array of instructions */
93 unsigned int ready
: 1; /* do we know where this label is yet? */
98 /* locate and return the label entry matching name */
100 struct label_
*label_find_(struct dynamic_array
*labels
, char *name
) {
103 for (x
= 0; x
< labels
->entries
; x
++) {
104 struct label_
*l
= (struct label_
*)DYNARRAY_ITEM(*labels
, x
);
105 if (strcmp(l
->label
, name
) == 0)
112 /* if a label has a validly-calculated address, fetch it */
114 int label_addr_(struct dynamic_array
*labels
, char *name
, DCPU16_WORD
*addr
) {
117 if ( (l
= label_find_(labels
, name
)) == NULL
)
126 /* attempt to determine the addresses of all labels */
128 void label_addr_calculate_(struct dynamic_array
*instructionps
, struct dynamic_array
*labels
) {
131 /* for each label.. */
132 for (i
= 0; i
< labels
->entries
; i
++) {
134 struct instruction_
**instr
;
135 unsigned int word_count
= 0;
137 l
= (struct label_
*)DYNARRAY_ITEM(*labels
, i
);
139 /* if it's already calculated, great. */
144 * starting at the instruction for this label,
145 * walk backwards through the list of instructions
146 * until we get to the start or a known prior label address.
147 * update our label with the freshly calculated addr
149 for (instr
= ((struct label_
*)DYNARRAY_ITEM(*labels
, i
))->instr
;
150 instr
>= (struct instruction_
**)DYNARRAY_ITEM(*instructionps
, 0);
154 DEBUG_PRINTF("%s: instr not ready\n", __func__
);
155 word_count
+= (*instr
)->length
;
157 /* have we come across an instruction which a label points to?
158 it should already be calculated, so just add that on and be done */
160 && strcmp((*instr
)->label
, l
->label
)) {
163 if (label_addr_(labels
, (*instr
)->label
, &addr
)) {
164 fprintf(stderr
, "internal error: incomplete prior address for '%s' while calculating '%s'\n",
174 l
->addr
= word_count
;
176 DEBUG_PRINTF("label '%s' now has addr of 0x%04x\n", l
->label
, word_count
);
181 /* generate the nibble for a given basic opcode */
183 int opcode_bits_(char *opcode
) {
187 } opcodes_lower_nibble
[] = {
189 /* { "future nbi instruction", 0x00 }, */
208 for (o
= opcodes_lower_nibble
; o
->op
[0]; o
++) {
209 if (strcasecmp(o
->op
, opcode
) == 0)
213 if (o
->op
[0] == '\0') {
214 fprintf(stderr
, "unknown instruction '%s'\n", opcode
);
221 /* generate the six bits for a given nbi opcode (aka first operand to opcode 0x00) */
223 int nbi_opcode_bits_(char *nbi_opcode
) {
227 } nbi_opcodes_bits
[] = {
228 { " ", 0x00 }, /* reserved for future */
233 for (o
= nbi_opcodes_bits
; o
->op
[0]; o
++) {
234 if (strcasecmp(o
->op
, nbi_opcode
) == 0)
238 if (o
->op
[0] == '\0') {
239 fprintf(stderr
, "unknown nbi instruction '%s'\n", o
->op
);
246 /* convert register character like 'x' to value like 0x03 */
248 unsigned int register_enumerate_(char r
) {
249 const char regs
[] = "AaBbCcXxYyZzIiJj";
250 const char *x
= strchr(regs
, r
);
255 fprintf(stderr
, "internal error, unknown register character 0x%02x\n", r
);
259 /* removes all occurences of chars from buf */
261 void buf_strip_chars_(char *buf
, char *chars
) {
264 for (s
= d
= buf
; *s
; s
++, d
++) {
265 while (*s
&& strchr(chars
, *s
)) {
277 * generate the six bits for a given operand string
278 * returns -1 if it could not parse the operand
279 * returns -2 if it could not parse the operand due to an unresolved label
280 * notes: nextword may be overwritten even if it's not used in final instruction
283 int value_bits_(struct dynamic_array
*labels
, const char *operand_orig
, DCPU16_WORD
*nextword
, unsigned int *nextwordused
, unsigned int allow_short_labels
) {
284 static char *operand
= NULL
;
285 static size_t operand_sz
= 0;
291 Our operand working buffer shouldn't ever need to be too big,
292 but DAT might blow that assumption.
294 if (operand_sz
<= strlen(operand_orig
)) {
296 size_t new_sz
= strlen(operand_orig
);
302 DEBUG_PRINTF("%s: allocating buffer of size %zu\n", __func__
, new_sz
);
303 tmp_ptr
= realloc(operand
, new_sz
);
304 if (tmp_ptr
== NULL
) {
305 fprintf(stderr
, "%s(%zu):%s\n", "realloc", new_sz
, strerror(errno
));
312 o
= strcpy(operand
, operand_orig
);
314 DEBUG_PRINTF("%s: operand '%s' ", __func__
, operand
); /* completed later */
316 /* this is a very stupid parser */
318 /* first, let's trim all whitespace out of string at once to make parsing easier */
319 buf_strip_chars_(operand
, " \t\n");
321 /* single character might match a register */
322 if (strlen(operand
) == 1
323 && strchr("AaBbCcXxYyZzIiJj", *operand
)) {
324 DEBUG_PRINTFQ("is register %c\n", *operand
);
325 return register_enumerate_(*operand
);
329 if (strcasecmp(operand
, "POP") == 0) {
330 DEBUG_PRINTFQ("is POP\n");
333 if (strcasecmp(operand
, "PUSH") == 0) {
334 DEBUG_PRINTFQ("is PUSH\n");
337 if (strcasecmp(operand
, "PEEK") == 0) {
338 DEBUG_PRINTFQ("is PEEK\n");
341 if (strcasecmp(operand
, "SP") == 0) {
342 DEBUG_PRINTFQ("is register SP\n");
345 if (strcasecmp(operand
, "PC") == 0) {
346 DEBUG_PRINTFQ("is register PC\n");
349 if (strcasecmp(operand
, "O") == 0) {
350 DEBUG_PRINTFQ("is register O\n");
354 /* is the operand [bracketed]? */
355 if (operand
[0] == '[' && operand
[strlen(operand
) - 1] == ']') {
356 /* eat the brackets */
357 operand
[strlen(operand
) - 1] = '\0';
360 /* is it [register]? */
361 if (strlen(operand
) == 1
362 && strchr("AaBbCcXxYyZzIiJj", *operand
)) {
363 DEBUG_PRINTFQ("is dereferenced register %c\n", *operand
);
364 return 0x08 | register_enumerate_(*operand
);
367 /* is it [register+something]? */
368 if ( (ep
= strchr(operand
, '+')) ) {
376 /* figure out which one is which */
378 && strchr("AaBbCcXxYyZzIiJj", *ep
)) {
381 } else if (strlen(operand
) == 1
382 && strchr("AaBbCcXxYyZzIiJj", *operand
) ) {
386 DEBUG_PRINTFQ("is unparsable\n");
387 fprintf(stderr
, "couldn't parse operand '%s'\n", operand_orig
);
391 /* check if something is understandable as a value */
393 l
= strtoul(constant
, &ep
, 0);
395 && (*constant
&& (*ep
== '\0')) ) {
396 /* string conversion went without issue */
397 /* validate it will fit in a word */
399 DEBUG_PRINTFQ("is out of range\n");
400 fprintf(stderr
, "constant invalid in operand '%s'\n", operand_orig
);
405 *nextword
= l
& 0xffff;
407 DEBUG_PRINTFQ("is a dereferenced register (%c) + constant (%hu)\n", *reg
, *nextword
);
408 return 0x10 | register_enumerate_(*reg
);
410 DEBUG_PRINTFQ("is out of range\n");
411 fprintf(stderr
, "trouble with operand '%s': %s\n", operand_orig
, strerror(errno
));
415 /* what? still here? assume it's a label, I guess */
416 /* try to populate nextword with label address */
417 if (label_addr_(labels
, operand
, nextword
)) {
418 DEBUG_PRINTFQ("(deferred label resolution)\n");
422 DEBUG_PRINTFQ("is a dereferenced register (%c) + label\n", *reg
);
424 return 0x10 | register_enumerate_(*reg
);
427 /* it must just be a dereferenced literal then */
430 l
= strtoul(operand
, &ep
, 0);
432 && (*operand
&& (*ep
== '\0')) ) {
433 /* string conversion went without issue */
434 /* validate it will fit in a word */
436 DEBUG_PRINTFQ("is out of range\n");
437 fprintf(stderr
, "constant invalid in operand '%s'\n", operand_orig
);
441 DEBUG_PRINTFQ("is a dereferenced literal value (%hu)\n", *nextword
);
442 *nextword
= l
& 0xffff;
446 DEBUG_PRINTFQ("is out of range\n");
447 fprintf(stderr
, "trouble with operand '%s': %s\n", operand_orig
, strerror(errno
));
450 /* not a number? try a label */
451 if (label_addr_(labels
, operand
, nextword
)) {
452 DEBUG_PRINTFQ("(deferred label resolution)\n");
456 DEBUG_PRINTFQ("is a dereferenced label\n");
461 /* left with a literal or a label, then */
464 l
= strtoul(operand
, &ep
, 0);
466 || (*operand
&& (*ep
== '\0')) ) {
468 DEBUG_PRINTFQ("is out of range\n");
469 fprintf(stderr
, "constant invalid in operand '%s'\n", operand_orig
);
473 DEBUG_PRINTFQ("is literal value (%lu)\n", l
);
478 *nextword
= l
& 0xffff;
483 /* try to populate nextword with label address */
484 if (label_addr_(labels
, operand
, nextword
)) {
485 DEBUG_PRINTFQ("(deferred label resolution)\n");
486 /* assume non-small literal value */
491 DEBUG_PRINTFQ("is label '%s' (0x%02hx)\n", operand
, *nextword
);
492 if (*nextword
< 0x20 && allow_short_labels
) {
493 DEBUG_PRINTF("small value label win\n");
494 return (0x20 + *nextword
) & 0x3f;
501 /* prints an instruction's assembly */
503 int instruction_print_(struct instruction_
*i
, unsigned int with_label
) {
508 r
= printf("%-16s %3s", i
->label
? i
->label
: "", i
->opcode
);
510 r
= printf("%3s", i
->opcode
);
512 for (o
= i
->operands
; o
; o
= o
->next
)
513 r
+= printf(" %s%s", o
->operand
, o
->next
? "," : "");
519 * Parses the zero-terminated line of input 'buf' into a newly-allocated struct instruction_.
520 * [label] opcode [operand[,operand[,...]]]
521 * Does not yet validate if labels, opcodes, or operands are valid...
524 int buf_tokenize_(char *buf
, struct instruction_
**next_instr
) {
525 const char const *sep
= " \t\n";
526 const char const *quot
= "'`\"";
527 struct instruction_
*instr
= NULL
;
528 struct operand_
*operand_list
= NULL
;
529 struct operand_
**o_next
= &operand_list
;
535 size_t instr_words_needed
= 1;
538 assert(next_instr
!= NULL
);
542 /* kill leading whitespace */
543 buf
+= strspn(buf
, sep
);
545 /* locate first non-quoted ';', ignore anything following it */
546 x
= strqtok_r(buf
, ";", '\\', quot
, &qt
, &st
);
550 fprintf(stderr
, "unmatched %c-quote\n", *qt
);
556 /* kill trailing whitespace */
557 for (x
= buf
+ strlen(buf
) - 1; *x
&& strchr(sep
, *x
); x
--)
562 DEBUG_PRINTF("trimmed buf: '%s'\n", buf
);
564 /* determine if first token is label, opcode, or we just have a blank line to ignore */
565 x
= strqtok_r(buf
, sep
, '\\', quot
, &qt
, &st
);
566 if (x
== NULL
|| *x
== '\0')
569 fprintf(stderr
, "unmatched %c-quote '%s'\n", *qt
, qt
);
573 /* I want c-style labels in my asm, but example in spec uses : in prefix rather than postfix */
574 #ifdef NON_SPEC_LABELS
575 /* labels end with :, otherwise its an opcode */
576 y
= x
+ strlen(x
) - 1;
578 DEBUG_PRINTF("found label '%s'\n", y
);
581 opcode
= strqtok_r(NULL
, sep
, '\\', quot
, &qt
, &st
);
583 fprintf(stderr
, "unmatched %c-quote '%s'\n", *qt
, qt
);
590 #else /* NON_SPEC_LABELS */
591 /* labels.. begin? with ':' ? okay, I guess. Whatever. */
592 /* otherwise, it's an opcode */
594 DEBUG_PRINTF("found label '%s'\n", x
);
596 opcode
= strqtok_r(NULL
, sep
, '\\', quot
, &qt
, &st
);
598 fprintf(stderr
, "unmatched %c-quote '%s'\n", *qt
, qt
);
605 #endif /* NON_SPEC_LABELS */
607 if ( !label
&& (!opcode
|| !*opcode
) ) {
608 DEBUG_PRINTF("no label nor instruction?\n");
612 DEBUG_PRINTF("label:'%s' opcode:'%s' operands:'%s'\n", label
, opcode
, st
);
615 While normal instructions just have comma-separated operands,
616 DAT can be followed by comma-separated list of:
617 label, to be resolved to address
619 string, "quoted", characters to be rendered into low-byte of words
622 while ( (x
= strqtok_r(NULL
, ",", '\\', quot
, &qt
, &st
)) ) {
623 DEBUG_PRINTF("\tx:'%s' qt:'%s' st:'%s'\n", x
, qt
, st
);
626 fprintf(stderr
, "unmatched %c-quote '%s'\n", *qt
, qt
);
630 /* trim trailing whitespace */
631 y
= x
+ strlen(x
) - 1;
632 while (strchr(sep
, *y
)) {
637 /* new operand to append to list */
638 *o_next
= malloc(sizeof **o_next
);
639 if (*o_next
== NULL
) {
640 fprintf(stderr
, "%s():%s\n", "calloc", strerror(errno
));
644 /* assume an operand takes up one word, unless it's a string */
645 /* if it's a string, it comes with quotes, which will get stripped, but will include trailing zero */
646 instr_words_needed
+= (*x
== '"') ? strlen(x
) - 1 : 1;
648 (*o_next
)->operand
= strdup(x
);
649 if ((*o_next
)->operand
== NULL
) {
650 fprintf(stderr
, "%s():%s\n", "strdup", strerror(errno
));
653 (*o_next
)->next
= NULL
;
654 o_next
= &((*o_next
)->next
);
657 DEBUG_PRINTF("allocating instr with room for %zu words\n", instr_words_needed
);
659 /* extra room for assembled words */
660 instr
= calloc(1, (instr_words_needed
* sizeof *instr
->instr_words
) + sizeof *instr
);
662 fprintf(stderr
, "%s():%s\n", "calloc", strerror(errno
));
666 instr
->label
= label
? strdup(label
) : NULL
;
667 instr
->opcode
= opcode
? strdup(opcode
) : NULL
;
668 instr
->operands
= operand_list
;
675 /* try to generate bytecode for an instruction */
676 /* returns -1 on unrecoverable error */
678 int instr_assemble_(struct dynamic_array
*labels
, struct instruction_
*i
, unsigned int allow_short_labels
) {
679 unsigned int nwu
= 0; /* number of words used */
680 unsigned int incomplete
= 0;
682 struct operand_
*o
= i
->operands
;
684 if (opt_
.verbose
> 2) {
685 printf("%s: assembling %p ", __func__
, i
);
686 instruction_print_(i
, 1);
687 printf("(line :%zu)\n", i
->src_line
);
691 /* already assembled, nothing to do */
695 /* special case DAT */
696 if (strncasecmp(i
->opcode
, "DAT", 3) == 0) {
697 DEBUG_PRINTF("processing DAT...\n");
701 for ( /* */ ; o
; o
= o
->next
) {
706 DEBUG_PRINTF("DAT operand:'%s' next:%p\n", o
->operand
, o
->next
);
708 /* is this a string? */
709 if ( (x
= strchr("\"'`", o
->operand
[0])) ) {
710 dat_len
= strlen(o
->operand
) - 1;
711 if (o
->operand
[dat_len
] == *x
) {
713 DEBUG_PRINTF("DAT string operand: %s\n", o
->operand
);
715 for (j
= 0, x
= o
->operand
+ 1;
718 i
->instr_words
[i
->length
] = *x
;
721 /* Note that strings in DAT do not include their zero-terminators */
722 /* specify as 'DAT "string", 0' */
727 /* is this a number? */
730 l
= strtoul(o
->operand
, &ep
, 0);
732 && (*o
->operand
&& (*ep
== '\0')) ) {
733 /* conversion succeeded */
735 fprintf(stderr
, "value '%lu' out of range\n", l
);
738 i
->instr_words
[i
->length
] = l
;
743 /* otherwise assume it's a label, even if we don't know what it is */
744 if (label_addr_(labels
, o
->operand
, &i
->instr_words
[i
->length
])) {
745 DEBUG_PRINTF("(deferred label resolution)\n");
752 DEBUG_PRINTF("pending label address\n");
760 /* start with opcode bits */
761 bits
= opcode_bits_(i
->opcode
);
763 fprintf(stderr
, "unrecognized instruction '%s%s", i
->opcode
, i
->operands
? " " : "");
764 for (o
= i
->operands
; o
; o
= o
->next
)
765 fprintf(stderr
, " %s%s", o
->operand
, o
->next
? "," : "");
766 fprintf(stderr
, "'\n");
769 i
->instr_words
[0] |= 0x0f & bits
;
771 /* in rendered bytecode, all instructions have two operands; nbi instructions take 'first operand' bits. */
772 if ((bits
& 0x0f) == 0) {
773 bits
= nbi_opcode_bits_(i
->opcode
);
775 fprintf(stderr
, "INTERNAL ERROR: missing instruction in nbi opcode table\n");
780 fprintf(stderr
, "'%s' requires more operands\n", i
->opcode
);
783 bits
= value_bits_(labels
, o
->operand
, i
->instr_words
+ 1, &nwu
, allow_short_labels
);
785 fprintf(stderr
, "couldn't assemble instruction\n");
787 } else if (bits
== -2) {
788 DEBUG_PRINTF("%s: assembly deferred: unresolved label\n", __func__
);
789 /* keep going, but don't finalize until we can calculate label address */
795 i
->instr_words
[0] |= (bits
& 0x3f) << 4;
798 fprintf(stderr
, "'%s' requires more operands\n", i
->opcode
);
802 bits
= value_bits_(labels
, o
->operand
, i
->instr_words
+ 1 + nwu
, &nwu
, allow_short_labels
);
804 fprintf(stderr
, "couldn't assemble instruction\n");
806 } else if (bits
== -2) {
807 DEBUG_PRINTF("%s: assembly deferred: unresolved label\n", __func__
);
808 /* keep going, but don't finalize until we can calculate label address */
813 i
->instr_words
[0] |= (bits
& 0x3f) << 10;
816 fprintf(stderr
, "too many operands\n");
820 /* counting labels as words, we now know at least the maximum instruction length */
824 DEBUG_PRINTF("instruction words: [%u]", i
->length
);
825 for (bits
= 0; bits
<= (int)nwu
; bits
++)
826 DEBUG_PRINTFQ(" %04x", i
->instr_words
[bits
]);
829 DEBUG_PRINTFQ(" (preliminary)");
840 * read lines from stream f
841 * break each line into parts, populate parts into structures
844 int parse_stream_(FILE *f
, const char *src
, struct dynamic_array
*instructionps
, struct dynamic_array
*labels
, unsigned int allow_short_labels
) {
845 struct instruction_
*instr
, **instr_list_entry
;
846 unsigned int line
= 0;
850 buf
[sizeof buf
- 1] = '\0';
852 while (fgets(buf
, sizeof buf
, f
)) {
855 if (buf
[sizeof buf
- 1] != '\0') {
856 fprintf(stderr
, "%s:%u:%s", src
, line
, "input line too long\n");
861 if (buf_tokenize_(buf
, &instr
)) {
862 fprintf(stderr
, "%s:%u:%s", src
, line
, "trouble tokenizing input\n");
868 instr
->src_line
= line
;
869 /* add to list of instructions */
870 instr_list_entry
= dynarray_add(instructionps
, &instr
);
871 if (instr_list_entry
== NULL
) {
872 fprintf(stderr
, "%s:%u:%s", src
, line
, "could not populate instruction list\n");
877 struct label_ new_label
= {
878 .label
= instr
->label
,
879 .instr
= instr_list_entry
,
883 if (label_find_(labels
, instr
->label
)) {
884 fprintf(stderr
, "%s:%u:%s", src
, line
, "duplicate label\n");
888 if (dynarray_add(labels
, &new_label
) == NULL
) {
889 fprintf(stderr
, "%s:%u:%s", src
, line
, "could not populate label list\n");
892 label_addr_calculate_(instructionps
, labels
);
895 if (instr_assemble_(labels
, instr
, allow_short_labels
)) {
896 fprintf(stderr
, "%s:%u:%s", src
, line
, "could not assemble instruction\n");
902 fprintf(stderr
, "%s():%s\n", "fgets", strerror(errno
));
906 fprintf(stderr
, "parsing aborted\n");
914 * make a full pass over instruction list to resolve labels
917 int assemble_check_(struct dynamic_array
*instructionps
, struct dynamic_array
*labels
, unsigned int allow_short_labels
) {
921 /* fixing short labels .... */
922 /* by here we have our list of instructions and their maximum instruction lengths */
923 /* and we have a list of addresses, based on those maximum lengths */
924 /* So, if doing short labels, all label addresses are now suspect, so recompute them all... */
925 /* and reassemble.. */
926 /* uh.. what else am I forgetting.. this method won't work for labels approaching the limit */
927 /* of short form addresses, when there are more than the difference number of short form labels used previous to those addresses */
929 /* try this? keep another list of locations a label address is used */
930 /* as we step forward, and recompute an address, back up to first occurence of address, make sure nothing else has changed */
932 DEBUG_PRINTF(" final pass of assembler...\n");
933 for (x
= 0; x
< instructionps
->entries
; x
++) {
934 struct instruction_
**instrp
= (struct instruction_
**)DYNARRAY_ITEM(*instructionps
, x
);
935 retval
= instr_assemble_(labels
, *instrp
, allow_short_labels
);
937 fprintf(stderr
, "instruction %zu failed to assemble\n", x
);
940 if (! (*instrp
)->ready
) {
941 fprintf(stderr
, "instruction not resolvable\n");
946 VERBOSE_PRINTF("%3s %6s %-32s %-4s\n", "", "_addr_", "_label_", "_instruction_");
947 for (x
= 0; x
< labels
->entries
; x
++) {
948 struct label_
*l
= (struct label_
*)DYNARRAY_ITEM(*labels
, x
);
952 printf("%3s0x%04x %-32s ",
956 instruction_print_(*(l
->instr
), 0);
961 VERBOSE_PRINTF("\n");
964 fprintf(stderr
, "some labels could not be resolved\n");
970 * write assembled words to named file
973 int output_(struct dynamic_array
*instructionps
, const char *filename
) {
975 struct instruction_
**instrp
;
976 size_t i
, r
, total_words
= 0;
980 of
= fopen(filename
, "w");
982 fprintf(stderr
, "%s('%s'):%s\n", "fopen", filename
, strerror(errno
));
987 for (i
= 0; i
< instructionps
->entries
; i
++) {
988 instrp
= (struct instruction_
**)DYNARRAY_ITEM(*instructionps
, i
);
992 s
= instruction_print_(*instrp
, 1);
993 printf("%*s;", (44 - s
) > 0 ? (44 - s
) : 0, "");
994 for (x
= 0; x
< (*instrp
)->length
; x
++) {
995 printf(" %04x", (*instrp
)->instr_words
[x
]);
1001 r
= fwrite((*instrp
)->instr_words
, sizeof(DCPU16_WORD
), (*instrp
)->length
, of
);
1002 if (r
< (*instrp
)->length
) {
1003 fprintf(stderr
, "%s():%s\n", "fwrite", strerror(errno
));
1007 total_words
+= (*instrp
)->length
;
1010 fprintf(stderr
, "%s 0x%04zx instructions as 0x%04zx words\n",
1011 opt_
.dryrun
? "assembled" : "wrote",
1018 static struct dynamic_array
*instructionps_
;
1019 static struct dynamic_array
*labels_
;
1021 int main(int argc
, char *argv
[]) {
1022 const char *out_filename
= NULL
;
1023 unsigned int allow_short_labels
= 0;
1026 while ( (c
= getopt(argc
, argv
, "dhsvo:")) != EOF
) {
1033 allow_short_labels
++;
1038 fprintf(stderr
, "Sorry, I can only write one file at a time.\n");
1041 out_filename
= optarg
;
1061 if (out_filename
== NULL
)
1062 out_filename
= out_filename_default_
;
1065 instructionps_
= dynarray_new(sizeof (struct instruction_
*), 1024);
1066 labels_
= dynarray_new(sizeof(struct label_
), 256);
1067 if (instructionps_
== NULL
1068 || labels_
== NULL
) {
1069 fprintf(stderr
, "failed to initialize\n");
1073 /* if filenames were specified, parse them instead of stdin */
1076 char *filename
= *argv
;
1077 FILE *f
= fopen(filename
, "r");
1082 fprintf(stderr
, "%s('%s'):%s\n", "fopen", filename
, strerror(errno
));
1086 VERBOSE_PRINTF("assembling '%s'...\n", filename
);
1087 c
= parse_stream_(f
, filename
, instructionps_
, labels_
, allow_short_labels
);
1093 VERBOSE_PRINTF("assembling '%s'...\n", "stdin");
1094 c
= parse_stream_(stdin
, "-", instructionps_
, labels_
, allow_short_labels
);
1097 fprintf(stderr
, "could not parse input, aborting\n");
1101 if (assemble_check_(instructionps_
, labels_
, allow_short_labels
)) {
1102 fprintf(stderr
, "errors prevented assembly\n");
1106 if (output_(instructionps_
, out_filename
)) {
1107 fprintf(stderr
, "failed to create output\n");