From a44fc6f0eb899b6f745fe8ab43d5cb3e5b743c21 Mon Sep 17 00:00:00 2001 From: Justin Wind Date: Tue, 10 Apr 2012 20:28:35 -0700 Subject: [PATCH] separated vm-dcpu16.c cli driver wrapper from vm emulator core --- .gitignore | 2 +- Makefile | 5 +- as-dcpu16.c | 4 ++ dcpu16.c | 172 ++++++++++------------------------------------------ dcpu16.h | 39 ++++++++++++ vm-dcpu16.c | 133 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 211 insertions(+), 144 deletions(-) create mode 100644 vm-dcpu16.c diff --git a/.gitignore b/.gitignore index c774e03..73763e4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ as-dcpu16 -dcpu16 +vm-dcpu16 *.o diff --git a/Makefile b/Makefile index 78f7941..ad94a65 100644 --- 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 diff --git a/as-dcpu16.c b/as-dcpu16.c index c89d103..c618842 100644 --- a/as-dcpu16.c +++ b/as-dcpu16.c @@ -11,6 +11,10 @@ /* * quick and dirty assembler for dcpu16 * + * Justin Wind + * 2012 04 07 - implementation started + * 2012 04 10 - functional + * * TODO * needs ability to specify location for code or data */ diff --git a/dcpu16.c b/dcpu16.c index 26c594e..6f5617c 100644 --- a/dcpu16.c +++ b/dcpu16.c @@ -1,5 +1,5 @@ -#include #include +#include #include #include #include @@ -7,6 +7,8 @@ #include #include +#include "dcpu16.h" + /* * emulates the DCPU16 system from http://0x10c.com/doc/dcpu-16.txt * @@ -18,31 +20,18 @@ * 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; } diff --git a/dcpu16.h b/dcpu16.h index c9510c2..f0e4d8b 100644 --- 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 index 0000000..c3f45ea --- /dev/null +++ b/vm-dcpu16.c @@ -0,0 +1,133 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "dcpu16.h" + +/* + * cli driver for dcpu16 core + * + * Justin Wind + * 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); +} -- 2.45.2