separated vm-dcpu16.c cli driver wrapper from vm emulator core
authorJustin Wind <justin.wind@gmail.com>
Wed, 11 Apr 2012 03:28:35 +0000 (20:28 -0700)
committerJustin Wind <justin.wind@gmail.com>
Wed, 11 Apr 2012 03:28:35 +0000 (20:28 -0700)
.gitignore
Makefile
as-dcpu16.c
dcpu16.c
dcpu16.h
vm-dcpu16.c [new file with mode: 0644]

index c774e035b18cb85ee515b57a14881dc455f4812f..73763e4900587e77af21787ad579a97621cdf064 100644 (file)
@@ -1,3 +1,3 @@
 as-dcpu16
-dcpu16
+vm-dcpu16
 *.o
index 78f7941e97ac6c8879efcc87610d227e54ef14c5..ad94a65a6dcd3f1aadad04f7c551a78a69564b47 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,12 +1,13 @@
 #!make
 #
-PROGRAMS = dcpu16 as-dcpu16
+PROGRAMS = as-dcpu16 vm-dcpu16
 
 CFLAGS = -g -Wall -Wextra -pedantic -std=c99
-LDFLAGS = 
+LDFLAGS = -lreadline
 
 all:   $(PROGRAMS)
 
+vm-dcpu16: vm-dcpu16.o dcpu16.o
 
 as-dcpu16: as-dcpu16.o
 
index c89d1030816bdec4f9695fefddae34d01d87b11c..c61884232c573a3469890df36c251f2f7d154b53 100644 (file)
 /*
  *  quick and dirty assembler for dcpu16
  *  
+ *  Justin Wind <justin.wind@gmail.com>
+ *    2012 04 07 - implementation started
+ *    2012 04 10 - functional
+ *
  *  TODO
  *    needs ability to specify location for code or data
  */
index 26c594e02b60e524f596afcda0490d8e697456e2..6f5617ccff86cdd76accf814a68d77bbb011edbe 100644 (file)
--- a/dcpu16.c
+++ b/dcpu16.c
@@ -1,5 +1,5 @@
-#include <stdio.h>
 #include <stdlib.h>
+#include <stdio.h>
 #include <stdarg.h>
 #include <unistd.h>
 #include <string.h>
@@ -7,6 +7,8 @@
 #include <assert.h>
 #include <sysexits.h>
 
+#include "dcpu16.h"
+
 /*
  *  emulates the DCPU16 system from http://0x10c.com/doc/dcpu-16.txt
  *  
  *    2012 04 05 - implementation started
  *    2012 04 06 - first functionality achieved
  *    2012 04 09 - minor cleanups
+ *    2012 04 10 - moved cli to separate module
  *
  *  TODO
- *    move cli driver to separate module
  *    drop checks for assigning to literals -- it won't affect anything anyhow
  *    debug short literal decoding
  */
 
 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;     /* skip execution of next instruction */
-    WORD ram[RAM_SIZE];         /* memory */
-};
 
 
 static unsigned int trace_mode_ = 0; /* spew overly verbose internals */
@@ -53,7 +42,7 @@ static inline
 void warn_(char *fmt, ...) {
     va_list ap;
 
-    fprintf(stderr, "!!! ");
+    fprintf(stderr, "[warning] ");
     va_start(ap, fmt);
     vfprintf(stderr, fmt, ap);
     va_end(ap);
@@ -71,6 +60,7 @@ static inline
 void trace_(char *fmt, ...) {
     va_list ap;
 
+    fprintf(stdout, "[debug] ");
     va_start(ap, fmt);
     vfprintf(stdout, fmt, ap);
     va_end(ap);
@@ -83,10 +73,11 @@ void dcpu16_trace_cb_set(void (*fn)(char *fmt, ...)) {
 }
 
 
-
-/* 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) */
+/*  value_decode_
+ * 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;
@@ -539,7 +530,7 @@ void dump_value(WORD value, WORD nextword) {
     }
 }
 
-void dump_instruction(struct dcpu16 *d, WORD addr) {
+void dcpu16_disassemble_print(struct dcpu16 *d, WORD addr) {
     WORD opcode, a, b;
     unsigned int instr_len = 1;
     const struct opcode_entry *e;
@@ -587,7 +578,7 @@ void dump_instruction(struct dcpu16 *d, WORD addr) {
     printf("\n");
 }
 
-void dcpu16_execute_next_instruction(struct dcpu16 *d) {
+void dcpu16_step(struct dcpu16 *d) {
     WORD opcode;
     WORD val_a, val_b;
     const struct opcode_entry *e;
@@ -609,72 +600,7 @@ void dcpu16_execute_next_instruction(struct dcpu16 *d) {
     }
 }
 
-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-t -- test mode, load demo program\n"
-                   "\t-v -- verbose execution tracing\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) {
+void dcpu16_state_print(struct dcpu16 *d) {
     unsigned int i;
 
     printf("---- cycle:0x%08llx %2s:0x%04x %2s:0x%04x %2s:0x%04x\n",
@@ -688,8 +614,7 @@ void dump_cpu_state(struct dcpu16 *d) {
     printf("\n");
 }
 
-static
-void dump_ram(struct dcpu16 *d, WORD start, WORD stop) {
+void dcpu16_dump_ram(struct dcpu16 *d, WORD start, WORD stop) {
     unsigned int i, j;
     const unsigned int n = 8; /* words per line */
 
@@ -700,58 +625,23 @@ void dump_ram(struct dcpu16 *d, WORD start, WORD stop) {
     }
 }
 
+/*  dcpu16_new
+ *  allocate a new dcpu16 instance
+ */
+struct dcpu16 *dcpu16_new(void) {
+    struct dcpu16 *vm;
 
-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, "htv")) != EOF )
-    {
-        switch (c)
-        {
-            case 'v':
-            trace_mode_ = 1;
-            break;
-
-            case 't':
-            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);
-    dump_instruction(m, m->pc);
-    while (fgets(buf, sizeof buf, stdin)) {
-        dcpu16_execute_next_instruction(m);
-        dump_cpu_state(m);
-        dump_instruction(m, m->pc);
-    }
+    vm = calloc(1, sizeof *vm);
+    if (vm == NULL)
+        WARN("%s: %s(%zu): %s", __func__, "calloc", strerror(errno));
 
-    free(m);
+    return vm;
+}
 
-    exit(EX_OK);
+/*  dcpu16_delete
+ *  release a dcpu16 instance
+ */
+void dcpu16_delete(struct dcpu16 **vm) {
+    free(*vm);
+    *vm = NULL;
 }
index c9510c25ddda35042f599c46af7fce5f0ac30203..f0e4d8b54abb2d19ead94757e3858949d7819cf8 100644 (file)
--- a/dcpu16.h
+++ b/dcpu16.h
@@ -1,6 +1,45 @@
 #ifndef DCPU16_H_3XXIQQG2
 #define DCPU16_H_3XXIQQG2
 
+/* the target system's concept of a word */
 typedef unsigned short DCPU16_WORD;
 
+/* how much ram the system has */
+#define DCPU16_RAM 0x10000
+
+/* a self-contained dcpu16 core */
+struct dcpu16 {
+    unsigned long long cycle;   /* number of cycles this core has executed */
+    DCPU16_WORD reg[8];         /* system registers, a b c x y z i j */
+    DCPU16_WORD pc;             /* program counter */
+    DCPU16_WORD sp;             /* stack pointer */
+    DCPU16_WORD o;              /* overflow */
+    unsigned int skip_ : 1;     /* skip execution of next instruction */
+    DCPU16_WORD ram[DCPU16_RAM];  /* memory */
+    DCPU16_WORD reg_work_[2];   /* (private) work registers for holding literal values while decoding instructions */
+};
+
+/* instantiate a new core */
+struct dcpu16 *dcpu16_new(void);
+
+/* print the current state of a core */
+void dcpu16_state_print(struct dcpu16 *);
+
+/* print the contents of ram from second to third argument */
+void dcpu16_dump_ram(struct dcpu16 *, DCPU16_WORD, DCPU16_WORD);
+
+/* print the instruction at the specified address */
+void dcpu16_disassemble_print(struct dcpu16 *, DCPU16_WORD);
+
+
+/* execute the next instruction */
+void dcpu16_step(struct dcpu16 *);
+
+/* release a core */
+void dcpu16_delete(struct dcpu16 **);
+
+/* register callbacks to handle warning and debug messages, defaults to writing to stderr */
+void dcpu16_warn_cb_set(void (*)(char *, ...));
+void dcpu16_trace_cb_set(void (*)(char *, ...));
+
 #endif /* DCPU16_H_3XXIQQG2 */
diff --git a/vm-dcpu16.c b/vm-dcpu16.c
new file mode 100644 (file)
index 0000000..c3f45ea
--- /dev/null
@@ -0,0 +1,133 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <sysexits.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include "dcpu16.h"
+
+/*
+ *  cli driver for dcpu16 core
+ *  
+ *  Justin Wind <justin.wind@gmail.com>
+ *    2012 04 10 - implementation started
+ *
+ */
+
+static const char * const src_id_ = "$Id$";
+
+/* global invocation options */
+struct options {
+    unsigned int verbose;
+} opt_ = {
+    .verbose = 0,
+};
+
+#define VERBOSE_PRINTF(...) do { if (opt_.verbose) printf(__VA_ARGS__); } while (0)
+
+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 [file]\n",
+            prog);
+
+    if (full) {
+        fprintf(f, "\nOptions:\n"
+                "\t [file] -- ram image to load initially\n"
+                "\t -h -- this screen\n"
+                "\t -v -- verbose execution tracing\n");
+
+        fprintf(f, "\n%78s\n", src_id_);
+    }
+}
+
+static
+int file_load_(struct dcpu16 *vm, 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(vm->ram, sizeof(DCPU16_WORD), DCPU16_RAM, f);
+    VERBOSE_PRINTF("read %zu words", r);
+
+    if (ferror(f))
+        fprintf(stderr, "%s('%s'):%s\n", "fread", filename, strerror(errno));
+
+    fclose(f);
+    return 0;
+}
+
+int main(int argc, char **argv) {
+    int c;
+    char *command;
+    char *prompt = "dcpu16> ";
+    struct dcpu16 *vm;
+
+    while ( (c = getopt(argc, argv, "hv")) != EOF) {
+        switch (c) {
+            case 'v':
+                opt_.verbose++;
+                break;
+
+            case 'h':
+                usage_(argv[0], 1);
+                exit(EX_OK);
+
+            default:
+                usage_(argv[0], 0);
+                exit(EX_USAGE);
+        }
+    }
+    if (opt_.verbose < 1) {
+        dcpu16_warn_cb_set(NULL);
+        dcpu16_trace_cb_set(NULL);
+    } else if (opt_.verbose < 2) {
+        dcpu16_trace_cb_set(NULL);
+    }
+    argc -= optind;
+    argv += optind;
+
+    if ((vm = dcpu16_new()) == NULL) {
+        fprintf(stderr, "could not allocate new dcpu instance\n");
+        exit(EX_UNAVAILABLE);
+    }
+
+    if (argc) {
+        file_load_(vm, *argv);
+    }
+
+    dcpu16_state_print(vm);
+    dcpu16_disassemble_print(vm, vm->pc);
+    while ( (command = readline(prompt)) ) {
+        if (strcasecmp(command, "quit") == 0)
+            break;
+
+        dcpu16_step(vm);
+        dcpu16_state_print(vm);
+        dcpu16_disassemble_print(vm, vm->pc);
+    }
+
+    printf("finished\n");
+
+    dcpu16_delete(&vm);
+
+    exit(EX_OK);
+}