initial commit
authorJustin Wind <justin.wind@gmail.com>
Sun, 8 Apr 2012 22:12:52 +0000 (15:12 -0700)
committerJustin Wind <justin.wind@gmail.com>
Sun, 8 Apr 2012 22:12:52 +0000 (15:12 -0700)
Makefile [new file with mode: 0644]
dcpu16.c [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..f3324ef
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,18 @@
+#!make
+#
+PROGRAMS = dcpu16
+
+CFLAGS = -g -Wall -Wextra -pedantic -std=c99
+LDFLAGS = 
+
+all:   $(PROGRAMS)
+
+
+as-dcpu16: as-dcpu16.o
+
+dcpu16:        dcpu16.o
+
+clean: 
+       @rm -rf $(PROGRAMS) *.o *.dSYM
+
+check: $(PROGRAMS)
diff --git a/dcpu16.c b/dcpu16.c
new file mode 100644 (file)
index 0000000..d0d3b40
--- /dev/null
+++ b/dcpu16.c
@@ -0,0 +1,767 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <sysexits.h>
+
+/*
+ *  emulates the DCPU16 system from http://0x10c.com/doc/dcpu-16.txt
+ *  
+ *  I couldn't remember ever implementing an emulator before, so this
+ *  happened.  As such, consider this a toy in progress.
+ *  There are likely many improvable aspects.
+ *  
+ *  Justin Wind <justin.wind@gmail.com>
+ *    2012 04 05 - implementation started
+ *    2012 04 06 - first functionality achieved
+ *
+ *  TODO
+ *    move cli driver to separate module
+ */
+
+static const char * const src_id_ = "$Id$";
+
+/* the target system's concept of a word */
+#define WORD DCPU16_WORD
+typedef unsigned short WORD;
+
+#define RAM_SIZE 0x10000
+static const char regnames_[] = "ABCXYZIJ";
+struct dcpu16 {
+    unsigned long long cycle;   /* number of cycles it took to get to current state */
+    WORD reg_work_[2];          /* holding bins for literal values when decoding instructions */
+    WORD reg[8];                /* system registers, a b c x y z i j */
+    WORD pc;                    /* program counter */
+    WORD sp;                    /* stack pointer */
+    WORD o;                     /* overflow */
+    unsigned int skip_ : 1;     /* */
+    WORD ram[RAM_SIZE];         /* memory */
+};
+
+
+static unsigned int trace_mode_ = 0; /* turn on for overly verbose internals */
+
+#define WARN(...) warn(__VA_ARGS__)
+static inline void warn(char *fmt, ...) __attribute__((format(printf, 1, 2)));
+static inline void warn(char *fmt, ...) {
+    va_list ap;
+
+    fprintf(stderr, "!!! ");
+    va_start(ap, fmt);
+    vfprintf(stderr, fmt, ap);
+    va_end(ap);
+    fprintf(stderr, "\n");
+    fflush(stderr);
+}
+
+
+#define TRACE(...) do { if (trace_mode_) trace(__VA_ARGS__); } while (0)
+static inline void trace(char *fmt, ...) __attribute__((format(printf, 1, 2)));
+static inline
+void trace(char *fmt, ...) {
+    va_list ap;
+
+    va_start(ap, fmt);
+    vfprintf(stdout, fmt, ap);
+    va_end(ap);
+    fprintf(stdout, "\n");
+    fflush(stdout);
+}
+
+
+/* sets *v to be the destination of the value */
+/* workv is buffer to use to accumulate literal value before use */
+/* returns true if destination points to literal (id est *v should ignore writes) */
+static unsigned int value_decode(struct dcpu16 *d, WORD value, WORD *work_v, WORD **v) {
+    WORD nextword;
+    unsigned int retval = 0;
+
+    assert(value >= 0x00 && value <= 0x3f);
+
+    /* does this value indicate a literal */
+    if (value >= 0x1f)
+        retval = 1;
+
+    /* if we're skipping this instruction, just advance the pc if needed */ 
+    if (d->skip_) {
+        TRACE(">>      SKIP decode");
+        if (value == 0x1e || value == 0x1f)
+            d->pc++;
+        return retval;
+    }
+
+    if (value <= 0x07) { /* register */
+        *v = d->reg + value;
+        TRACE(">>     %c (0x%04x)",
+              regnames_[value],
+              **v);
+
+    } else if (value <= 0x0f) { /* [register] */
+        *v = &(d->ram[ d->reg[(value & 0x07)] ]);
+        TRACE(">>     [%c] [0x%04x] (0x%04x)",
+              regnames_[value&0x07],
+              d->reg[value&0x07],
+              **v);
+
+    } else if (value <= 0x17) { /* [next word + register] */
+        nextword = d->ram[ d->pc++ ];
+        d->cycle++;
+        *v = &(d->ram[ nextword + d->reg[(value & 0x07)] ]);
+        TRACE(">>     [nextword + %c] [0x%04x + 0x%04x] (0x%04x)",
+              regnames_[(value & 0x07)],
+              nextword,
+              d->reg[(value & 0x07)],
+              **v);
+
+    } else switch (value) {
+        case 0x18: /* POP / [sp++] */
+        *v = &(d->ram[ d->sp++ ]);
+        TRACE(">>     POP [0x%04x] (0x%04x)",
+              d->sp - 1,
+              **v);
+        break;
+
+        case 0x19: /* PEEK / [sp] */
+        *v = &(d->ram[ d->sp ]);
+        TRACE(">>     PEEK [0x%04x] (0x%04x)",
+              d->sp,
+              **v);
+        break;
+
+        case 0x1a: /* PUSH / [--sp] */
+        *v = &(d->ram[ --d->sp ]);
+        TRACE(">>     PUSH [0x%04x] (0x%04x)",
+              d->sp + 1,
+              **v);
+        break;
+
+        case 0x1b: /* SP */
+        *v = &(d->sp);
+        TRACE(">>     SP (0x%04x)",
+              **v);
+        break;
+
+        case 0x1c: /* PC */
+        *v = &(d->pc);
+        TRACE(">>     PC (0x%04x)", **v);
+        break;
+
+        case 0x1d: /* O */
+        *v = &(d->o);
+        TRACE(">>     O (0x%04x)", **v);
+        break;
+
+        case 0x1e: /* [next word] / [[pc++]] */
+        nextword = d->ram[ d->pc++ ];
+        d->cycle++;
+        *v = &(d->ram[ nextword ]);
+        TRACE(">>     [nextword] [0x%04x] (0x%04x)",
+              nextword,
+              **v);
+        break;
+
+        case 0x1f: /* next word (literal) / [pc++] */
+        nextword = d->ram[ d->pc++ ];
+        d->cycle++;
+        *work_v = nextword;
+        *v = work_v;
+        TRACE(">>     nextword (0x%04x)", **v);
+        break;
+
+        default: /* 0x20-0x3f: literal values 0x00-0x1f */
+        *work_v = value & 0x1f;
+        *v = work_v;
+        TRACE(">>     literal (0x%04x)", **v);
+    }
+
+    return retval;
+}
+
+#define OPCODE_BASIC_BITS (4)
+#define OPCODE_BASIC_SHIFT (0)
+
+#define OPCODE_NBI_BITS (6)
+#define OPCODE_NBI_SHIFT (4)
+
+#define OPCODE_FUTURE_BITS (16)
+#define OPCODE_FUTURE_SHIFT (10)
+
+#define OPCODE_NAME_LEN 16
+struct opcode_entry {
+    unsigned short value;
+    char name[OPCODE_NAME_LEN];
+    void (*impl)(struct dcpu16 *, WORD, WORD);
+};
+
+/* messy boilerplate for opcode handlers */
+
+#define OP_IMPL(x) static void op_##x(struct dcpu16 *d, WORD val_a, WORD val_b)
+
+#define OP_NBI_ (void)val_b
+#define OP_BASIC_ (void)value_decode(d, val_b, &d->reg_work_[0], &b)
+#define OP_TYPE(op_type) WORD *a, *b;\
+    unsigned int lit_a;\
+    do {\
+        assert(d != NULL);\
+        lit_a = value_decode(d, val_a, &d->reg_work_[0], &a);\
+        op_type;\
+        if (d->skip_) {\
+            TRACE("++ SKIPPED");\
+            d->skip_ = 0;\
+            return;\
+        }\
+    } while (0)
+
+
+#define OP_BASIC(x) WORD *a, *b;\
+    unsigned int lit_a;\
+    do {\
+        assert(d != NULL);\
+        lit_a = value_decode(d, val_a, &d->reg_work_[0], &a);\
+        value_decode(d, val_b, &d->reg_work_[1], &b);\
+        if (d->skip_) {\
+            TRACE("++ SKIPPED");\
+            d->skip_ = 0;\
+            return;\
+        }\
+    } while(0)
+
+#define OP_NBI(x) WORD *a;\
+    unsigned int lit_a;\
+    do {\
+        assert(d != NULL);\
+        lit_a = value_decode(d, val_a, &d->reg_work_[0], &a);\
+        (void)val_b;\
+        if (d->skip_) {\
+            TRACE("++ SKIPPED");\
+            d->skip_ = 0;\
+            return;\
+        }\
+    } while(0)
+
+
+/* extended opcodes */
+
+/*
+    N.B. this next function currently decodes values -- id est, it is
+    an opcode processing terminus; however, if 'future instruction set
+    expansion' happens, this will probably need to behave more like
+    the OP_IMPL(_nbi_) function which invoked it, if those instructions
+    have zero or differently styled operands.
+*/
+OP_IMPL(nbi__reserved_) {
+    OP_NBI(nbi__reserved_);
+    /* reserved for future expansion */
+
+    WORD future_opcode = (d->ram[d->pc] >> OPCODE_FUTURE_SHIFT);
+    WARN("reserved future opcode 0x%04x invoked", future_opcode);
+}
+
+OP_IMPL(nbi_jsr) {
+    OP_NBI(nbi_jsr);
+    /* pushes the address of the next instruction to the stack, then sets PC to a */
+
+    d->ram[ --d->sp ] = d->pc;
+    d->pc = *a;
+
+    d->cycle += 2;
+}
+
+OP_IMPL(nbi__reserved2_) {
+    OP_NBI(nbi__reserved2_);
+    /* reserved */
+
+    WARN("reserved nbi opcode invoked");
+}
+
+static const struct opcode_entry opcode_nbi_entries[] = {
+    {0x0, "(reserved)", op_nbi__reserved_},
+    {0x1, "JSR", op_nbi_jsr},
+    {0x2, "(reserved)", op_nbi__reserved2_},
+    {0x0, "", NULL}
+};
+#define OPCODE_NBI_MAX (((sizeof(opcode_nbi_entries)) / (sizeof(struct opcode_entry))) - 1)
+
+
+/* basic opcodes */
+
+/*
+    N.B. the following function does not decode values, as the nbi
+    instructions only have one operand.
+*/
+OP_IMPL(_nbi_) {
+    assert(d != NULL);
+    /* non-basic instruction */
+
+    /* don't do normal value decoding here */
+
+    WORD nbi_opcode = val_a;
+    const struct opcode_entry *e = opcode_nbi_entries;
+
+    e = opcode_nbi_entries + ( (nbi_opcode < OPCODE_NBI_MAX) ? nbi_opcode : (OPCODE_NBI_MAX - 1) );
+
+    assert(e->impl != NULL);
+
+    TRACE(">> %s 0x%04x", e->name, val_b);
+    e->impl(d, val_b, 0);
+}
+
+OP_IMPL(set) {
+    OP_BASIC(set);
+    /* sets a to b */
+
+    /* only set non-literal target */
+    if (val_a < 0x1f) {
+        *a = *b;
+    }
+
+    d->cycle += 1;
+}
+
+OP_IMPL(add) {
+    OP_BASIC(add);
+    /* sets a to a+b, sets O to 0x0001 if there's an overflow, 0x0 otherwise */
+    unsigned int acc = *a + *b;
+
+    if (val_a < 0x1f) {
+        *a = acc;
+    }
+    d->o = acc >> 16;
+
+    d->cycle += 2;
+}
+
+OP_IMPL(sub) {
+    OP_BASIC(sub);
+    /* sets a to a-b, sets O to 0xffff if there's an underflow, 0x0 otherwise */
+    unsigned int acc = *a - *b;
+
+    if (val_a < 0x1f) {
+        *a = acc;
+    }
+    d->o = acc >> 16;
+
+    d->cycle += 2;
+}
+
+OP_IMPL(mul) {
+    OP_BASIC(mul);
+    /* sets a to a*b, sets O to ((a*b)>>16)&0xffff */
+    unsigned int acc = *a * *b;
+
+    if (val_a < 0x1f) {
+        *a = acc;
+    }
+    d->o = acc >> 16;
+    d->cycle += 2;
+}
+
+OP_IMPL(div) {
+    OP_BASIC(div);
+    /* sets a to a/b, sets O to ((a<<16)/b)&0xffff. if b==0, sets a and O to 0 instead. */
+
+    if (*b == 0) {
+        if (val_a < 0x1f) {
+            *a = 0;
+        }
+        d->o = 0;
+    } else {
+        unsigned int acc = *a / *b;
+
+        if (val_a < 0x1f) {
+            *a = acc;
+        }
+
+        acc = (*a << 16) / *b;
+        d->o = acc;
+    }
+
+    d->cycle += 3;
+}
+
+OP_IMPL(mod) {
+    OP_BASIC(mod);
+    /* sets a to a%b. if b==0, sets a to 0 instead. */
+
+    if (*b == 0) {
+        if (val_a < 0x1f) {
+            *a = 0;
+        }
+    } else {
+        if (val_a < 0x1f) {
+            *a = *a % *b;
+        }
+    }
+
+    d->cycle += 3;
+}
+
+OP_IMPL(shl) {
+    OP_BASIC(shl);
+    /* sets a to a<<b, sets O to ((a<<b)>>16)&0xffff */
+    unsigned int acc = *a << *b;
+
+    if (val_a < 0x1f) {
+        *a = acc;
+    }
+    d->o = acc >> 16;
+
+    d->cycle += 2;
+}
+
+OP_IMPL(shr) {
+    OP_BASIC(shr);
+    /* sets a to a>>b, sets O to ((a<<16)>>b)&0xffff */
+    unsigned int acc = *a >> *b;
+
+    if (val_a < 0x1f) {
+        *a = acc;
+    }
+    d->o = (*a << 16) >> *b;
+
+    d->cycle += 2;
+}
+
+OP_IMPL(and) {
+    OP_BASIC(and);
+    /* sets a to a&b */
+
+    if (val_a < 0x1f) {
+        *a = *a & *b;
+    }
+
+    d->cycle += 1;
+}
+
+OP_IMPL(bor) {
+    OP_BASIC(bor);
+    /* sets a to a|b */
+
+    if (val_a < 0x1f) {
+        *a = *a | *b;
+    }
+
+    d->cycle += 1;
+}
+
+OP_IMPL(xor) {
+    OP_BASIC(xor);
+    /* sets a to a^b */
+
+    if (val_a < 0x1f) {
+        *a = *a ^ *b;
+    }
+
+    d->cycle += 1;
+}
+
+OP_IMPL(ife) {
+    OP_BASIC(ife);
+    /* performs next instruction only if a==b */
+
+    if (*a == *b) {
+        /* */
+    } else {
+        d->skip_ = 1;
+        d->cycle++;
+    }
+
+    d->cycle += 2;
+}
+
+OP_IMPL(ifn) {
+    OP_BASIC(ifn);
+    /* performs next instruction only if a!=b */
+
+    if (*a != *b) {
+        /* */
+    } else {
+        d->skip_ = 1;
+        d->cycle++;
+    }
+
+    d->cycle += 2;
+}
+
+OP_IMPL(ifg) {
+    OP_BASIC(ifg);
+    /* performs next instruction only if a>b */
+
+    if (*a > *b) {
+        /* */
+    } else {
+        d->skip_ = 1;
+        d->cycle++;
+    }
+
+    d->cycle += 2;
+}
+
+OP_IMPL(ifb) {
+    OP_BASIC(ifb);
+    /* performs next instruction only if (a&b)!=0 */
+
+    if ((*a & *b) != 0) {
+        /* */
+    } else {
+        d->skip_ = 1;
+        d->cycle++;
+    }
+
+    d->cycle += 2;
+}
+
+static const struct opcode_entry opcode_basic_entries[] = {
+    {0x0, "(nbi)", op__nbi_},
+    {0x1, "SET", op_set },
+    {0x2, "ADD", op_add },
+    {0x3, "SUB", op_sub },
+    {0x4, "MUL", op_mul },
+    {0x5, "DIV", op_div },
+    {0x6, "MOD", op_mod },
+    {0x7, "SHL", op_shl },
+    {0x8, "SHR", op_shr },
+    {0x9, "AND", op_and },
+    {0xa, "BOR", op_bor },
+    {0xb, "XOR", op_xor },
+    {0xc, "IFE", op_ife },
+    {0xd, "IFN", op_ifn },
+    {0xe, "IFG", op_ifg },
+    {0xf, "IFB", op_ifb },
+    {0x0, "", NULL }
+};
+
+void dump_value(WORD value, WORD nextword) {
+    if (value < 0x07) {
+        printf(" %c", regnames_[value]);
+    } else if (value < 0x0f) {
+        printf(" [%c]", regnames_[value & 0x07]);
+    } else if (value < 0x17) {
+        printf(" [0x%04x + %c]", nextword, regnames_[value & 0x07]);
+    } else switch (value) {
+        case 0x18: printf(" POP"); break;
+        case 0x19: printf(" PEEK"); break;
+        case 0x1a: printf(" PUSH"); break;
+        case 0x1b: printf(" SP"); break;
+        case 0x1c: printf(" PC"); break;
+        case 0x1d: printf(" O"); break;
+        case 0x1e: printf(" [0x%04x]", nextword); break;
+        case 0x1f: printf(" 0x%04x", nextword); break;
+        default:   printf(" 0x%02x", value - 0x20);
+    }
+}
+
+void dump_instruction(struct dcpu16 *d, WORD addr) {
+    WORD opcode, a, b;
+    unsigned int instr_len = 1;
+    const struct opcode_entry *e;
+
+    opcode = (d->ram[addr] >> OPCODE_BASIC_SHIFT) & ((1 << OPCODE_BASIC_BITS) - 1);
+    a = (d->ram[addr] >> (OPCODE_BASIC_SHIFT + OPCODE_BASIC_BITS)) & ((1 << 6) - 1);
+    b = (d->ram[addr] >> (OPCODE_BASIC_SHIFT + OPCODE_BASIC_BITS + 6)) & ((1 << 6) - 1); 
+
+    assert(opcode < 0x0f);
+    assert(a < 0x3f);
+    assert(b < 0x3f);
+
+    printf("next instr 0x%04x: %04x", addr, d->ram[addr]);
+
+    if (opcode != 0)
+    {
+        if (a == 0x1e || a == 0x1f) {
+            printf(" %04x", d->ram[addr + instr_len]);
+            instr_len++;
+        }
+    }
+    if (b == 0x1e || b == 0x1f) {
+        printf(" %04x", d->ram[addr + instr_len]);
+        instr_len++;
+    }
+
+    if (opcode)
+        e = opcode_basic_entries + opcode;
+    else
+        e = opcode_nbi_entries + ( (a < OPCODE_NBI_MAX) ? a : (OPCODE_NBI_MAX - 1) );
+    printf("\n\t%s", e->name);
+    if (opcode != 0) {
+        dump_value(a, d->ram[addr + 1]);
+        if (a == 0x1e || a == 0x1f)
+            addr++;
+        printf(",");
+    }
+
+    dump_value(b, d->ram[addr + 1]);
+
+    printf("\n");
+}
+
+void dcpu16_execute_next_instruction(struct dcpu16 *d) {
+    WORD opcode;
+    WORD val_a, val_b;
+    const struct opcode_entry *e;
+
+    /* fetch next instruction */
+    if (d->pc > RAM_SIZE) { /* currently impossible */
+        WARN("%s beyond %u", "PC", RAM_SIZE);
+        /* d->pc %= RAM_SIZE; */
+    }
+
+    opcode = (d->ram[ d->pc ] >> OPCODE_BASIC_SHIFT) & ((1 << OPCODE_BASIC_BITS) - 1);
+    val_a = (d->ram[d->pc] >> (OPCODE_BASIC_SHIFT + OPCODE_BASIC_BITS)) & ((1 << 6) - 1);
+    val_b = (d->ram[d->pc] >> (OPCODE_BASIC_SHIFT + OPCODE_BASIC_BITS + 6)) & ((1 << 6) - 1);
+
+    d->pc++;
+
+    for (e = opcode_basic_entries; e->impl; e++) {
+        if (e->value == opcode) {
+            TRACE(">> %s 0x%04x, 0x%04x", e->name, val_a, val_b);
+            e->impl(d, val_a, val_b);
+            break;
+        }
+    }
+}
+
+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");
+
+        fprintf(f, "\n%78s\n", src_id_);
+    }
+}
+
+static int file_load(struct dcpu16 *d, char *filename) {
+    FILE *f;
+    size_t r;
+
+    f = fopen(filename, "rb");
+    if (f == NULL)
+    {
+        fprintf(stderr, "%s(%s):%s\n", "fopen", filename, strerror(errno));
+        return -1;
+    }
+
+    r = fread(d->ram, sizeof(WORD), RAM_SIZE, f);
+    TRACE("read %zu words", r);
+
+    if (ferror(f)) {
+        fprintf(stderr, "%s():%s\n", "fread", strerror(errno));
+    }
+
+    fclose(f);
+    return 0;
+}
+
+static void testprog_load(struct dcpu16 *d) {
+    static WORD bin[] = {
+        0x7c01, 0x0030, 0x7de1, 0x1000, 0x0020, 0x7803, 0x1000, 0xc00d,
+        0x7dc1, 0x001a, 0xa861, 0x7c01, 0x2000, 0x2161, 0x2000, 0x8463,
+        0x806d, 0x7dc1, 0x000d, 0x9031, 0x7c10, 0x0018, 0x7dc1, 0x001a,
+        0x9037, 0x61c1, 0x7dc1, 0x001a, 0x0000
+    };
+    size_t i;
+
+    printf("loading...\n");
+    for (i = 0; i < (sizeof(bin) / sizeof(WORD)); i++)
+    {
+        printf(" %04x", bin[i]);
+        d->ram[i] = bin[i];
+    }
+    printf("\nloaded 0x%04zx words\n", i - 1);
+}
+
+static
+void dump_cpu_state(struct dcpu16 *d) {
+    unsigned int i;
+
+    printf("[--- cycle:0x%08llx %2s:0x%04x %2s:0x%04x %2s:0x%04x\n",
+           d->cycle,
+           "PC", d->pc,
+           "SP", d->sp,
+           "O", d->o);
+    printf(" ");
+    for (i = 0; i < 8; i++)
+        printf("  %c:0x%04x", regnames_[i], d->reg[i]);
+    printf("\n");
+}
+
+static
+void dump_ram(struct dcpu16 *d, WORD start, WORD stop) {
+    unsigned int i, j;
+    const unsigned int n = 8; /* words per line */
+
+    for (i = start, j = 0; i <= stop; i++, j++) {
+        if (j % n == 0)
+            printf("0x%04x:\t", i);
+        printf(" %04x%s", d->ram[i], (j % n) == (n - 1) ? "\n" : "");
+    }
+}
+
+
+int main(int argc, char **argv) {
+    struct dcpu16 *m;
+    int c;
+    char buf[512];
+
+    m = calloc(1, sizeof *m);
+    if (m == NULL)
+    {
+        fprintf(stderr, "%s:%s\n", "calloc", strerror(errno));
+        exit(EX_OSERR);
+    }
+
+    while ( (c = getopt(argc, argv, "ht")) != EOF )
+    {
+        switch (c)
+        {
+            case 't':
+            trace_mode_ = 1;
+            dump_ram(m, 0, 0x001f);
+            testprog_load(m);
+            dump_ram(m, 0, 0x001f);
+            break;
+
+            case 'h':
+            usage(argv[0], 1);
+            exit(EX_OK);
+
+            default:
+            usage(argv[0], 0);
+            exit(EX_USAGE);
+        }
+    }
+
+    if (argc - optind)
+    {
+        /* read file */
+        file_load(m, argv[optind]);
+    }
+
+    dump_cpu_state(m);
+    while (fgets(buf, sizeof buf, stdin)) {
+        dcpu16_execute_next_instruction(m);
+        dump_cpu_state(m);
+        if (trace_mode_)
+            dump_instruction(m, m->pc);
+    }
+
+    free(m);
+
+    exit(EX_OK);
+}