X-Git-Url: http://git.squeep.com/?p=dcpu16;a=blobdiff_plain;f=dcpu16.c;h=6f5617ccff86cdd76accf814a68d77bbb011edbe;hp=d0d3b40dbff499c2265750c26b710016bc445416;hb=a44fc6f0eb899b6f745fe8ab43d5cb3e5b743c21;hpb=95a676586942a2ddd1040503754d05ca91f6e3a7 diff --git a/dcpu16.c b/dcpu16.c index d0d3b40..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 * @@ -17,69 +19,70 @@ * Justin Wind * 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; /* */ - WORD ram[RAM_SIZE]; /* memory */ -}; -static unsigned int trace_mode_ = 0; /* turn on for overly verbose internals */ +static unsigned int trace_mode_ = 0; /* spew 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, ...) { +#define WARN(...) do { if (warn_cb_) warn_cb_(__VA_ARGS__); } while (0) +static inline void warn_(char *fmt, ...) __attribute__((format(printf, 1, 2))); +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); fprintf(stderr, "\n"); fflush(stderr); } +static void (*warn_cb_)(char *fmt, ...) = warn_; +void dcpu16_warn_cb_set(void (*fn)(char *fmt, ...)) { + warn_cb_ = fn; +} - -#define TRACE(...) do { if (trace_mode_) trace(__VA_ARGS__); } while (0) -static inline void trace(char *fmt, ...) __attribute__((format(printf, 1, 2))); +#define TRACE(...) do { if (trace_cb_ && trace_mode_) trace_cb_(__VA_ARGS__); } while (0) +static inline void trace_(char *fmt, ...) __attribute__((format(printf, 1, 2))); static inline -void trace(char *fmt, ...) { +void trace_(char *fmt, ...) { va_list ap; + fprintf(stdout, "[debug] "); va_start(ap, fmt); vfprintf(stdout, fmt, ap); va_end(ap); fprintf(stdout, "\n"); fflush(stdout); } +static void (*trace_cb_)(char *fmt, ...) = trace_; +void dcpu16_trace_cb_set(void (*fn)(char *fmt, ...)) { + trace_cb_ = fn; +} -/* 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; - assert(value >= 0x00 && value <= 0x3f); + assert(value <= 0x3f); /* does this value indicate a literal */ if (value >= 0x1f) @@ -200,12 +203,11 @@ struct opcode_entry { #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_NBI_ (void)val_b, (void)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_) {\ @@ -214,33 +216,8 @@ struct opcode_entry { 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) +#define OP_BASIC(x) OP_TYPE(OP_BASIC_) +#define OP_NBI(x) OP_TYPE(OP_NBI_) /* extended opcodes */ @@ -293,7 +270,6 @@ static const struct opcode_entry opcode_nbi_entries[] = { instructions only have one operand. */ OP_IMPL(_nbi_) { - assert(d != NULL); /* non-basic instruction */ /* don't do normal value decoding here */ @@ -554,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; @@ -563,11 +539,11 @@ void dump_instruction(struct dcpu16 *d, WORD addr) { 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); + assert(opcode <= 0x0f); + assert(a <= 0x3f); + assert(b <= 0x3f); - printf("next instr 0x%04x: %04x", addr, d->ram[addr]); + printf(" next instr 0x%04x: %04x", addr, d->ram[addr]); if (opcode != 0) { @@ -585,7 +561,11 @@ void dump_instruction(struct dcpu16 *d, WORD addr) { 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); + + printf("%s%s ; %s", + instr_len < 3 ? " " : "", + instr_len < 2 ? " " : "", + e->name); if (opcode != 0) { dump_value(a, d->ram[addr + 1]); if (a == 0x1e || a == 0x1f) @@ -598,16 +578,12 @@ 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; - /* fetch next instruction */ - if (d->pc > RAM_SIZE) { /* currently impossible */ - WARN("%s beyond %u", "PC", RAM_SIZE); - /* d->pc %= RAM_SIZE; */ - } + /* decode opcode and invoke */ 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); @@ -624,73 +600,10 @@ 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"); - - 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("---- cycle:0x%08llx %2s:0x%04x %2s:0x%04x %2s:0x%04x\n", d->cycle, "PC", d->pc, "SP", d->sp, @@ -701,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 */ @@ -713,55 +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, "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); - } + 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; }