--- /dev/null
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sysexits.h>
+#include <assert.h>
+
+/* #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 <file> -- output to <file> [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);
+}
--- /dev/null
+#ifndef AS_DCPU16_H_PTFNJB09
+#define AS_DCPU16_H_PTFNJB09
+
+#include "dcpu16.h"
+
+struct instruction_ {
+ char *label; /* set if a label points here */
+ char *opcode; /* tokenized instruction text */
+ struct operand_ *operands; /* list of operands */
+ unsigned int length; /* words */
+ unsigned int ready : 1; /* bytecode computed? */
+ DCPU16_WORD instr_words[];
+};
+
+
+enum operand_types_{
+ OT_DIRECT, /* these operands simply render their contents into bytecode */
+ OT_NEXT, /* these operands increase instruction length */
+ OT_LABEL /* labels need to be computed then converted to other types */
+};
+
+struct operand_ {
+ struct operand_ *next;
+ char *operand; /* tokenized operand text */
+ enum operand_types_ type;
+ union {
+ DCPU16_WORD word_value;
+ struct instruction_ *label_destination;
+ } value;
+};
+
+
+#define IL_SIZE(entries) (((entries) * sizeof(struct instruction_ *)) + sizeof(struct instruction_list_))
+
+struct instruction_list_ {
+ size_t allocated;
+ size_t entries;
+ struct instruction_ *instr[];
+};
+
+
+/* note label table holds its own structs, not pointers */
+struct label_ {
+ char *label; /* name of label */
+ struct instruction_ *instr;
+};
+
+#define LL_SIZE(entries) (((entries) * sizeof(struct label_ *)) + sizeof(struct label_list_))
+
+struct label_list_ {
+ size_t allocated;
+ size_t entries;
+ struct label_ label[];
+};
+
+#endif /* AS_DCPU16_H_PTFNJB09 */