#include #include #include #include #include #include #include /* #include dcpu16.h */ typedef unsigned short DCPU16_WORD; /* quick and dirty assembler for dcpu16 */ static const char * const src_id_ = "$Id$"; const char const out_filename_default_[] = "a.out"; static void usage_(char *prog, unsigned int full) { FILE *f = full ? stdout : stderr; char *x = strrchr(prog, '/'); if (x && *(x + 1)) prog = x + 1; if (full) fprintf(f, "%s -- \n\n", prog); fprintf(f, "Usage: %s\n", prog); if (full) { fprintf(f, "\nOptions:\n" "\t-h -- this screen\n" "\t-o -- output to [default: %s]\n", out_filename_default_); fprintf(f, "\n%78s\n", src_id_); } } struct operand_ { struct operand_ *next; char *operand; }; struct instruction_ { struct instruction_ *next; char *label; char *opcode; struct operand_ *operands; unsigned int length; /* words */ DCPU16_WORD instr_words[]; }; /* buf must be 0-terminated */ static int buf_tokenize_(char *buf, struct instruction_ **next_instr) { const char const *sep = " \t\n"; struct instruction_ *instr = NULL; char *label = NULL, *opcode = NULL, *operand = NULL; char *x, *y, *st; assert(buf != NULL); assert(next_instr != NULL); *next_instr = NULL; /* kill comments */ if ((x = strchr(buf, ';')) != NULL) *x = '\0'; /* kill leading whitespace */ buf += strspn(buf, " \t\n"); /* kill trailing newlines */ if ((x = strrchr(buf, '\n')) != NULL) *x = '\0'; /* determine if first token is label, opcode, or we just have a blank line to ignore */ x = strtok_r(buf, sep, &st); /* empty line? nothing to do here. */ if (x == NULL) return 0; /* labels end with :, otherwise its an opcode */ if ((y = strrchr(x, ':')) != NULL) { *y = '\0'; label = x; opcode = strtok_r(NULL, sep, &st); } else { label = NULL; opcode = x; } if (opcode) { operand = st; } instr = calloc(1, sizeof *instr); if (instr == NULL) { fprintf(stderr, "%s():%s\n", "malloc", strerror(errno)); return -1; } instr->label = label; instr->opcode = opcode; if (operand) { } *next_instr = instr; return 0; } /* thish should grow buffer to fit huge linesh, but I jusht don't care right now, hic */ static int parse_stream_(FILE *f) { struct instruction_ *instr; char buf[(1<<14)]; buf[sizeof buf - 1] = '\0'; while (fgets(buf, sizeof buf, f)) { if (buf[sizeof buf - 1] != '\0') { fprintf(stderr, "input buffer exhausted\n"); break; } if (buf_tokenize_(buf, &instr)) { fprintf(stderr, "trouble tokenizing input\n"); break; } if (instr) { struct operand_ *o; if (instr->label) { printf("TRACE: new label '%s'\n", instr->label); } printf("TRACE: tokenized opcode:%s operands:", instr->opcode); for (o = instr->operands; o; o = o->next) { printf("%s%s", o->operand, o->next ? ", " : ""); } printf("\n"); /* add to queue of instructions */ } } if (ferror(f)) { fprintf(stderr, "%s():%s\n", "fgets", strerror(errno)); return -1; } if (! feof(f)) { fprintf(stderr, "parsing aborted\n"); return -1; } return 0; } int main(int argc, char *argv[]) { const char *out_filename = NULL; int c; while ( (c = getopt(argc, argv, "ho:")) != EOF ) { switch (c) { case 'o': if (out_filename) { fprintf(stderr, "Sorry, I can only write one file at a time.\n"); exit(EX_CANTCREAT); } out_filename = optarg; break; case 'h': usage_(argv[0], 1); exit(EX_OK); default: usage_(argv[0], 0); exit(EX_USAGE); } } if (out_filename == NULL) out_filename = out_filename_default_; /* if filenames were specified, parse them instead of stdin */ if (argc - optind) { while (argc - optind) { FILE *f = fopen(argv[argc - optind], "r"); if (f == NULL) { fprintf(stderr, "%s('%s'):%s\n", "fopen", argv[argc - optind], strerror(errno)); optind++; continue; } parse_stream_(f); fclose(f); optind++; } } else { parse_stream_(stdin); } exit(EX_OK); }