-#include <stdio.h>
#include <stdlib.h>
+#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#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 */
void warn_(char *fmt, ...) {
va_list ap;
- fprintf(stderr, "!!! ");
+ fprintf(stderr, "[warning] ");
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
void trace_(char *fmt, ...) {
va_list ap;
+ fprintf(stdout, "[debug] ");
va_start(ap, fmt);
vfprintf(stdout, fmt, ap);
va_end(ap);
}
-
-/* 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;
}
}
-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;
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;
}
}
-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",
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 */
}
}
+/* 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;
}
#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 */
--- /dev/null
+#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);
+}