14 * quick and dirty assembler for dcpu16
16 * Justin Wind <justin.wind@gmail.com>
17 * 2012 04 07 - implementation started
18 * 2012 04 10 - functional
19 * 2012 04 16 - support dat statements
20 * 2012 05 05 - v1.7 revision started
21 * 2012 05 08 - v1.7 revision implemented
24 * needs ability to specify location for code or data
25 * needs ability to specify label as relative to another label
26 * short labels not correctly computed
27 * in label struct, store index of instruction rather than ptr, ptrs for iteration in addr calculation are ugly
30 static const char * const src_id_
= "$Id$";
32 const char const out_filename_default_
[] = "a.out";
34 /* global invocation options */
43 #define DEBUG_PRINTF(...) do { if (opt_.verbose > 2) { printf("DEBUG: "); printf(__VA_ARGS__); } } while (0)
44 #define DEBUG_PRINTFQ(...) do { if (opt_.verbose > 2) printf(__VA_ARGS__); } while (0)
45 #define VERBOSE_PRINTF(...) do { if (opt_.verbose) printf(__VA_ARGS__); } while (0)
48 void usage_(char *prog
, unsigned int full
) {
49 FILE *f
= full
? stdout
: stderr
;
50 char *x
= strrchr(prog
, '/');
56 fprintf(f
, "%s -- \n\n",
59 fprintf(f
, "Usage: %s [-h] [-v] [-s] [-o file] file [file [...]]\n",
63 fprintf(f
, "\nOptions:\n"
64 "\t-h -- this screen\n"
65 "\t-o <file> -- output to <file> [default: %s]\n"
66 "\t-s -- allow short labels in instruction words\n"
67 "\t-d -- dry run, print results, do not write to file\n"
68 "\t-v -- verbose output\n",
69 out_filename_default_
);
71 fprintf(f
, "\n%78s\n",
76 /* LSB-0 aaaaaabbbbbooooo */
78 #define OPERAND_B_BITS 5
79 #define OPERAND_A_BITS 6
80 #define N_BIT_MASK(__x__) ((1 << (__x__)) - 1)
83 /* instructions have operands */
85 struct operand_
*next
;
86 char *operand
; /* tokenized operand text */
89 /* keep an array of instructions as we read them in */
92 char *label
; /* set if a label points here */
93 char *opcode
; /* tokenized instruction text */
94 struct operand_
*operands
; /* list of operands */
95 unsigned int ready
: 1; /* bytecode computed? */
96 unsigned int length
; /* number of words of bytecode */
97 DCPU16_WORD instr_words
[];
100 /* keep an array of labels, indexed back to their instruction locations */
102 char *label
; /* name of label */
103 struct instruction_
**instr
; /* pointer into array of instructions */
104 unsigned int ready
: 1; /* do we know where this label is yet? */
109 /* locate and return the label entry matching name */
111 struct label_
*label_find_(struct dynamic_array
*labels
, char *name
) {
114 for (x
= 0; x
< labels
->entries
; x
++) {
115 struct label_
*l
= (struct label_
*)DYNARRAY_ITEM(*labels
, x
);
116 if (strcmp(l
->label
, name
) == 0)
123 /* if a label has a validly-calculated address, fetch it */
125 int label_addr_(struct dynamic_array
*labels
, char *name
, DCPU16_WORD
*addr
) {
128 if ( (l
= label_find_(labels
, name
)) == NULL
)
137 /* attempt to determine the addresses of all labels */
139 void label_addr_calculate_(struct dynamic_array
*instructionps
, struct dynamic_array
*labels
) {
142 /* idea: label1:label2 - calculated as offset between labels */
144 /* for each label.. */
145 for (i
= 0; i
< labels
->entries
; i
++) {
147 struct instruction_
**instr
;
148 unsigned int word_count
= 0;
150 l
= (struct label_
*)DYNARRAY_ITEM(*labels
, i
);
152 DEBUG_PRINTFQ("%s: calculating address of label '%s'\n", __func__
, l
->label
);
155 force full resolution
while debugging
156 /* if it's already calculated, great. */
162 * starting at the instruction for this label,
163 * walk backwards through the list of instructions
164 * until we get to the start or a known prior label address.
165 * update our label with the freshly calculated addr
168 /* first fetch the instruction associated with the label we want to know about.. */
169 /* the addr of this instruction will be whatever follows all the preceding instructions */
170 /* so back up one before counting instruction lengths... */
171 instr
= ((struct label_
*)DYNARRAY_ITEM(*labels
, i
))->instr
;
172 /* is it the first one? */
173 if (instr
== (struct instruction_
**)DYNARRAY_ITEM(*instructionps
, 0))
178 while (instr
>= (struct instruction_
**)DYNARRAY_ITEM(*instructionps
, 0)) {
179 if ((*instr
)->ready
== 0)
180 DEBUG_PRINTF("%s: instr '%s' not ready\n", __func__
, (*instr
)->opcode
);
181 word_count
+= (*instr
)->length
;
183 DEBUG_PRINTF("%s: instr '%s' takes '%u' bytes\n", __func__
, (*instr
)->opcode
, (*instr
)->length
);
185 /* have we come across an instruction which a label points to?
186 it should already be calculated, so just add that on and be done */
188 && strcmp((*instr
)->label
, l
->label
)) {
191 if (label_addr_(labels
, (*instr
)->label
, &addr
)) {
192 fprintf(stderr
, "internal error: incomplete prior address for '%s' while calculating '%s'\n",
203 l
->addr
= word_count
;
205 DEBUG_PRINTF("label '%s' now has addr of 0x%04x\n", l
->label
, word_count
);
210 /* generate the nibble for a given basic opcode */
212 int opcode_bits_(char *opcode
) {
216 } opcodes_lower_nibble
[] = {
256 for (o
= opcodes_lower_nibble
; o
->op
[0]; o
++) {
257 if (strcasecmp(o
->op
, opcode
) == 0)
261 if (o
->op
[0] == '\0') {
262 fprintf(stderr
, "unknown instruction '%s'\n", opcode
);
269 /* generate the six bits for a given nbi opcode (aka first operand to opcode 0x00) */
271 int nbi_opcode_bits_(char *nbi_opcode
) {
275 } nbi_opcodes_bits
[] = {
276 { " ", 0x00 }, /* reserved for future */
289 for (o
= nbi_opcodes_bits
; o
->op
[0]; o
++) {
290 if (strcasecmp(o
->op
, nbi_opcode
) == 0)
294 if (o
->op
[0] == '\0') {
295 fprintf(stderr
, "unknown nbi instruction '%s'\n", o
->op
);
302 /* convert register character like 'x' to value like 0x03 */
304 unsigned int register_enumerate_(char r
) {
305 const char regs
[] = "AaBbCcXxYyZzIiJj";
306 const char *x
= strchr(regs
, r
);
311 fprintf(stderr
, "internal error, unknown register character 0x%02x\n", r
);
315 /* removes all occurences of chars from buf */
317 void buf_strip_chars_(char *buf
, char *chars
) {
320 for (s
= d
= buf
; *s
; s
++, d
++) {
321 while (*s
&& strchr(chars
, *s
)) {
333 * generate the six bits for a given operand string
334 * returns -1 if it could not parse the operand
335 * returns -2 if it could not parse the operand due to an unresolved label
336 * notes: nextword may be overwritten even if it's not used in final instruction
340 int value_bits_(struct dynamic_array
*labels
, const char *operand_orig
, DCPU16_WORD
*nextword
, unsigned int *nextwordused
, unsigned int allow_short_labels
) {
341 static char *operand
= NULL
;
342 static size_t operand_sz
= 0;
348 Our operand working buffer shouldn't ever need to be too big,
349 but DAT might blow that assumption.
351 if (operand_sz
<= strlen(operand_orig
)) {
353 size_t new_sz
= strlen(operand_orig
);
359 DEBUG_PRINTF("%s: allocating buffer of size %zu\n", __func__
, new_sz
);
360 tmp_ptr
= realloc(operand
, new_sz
);
361 if (tmp_ptr
== NULL
) {
362 fprintf(stderr
, "%s(%zu):%s\n", "realloc", new_sz
, strerror(errno
));
369 o
= strcpy(operand
, operand_orig
);
371 DEBUG_PRINTF("%s: operand '%s' ", __func__
, operand
); /* completed later */
373 /* this is a very stupid parser */
375 /* first, let's trim all whitespace out of string at once to make parsing easier */
376 buf_strip_chars_(operand
, " \t\n");
378 /* single character might match a register */
379 if (strlen(operand
) == 1
380 && strchr("AaBbCcXxYyZzIiJj", *operand
)) {
381 DEBUG_PRINTFQ("is register %c\n", *operand
);
382 return register_enumerate_(*operand
);
387 /* push and pop now share the same operand value */
388 if (strcasecmp(operand
, "POP") == 0
389 || strcasecmp(operand
, "[SP++]") == 0) {
390 DEBUG_PRINTFQ("is POP\n");
393 if (strcasecmp(operand
, "PUSH") == 0
394 || strcasecmp(operand
, "[--SP]") == 0) {
395 DEBUG_PRINTFQ("is PUSH\n");
399 if (strcasecmp(operand
, "PEEK") == 0
400 || strcasecmp(operand
, "[SP]") == 0) {
401 DEBUG_PRINTFQ("is PEEK\n");
405 /* this could be better, if we had a real token tree */
406 if (strncasecmp(operand
, "PICK", 4) == 0) {
407 DEBUG_PRINTFQ("is PICK ");
410 l
= strtoul(operand
+ 4, &ep
, 0);
412 && (*(operand
+ 4) && (*ep
== '\0')) ) {
414 DEBUG_PRINTFQ("(out of range)\n");
415 fprintf(stderr
, "constant invalid in operand '%s'\n", operand_orig
);
418 } else if (errno
== ERANGE
) {
419 DEBUG_PRINTFQ("(out of range)\n");
420 fprintf(stderr
, "constant invalid in operand '%s'\n", operand_orig
);
423 *nextword
= l
& 0xffff;
425 DEBUG_PRINTFQ("0x%04x\n", *nextword
);
429 if (strcasecmp(operand
, "SP") == 0) {
430 DEBUG_PRINTFQ("is register SP\n");
433 if (strcasecmp(operand
, "PC") == 0) {
434 DEBUG_PRINTFQ("is register PC\n");
437 if (strcasecmp(operand
, "EX") == 0) {
438 DEBUG_PRINTFQ("is register EX\n");
442 /* is the operand [bracketed]? */
443 if (operand
[0] == '[' && operand
[strlen(operand
) - 1] == ']') {
444 /* eat the brackets */
445 operand
[strlen(operand
) - 1] = '\0';
448 /* is it [register]? */
449 if (strlen(operand
) == 1
450 && strchr("AaBbCcXxYyZzIiJj", *operand
)) {
451 DEBUG_PRINTFQ("is dereferenced register %c\n", *operand
);
452 return 0x08 | register_enumerate_(*operand
);
455 /* is it [register+something]? */
456 if ( (ep
= strchr(operand
, '+')) ) {
460 DEBUG_PRINTFQ("is multipart.. ");
466 /* figure out which one is which */
467 if ((strlen(ep
) == 1 && strchr("AaBbCcXxYyZzIiJj", *ep
))
468 || (strlen(ep
) == 2 && strcasecmp(ep
, "SP")) ) {
471 } else if ((strlen(operand
) == 1 && strchr("AaBbCcXxYyZzIiJj", *operand
))
472 || (strlen(operand
) == 2 && strcasecmp(operand
, "SP")) ) {
476 DEBUG_PRINTFQ("is unparsable\n");
477 fprintf(stderr
, "couldn't parse operand '%s'\n", operand_orig
);
481 /* check if something is understandable as a value */
483 l
= strtoul(constant
, &ep
, 0);
485 && (*constant
&& (*ep
== '\0')) ) {
486 /* string conversion went without issue */
487 /* validate it will fit in a word */
489 DEBUG_PRINTFQ("is out of range\n");
490 fprintf(stderr
, "constant invalid in operand '%s'\n", operand_orig
);
495 *nextword
= l
& 0xffff;
498 /* special case [SP+n]/PICK n */
499 if (strlen(reg
) == 2) {
500 DEBUG_PRINTFQ("is PICK 0x%04x\n", *nextword
);
504 DEBUG_PRINTFQ("is a dereferenced register (%c) + constant (%hu)\n", *reg
, *nextword
);
505 return 0x10 | register_enumerate_(*reg
);
506 } else if (errno
== ERANGE
) {
507 fprintf(stderr
, "%s('%s'):%s\n", "strtoul", constant
, strerror(errno
));
510 /* what? still here? assume it's a label, I guess */
511 /* try to populate nextword with label address */
512 if (label_addr_(labels
, operand
, nextword
)) {
513 DEBUG_PRINTFQ("(deferred label resolution)\n");
517 DEBUG_PRINTFQ("is a dereferenced register (%c) + label\n", *reg
);
519 return 0x10 | register_enumerate_(*reg
);
522 /* it must just be a dereferenced literal then */
525 l
= strtoul(operand
, &ep
, 0);
527 && (*operand
&& (*ep
== '\0')) ) {
528 /* string conversion went without issue */
529 /* validate it will fit in a word */
531 DEBUG_PRINTFQ("is out of range\n");
532 fprintf(stderr
, "constant invalid in operand '%s'\n", operand_orig
);
536 DEBUG_PRINTFQ("is a dereferenced literal value (%hu)\n", *nextword
);
537 *nextword
= l
& 0xffff;
541 DEBUG_PRINTFQ("is out of range\n");
542 fprintf(stderr
, "trouble with operand '%s': %s\n", operand_orig
, strerror(errno
));
545 /* not a number? try a label */
546 if (label_addr_(labels
, operand
, nextword
)) {
547 DEBUG_PRINTFQ("(deferred label resolution)\n");
551 DEBUG_PRINTFQ("is a dereferenced label\n");
556 /* left with a literal or a label, then */
559 l
= strtoul(operand
, &ep
, 0);
561 || (*operand
&& (*ep
== '\0')) ) {
563 DEBUG_PRINTFQ("is out of range\n");
564 fprintf(stderr
, "constant invalid in operand '%s'\n", operand_orig
);
568 DEBUG_PRINTFQ("is literal value (%lu)\n", l
);
576 *nextword
= l
& 0xffff;
581 /* try to populate nextword with label address */
582 if (label_addr_(labels
, operand
, nextword
)) {
583 DEBUG_PRINTFQ("(deferred label resolution)\n");
584 /* assume non-small literal value */
589 DEBUG_PRINTFQ("is label '%s' (0x%02hx)\n", operand
, *nextword
);
590 if (allow_short_labels
591 && (*nextword
< 0x1f) ) {
592 DEBUG_PRINTF("small value label win\n");
593 return (0x21 + *nextword
) & N_BIT_MASK(OPERAND_A_BITS
);
595 if (allow_short_labels
596 && (*nextword
== 0xffff) ) {
597 DEBUG_PRINTF("small value label win\n");
605 /* prints an instruction's assembly */
607 int instruction_print_(struct instruction_
*i
, unsigned int with_label
) {
612 r
= printf("%-16s ", i
->label
? i
->label
: "");
614 r
= printf("%3s", i
->opcode
? i
->opcode
: "");
616 for (o
= i
->operands
; o
; o
= o
->next
)
617 r
+= printf(" %s%s", o
->operand
, o
->next
? "," : "");
622 l
= dcpu16_mnemonify_buf(i
->instr_words
);
626 DEBUG_PRINTF("!!internal inconsistency!! i->length:%u l:%hu should match\n", i
->length
, l
);
632 * Parses a zero-terminated line of input into a newly-allocated struct instruction_.
633 * [label] instruction [operand[,operand[,...]]]
634 * Does no validation of contents of any of these tokens, as of yet.
635 * does not clean up after itself if a malloc fails
638 int tokenize_line_(char *line
, struct instruction_
**next_instr
) {
639 const char const *whitespace
= " \t\n";
640 const char const *quotes
= "\"'`";
641 struct instruction_
*instr
= NULL
;
643 char *label
, *opcode
;
644 struct operand_
*operand_list
= NULL
;
645 struct operand_
**operand_tail
= &operand_list
;
646 size_t instr_words_needed
= 0;
653 /* strip leading whitespace */
654 line
+= strspn(line
, whitespace
);
658 /* set first bare ';' to '\0', thus isolating any comments */
659 /* here we only care about the side-effect of truncating the first separator character */
660 (void)strqtok_r(line
, ";", '\\', quotes
, &qt
, &st
);
661 /* we don't care if there was an unmatched quote at this point, let's see what happens */
665 /* carve off the first token, determine if it is a label */
666 x
= strqtok_r(line
, whitespace
, '\\', quotes
, &qt
, &st
);
667 if (x
== NULL
|| *x
== '\0')
670 /* labels could contain an unmatched quote character, I guess? */
674 /* we have something, try to make sense of what it is */
676 #ifdef NON_SPEC_LABELS
677 /* I want my labels like 'label:' */
678 if ( *(x
+ strlen(line
) - 1) == ':' ) {
679 *(x
+ strlen(line
) - 1) = '\0';
680 DEBUG_PRINTF("label: %s\n", x
);
684 opcode
= strqtok_r(NULL
, whitespace
, '\\', quotes
, &qt
, &st
);
689 #endif /* NON_SPEC_LABELS */
691 /* spec gives example of labels as ':label' */
696 opcode
= strqtok_r(NULL
, whitespace
, '\\', quotes
, &qt
, &st
);
701 /* opcodes shouldn't have quotes, so we'll ignore any unmatched quotes again */
703 if (opcode
&& *opcode
) {
704 /* if we have an opcode, we'll need at least one word to compile instruction */
705 instr_words_needed
++;
707 /* build a list of operands to hang off this instruction */
708 while ( (x
= strqtok_r(NULL
, ",", '\\', quotes
, &qt
, &st
)) ) {
709 struct operand_
*new_operand
;
712 /* trim whitespaces */
713 x
+= strspn(x
, whitespace
);
716 for (y
= x
+ strlen(x
) - 1; *y
; y
--) {
717 if (strchr(whitespace
, *y
)) {
724 fprintf(stderr
, "null operand encountered\n");
728 DEBUG_PRINTF("tokenized operand '%s'\n", x
);
730 new_operand
= malloc(sizeof *new_operand
);
731 if (new_operand
== NULL
) {
732 fprintf(stderr
, "%s():%s\n", "malloc", strerror(errno
));
736 new_operand
->operand
= strdup(x
);
737 if (new_operand
->operand
== NULL
) {
738 fprintf(stderr
, "%s():%s\n", "strdup", strerror(errno
));
742 new_operand
->next
= NULL
;
744 if (strchr(quotes
, x
[0])) {
745 /* if this is a quoted operand, assuming we are in a DAT statement, it will take up slightly less room than it is long */
746 instr_words_needed
+= strlen(x
) - 1;
748 instr_words_needed
++;
750 *operand_tail
= new_operand
;
751 operand_tail
= &(*operand_tail
)->next
;
755 DEBUG_PRINTF("allocating new instruction with room for %zu bytes\n", instr_words_needed
);
757 instr
= calloc(1, (instr_words_needed
* sizeof *instr
->instr_words
) + sizeof *instr
);
759 fprintf(stderr
, "%s():%s\n", "malloc", strerror(errno
));
764 instr
->label
= strdup(label
);
765 if (instr
->label
== NULL
) {
766 fprintf(stderr
, "%s():%s\n", "malloc", strerror(errno
));
774 instr
->opcode
= strdup(opcode
);
775 if (instr
->opcode
== NULL
) {
776 fprintf(stderr
, "%s():%s\n", "malloc", strerror(errno
));
783 instr
->operands
= operand_list
;
790 /* try to generate bytecode for an instruction */
791 /* returns -1 on unrecoverable error */
793 int instr_assemble_(struct dynamic_array
*labels
, struct instruction_
*i
, unsigned int allow_short_labels
) {
794 unsigned int nwu
= 0; /* number of words used */
795 unsigned int incomplete
= 0;
797 struct operand_
*o
= i
->operands
;
799 if (opt_
.verbose
> 2) {
800 printf("%s: assembling %p ", __func__
, (void *)i
);
801 instruction_print_(i
, 1);
802 printf("(line %zu)\n", i
->src_line
);
805 if (i
->opcode
== NULL
) {
807 assert(i
->operands
== NULL
);
808 /* just a label, move along */
814 /* special case DAT */
815 if (strncasecmp(i
->opcode
, "DAT", 3) == 0) {
816 DEBUG_PRINTF("processing DAT...\n");
820 for ( /* */ ; o
; o
= o
->next
) {
825 DEBUG_PRINTF("DAT operand:'%s' next:%p\n", o
->operand
, (void *)o
->next
);
827 /* is this a string? */
828 /* does it start with a quote, and end with the same quote? */
829 if ( (x
= strchr("\"'`", o
->operand
[0])) ) {
830 dat_len
= strlen(o
->operand
) - 1;
831 if (o
->operand
[dat_len
] == *x
) {
833 DEBUG_PRINTF("DAT string operand: %s\n", o
->operand
);
835 for (j
= 0, x
= o
->operand
+ 1;
838 i
->instr_words
[i
->length
] = *x
;
841 /* Note that strings in DAT do not include their zero-terminators */
842 /* specify as 'DAT "string", 0' */
847 /* is this a number? */
850 l
= strtoul(o
->operand
, &ep
, 0);
852 && (*o
->operand
&& (*ep
== '\0')) ) {
853 /* conversion succeeded */
855 fprintf(stderr
, "value '%lu' out of range\n", l
);
858 i
->instr_words
[i
->length
] = l
;
863 /* otherwise assume it's a label, even if we don't know what it is */
864 if (label_addr_(labels
, o
->operand
, &i
->instr_words
[i
->length
])) {
865 DEBUG_PRINTF("(deferred label '%s' resolution)\n", o
->operand
);
872 DEBUG_PRINTF("pending label address\n");
880 /* start with opcode bits */
881 bits
= opcode_bits_(i
->opcode
);
883 fprintf(stderr
, "unrecognized instruction '%s%s", i
->opcode
, i
->operands
? " " : "");
884 for (o
= i
->operands
; o
; o
= o
->next
)
885 fprintf(stderr
, " %s%s", o
->operand
, o
->next
? "," : "");
886 fprintf(stderr
, "'\n");
889 i
->instr_words
[0] |= bits
& N_BIT_MASK(OPCODE_BITS
);
891 /* in rendered bytecode, all instructions have a and b operands; nbi instructions occupy 'b operand' bits. */
892 if ((bits
& N_BIT_MASK(OPCODE_BITS
)) == 0) {
893 bits
= nbi_opcode_bits_(i
->opcode
);
895 fprintf(stderr
, "INTERNAL ERROR: missing instruction in nbi opcode table\n");
900 fprintf(stderr
, "'%s' requires more operands\n", i
->opcode
);
903 bits
= value_bits_(labels
, o
->operand
, i
->instr_words
+ 1, &nwu
, allow_short_labels
);
905 fprintf(stderr
, "couldn't assemble instruction\n");
907 } else if (bits
== -2) {
908 DEBUG_PRINTF("%s: assembly deferred: unresolved label\n", __func__
);
909 /* keep going, but don't finalize until we can calculate label address */
915 if (bits
> N_BIT_MASK(OPERAND_B_BITS
)) {
916 fprintf(stderr
, "%s: internal error: operand '%s' generated out of range\n", __func__
, "b");
919 i
->instr_words
[0] |= (bits
& N_BIT_MASK(OPERAND_B_BITS
)) << OPCODE_BITS
;
922 fprintf(stderr
, "'%s' requires more operands\n", i
->opcode
);
926 bits
= value_bits_(labels
, o
->operand
, i
->instr_words
+ 1 + nwu
, &nwu
, allow_short_labels
);
928 fprintf(stderr
, "couldn't assemble instruction\n");
930 } else if (bits
== -2) {
931 DEBUG_PRINTF("%s: assembly deferred: unresolved label\n", __func__
);
932 /* keep going, but don't finalize until we can calculate label address */
937 if (bits
> N_BIT_MASK(OPERAND_A_BITS
)) {
938 fprintf(stderr
, "%s: internal error: operand '%s' generated out of range\n", __func__
, "a");
940 i
->instr_words
[0] |= (bits
& N_BIT_MASK(OPERAND_A_BITS
)) << (OPCODE_BITS
+ OPERAND_B_BITS
);
943 fprintf(stderr
, "too many operands\n");
947 /* counting labels as words, we now know at least the maximum instruction length */
951 DEBUG_PRINTF("instruction words: [%u]", i
->length
);
952 for (bits
= 0; bits
<= (int)nwu
; bits
++)
953 DEBUG_PRINTFQ(" %04x", i
->instr_words
[bits
]);
956 DEBUG_PRINTFQ(" (preliminary)");
967 * read lines from stream f
968 * break each line into parts, populate parts into structures
971 int parse_stream_(FILE *f
, const char *src
, struct dynamic_array
*instructionps
, struct dynamic_array
*labels
, unsigned int allow_short_labels
) {
972 struct instruction_
*instr
, **instr_list_entry
;
973 unsigned int line
= 0;
977 buf
[sizeof buf
- 1] = '\0';
979 while (fgets(buf
, sizeof buf
, f
)) {
982 if (buf
[sizeof buf
- 1] != '\0') {
983 fprintf(stderr
, "%s:%u:%s", src
, line
, "input line too long\n");
988 if (tokenize_line_(buf
, &instr
)) {
989 fprintf(stderr
, "%s:%u:%s", src
, line
, "trouble tokenizing input\n");
995 instr
->src_line
= line
;
996 /* add to list of instructions */
997 instr_list_entry
= dynarray_add(instructionps
, &instr
);
998 if (instr_list_entry
== NULL
) {
999 fprintf(stderr
, "%s:%u:%s", src
, line
, "could not populate instruction list\n");
1004 struct label_ new_label
= {
1005 .label
= instr
->label
,
1006 .instr
= instr_list_entry
,
1010 if (label_find_(labels
, instr
->label
)) {
1011 fprintf(stderr
, "%s:%u:%s", src
, line
, "duplicate label\n");
1015 if (dynarray_add(labels
, &new_label
) == NULL
) {
1016 fprintf(stderr
, "%s:%u:%s", src
, line
, "could not populate label list\n");
1019 label_addr_calculate_(instructionps
, labels
);
1022 if (instr_assemble_(labels
, instr
, allow_short_labels
)) {
1023 fprintf(stderr
, "%s:%u:%s", src
, line
, "could not assemble instruction\n");
1029 fprintf(stderr
, "%s():%s\n", "fgets", strerror(errno
));
1033 fprintf(stderr
, "parsing aborted\n");
1041 * make a full pass over instruction list to resolve labels
1044 int assemble_check_(struct dynamic_array
*instructionps
, struct dynamic_array
*labels
, unsigned int allow_short_labels
) {
1048 /* fixing short labels .... */
1049 /* by here we have our list of instructions and their maximum instruction lengths */
1050 /* and we have a list of addresses, based on those maximum lengths */
1051 /* So, if doing short labels, all label addresses are now suspect, so recompute them all... */
1052 /* and reassemble.. */
1053 /* uh.. what else am I forgetting.. this method won't work for labels approaching the limit */
1054 /* of short form addresses, when there are more than the difference number of short form labels used previous to those addresses */
1056 /* try this? keep another list of locations a label address is used */
1057 /* as we step forward, and recompute an address, back up to first occurence of address, make sure nothing else has changed */
1059 DEBUG_PRINTF(" final pass of assembler...\n");
1060 for (x
= 0; x
< instructionps
->entries
; x
++) {
1061 struct instruction_
**instrp
= (struct instruction_
**)DYNARRAY_ITEM(*instructionps
, x
);
1062 retval
= instr_assemble_(labels
, *instrp
, allow_short_labels
);
1064 fprintf(stderr
, "instruction %zu failed to assemble\n", x
);
1067 if (! (*instrp
)->ready
) {
1068 fprintf(stderr
, "instruction not resolvable\n");
1073 VERBOSE_PRINTF("%3s %6s %-32s %-4s\n", "", "_addr_", "_label_", "_instruction_");
1074 for (x
= 0; x
< labels
->entries
; x
++) {
1075 struct label_
*l
= (struct label_
*)DYNARRAY_ITEM(*labels
, x
);
1079 printf("%3s0x%04x %-32s ",
1080 l
->ready
? "" : "*",
1083 instruction_print_(*(l
->instr
), 0);
1088 VERBOSE_PRINTF("\n");
1091 fprintf(stderr
, "some labels could not be resolved\n");
1097 * write assembled words to named file
1100 int output_(struct dynamic_array
*instructionps
, const char *filename
) {
1102 struct instruction_
**instrp
;
1103 size_t i
, r
, total_words
= 0;
1106 if (! opt_
.dryrun
) {
1107 of
= fopen(filename
, "w");
1109 fprintf(stderr
, "%s('%s'):%s\n", "fopen", filename
, strerror(errno
));
1114 for (i
= 0; i
< instructionps
->entries
; i
++) {
1115 instrp
= (struct instruction_
**)DYNARRAY_ITEM(*instructionps
, i
);
1119 s
= instruction_print_(*instrp
, 1);
1120 printf("%*s;", (44 - s
) > 0 ? (44 - s
) : 0, "");
1121 for (x
= 0; x
< (*instrp
)->length
; x
++) {
1122 printf(" %04x", (*instrp
)->instr_words
[x
]);
1128 r
= fwrite((*instrp
)->instr_words
, sizeof(DCPU16_WORD
), (*instrp
)->length
, of
);
1129 if (r
< (*instrp
)->length
) {
1130 fprintf(stderr
, "%s():%s\n", "fwrite", strerror(errno
));
1134 total_words
+= (*instrp
)->length
;
1137 fprintf(stderr
, "%s 0x%04zx instructions as 0x%04zx words\n",
1138 opt_
.dryrun
? "assembled" : "wrote",
1145 static struct dynamic_array
*instructionps_
;
1146 static struct dynamic_array
*labels_
;
1148 int main(int argc
, char *argv
[]) {
1149 const char *out_filename
= NULL
;
1150 unsigned int allow_short_labels
= 0;
1153 while ( (c
= getopt(argc
, argv
, "dhsvo:")) != EOF
) {
1160 allow_short_labels
++;
1165 fprintf(stderr
, "Sorry, I can only write one file at a time.\n");
1168 out_filename
= optarg
;
1188 if (out_filename
== NULL
)
1189 out_filename
= out_filename_default_
;
1192 instructionps_
= dynarray_new(sizeof (struct instruction_
*), 1024);
1193 labels_
= dynarray_new(sizeof(struct label_
), 256);
1194 if (instructionps_
== NULL
1195 || labels_
== NULL
) {
1196 fprintf(stderr
, "failed to initialize\n");
1200 /* if filenames were specified, parse them instead of stdin */
1203 char *filename
= *argv
;
1204 FILE *f
= fopen(filename
, "r");
1209 fprintf(stderr
, "%s('%s'):%s\n", "fopen", filename
, strerror(errno
));
1213 VERBOSE_PRINTF("assembling '%s'...\n", filename
);
1214 c
= parse_stream_(f
, filename
, instructionps_
, labels_
, allow_short_labels
);
1220 VERBOSE_PRINTF("assembling '%s'...\n", "stdin");
1221 c
= parse_stream_(stdin
, "-", instructionps_
, labels_
, allow_short_labels
);
1224 fprintf(stderr
, "could not parse input, aborting\n");
1228 if (assemble_check_(instructionps_
, labels_
, allow_short_labels
)) {
1229 fprintf(stderr
, "errors prevented assembly\n");
1233 if (output_(instructionps_
, out_filename
)) {
1234 fprintf(stderr
, "failed to create output\n");