-#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
*
* Justin Wind <justin.wind@gmail.com>
* 2012 04 05 - implementation started
* 2012 04 06 - first functionality achieved
+ * 2012 04 09 - minor cleanups
+ * 2012 04 10 - moved cli to separate module
+ * 2012 04 12 - added basic callback support for address accesses
*
* TODO
- * move cli driver to separate module
+ * change api to print into buffers rather than stdio
+ * refactor opcode functiontables into switch statements
+ * let callbacks determine whether to override events, or just observe
+ * sort init callbacks by base addr, to call in-order
*/
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 */
-};
-
+#define OPCODE_BASIC_BITS 4
+#define OPCODE_OPERAND_BITS 6
-static unsigned int trace_mode_ = 0; /* turn on for overly verbose internals */
+static const char * const regnames_ = "ABCXYZIJ";
-#define WARN(...) warn(__VA_ARGS__)
-static inline void warn(char *fmt, ...) __attribute__((format(printf, 1, 2)));
-static inline void warn(char *fmt, ...) {
+/* some default warning and debug reporting functions, which can be overridden by clients */
+#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)));
+#ifdef DEBUG
+#define TRACE(...) do { if (trace_cb_) 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);
}
+#else /* DEBUG */
+#define TRACE(...) do {} while(0)
+#endif /* DEBUG */
+static void (*trace_cb_)(char *fmt, ...) =
+#ifdef DEBUG
+ trace_
+#else /* DEBUG */
+ NULL
+#endif
+ ;
+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) */
-static unsigned int value_decode(struct dcpu16 *d, WORD value, WORD *work_v, WORD **v) {
- WORD nextword;
+/* acct_event_
+ * invokes callbacks for specified event
+ */
+static inline
+void acct_event_(struct dcpu16 *vm, dcpu16_acct_event ev, DCPU16_WORD addr) {
+ struct dcpu16_acct_cb *cb = vm->cb_table_;
+ size_t i;
+
+ for (i = 0; i < vm->cb_table_entries_; i++) {
+ if ( (cb[i].mask & ev) )
+ cb[i].fn(vm, ev, addr, cb[i].data);
+ }
+}
+
+
+/* value_decode_
+ * sets *v to be the destination of the value
+ * advances d->pc if necessary
+ * workv is buffer to use to accumulate literal value before use, one exists for either potential instruction operand
+ * e_addr is for accounting callback
+ * returns true if destination points to literal (id est *v should ignore writes)
+ */
+static
+unsigned int value_decode_(struct dcpu16 *d, DCPU16_WORD value, DCPU16_WORD *work_v, DCPU16_WORD **v, DCPU16_WORD *e_addr) {
+ DCPU16_WORD nextword;
unsigned int retval = 0;
- assert(value >= 0x00 && value <= 0x3f);
+ assert(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&0x07],
d->reg[value&0x07],
**v);
+ *e_addr = d->reg[(value & 0x07)];
} else if (value <= 0x17) { /* [next word + register] */
nextword = d->ram[ d->pc++ ];
nextword,
d->reg[(value & 0x07)],
**v);
+ *e_addr = nextword + d->reg[(value & 0x07)];
} else switch (value) {
case 0x18: /* POP / [sp++] */
TRACE(">> POP [0x%04x] (0x%04x)",
d->sp - 1,
**v);
+ *e_addr = d->sp - 1;
break;
case 0x19: /* PEEK / [sp] */
TRACE(">> PEEK [0x%04x] (0x%04x)",
d->sp,
**v);
+ *e_addr = d->sp;
break;
case 0x1a: /* PUSH / [--sp] */
TRACE(">> PUSH [0x%04x] (0x%04x)",
d->sp + 1,
**v);
+ *e_addr = d->sp + 1;
break;
case 0x1b: /* SP */
TRACE(">> [nextword] [0x%04x] (0x%04x)",
nextword,
**v);
+ *e_addr = nextword;
break;
case 0x1f: /* next word (literal) / [pc++] */
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);
+ void (*impl)(struct dcpu16 *, DCPU16_WORD, DCPU16_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_IMPL(x) static void op_##x(struct dcpu16 *d, DCPU16_WORD val_a, DCPU16_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;\
+#define OP_TYPE(op_type) DCPU16_WORD *a, *b;\
unsigned int lit_a;\
+ DCPU16_WORD ev_a_addr = 0, ev_b_addr = 0;\
do {\
- assert(d != NULL);\
- lit_a = value_decode(d, val_a, &d->reg_work_[0], &a);\
+ lit_a = value_decode_(d, val_a, &d->reg_work_[0], &a, &ev_a_addr);\
op_type;\
- if (d->skip_) {\
- TRACE("++ SKIPPED");\
- d->skip_ = 0;\
- return;\
- }\
} while (0)
+#define OP_NBI_ (void)val_b, (void)b, (void)ev_b_addr
+#define OP_BASIC_ (void)value_decode_(d, val_b, &d->reg_work_[0], &b, &ev_b_addr)
+#define OP_BASIC(x) OP_TYPE(OP_BASIC_)
+#define OP_NBI(x) OP_TYPE(OP_NBI_)
-
-#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)
-
+/*
+ accounting helpers, these fire off the related callbacks for memory reads,
+ memory writes, and execution of reserved instructions
+ */
+#define ACCT_R(addr) do { acct_event_(d, DCPU16_ACCT_EV_READ, addr); } while (0)
+#define ACCT_W(addr) do { acct_event_(d, DCPU16_ACCT_EV_WRITE, addr); } while (0)
+#define ACCT_ILL(addr) do { acct_event_(d, DCPU16_ACCT_EV_NOP, addr); } while (0)
/* extended opcodes */
OP_NBI(nbi__reserved_);
/* reserved for future expansion */
- WORD future_opcode = (d->ram[d->pc] >> OPCODE_FUTURE_SHIFT);
+ DCPU16_WORD future_opcode = (d->ram[d->pc] >> (OPCODE_BASIC_BITS + OPCODE_OPERAND_BITS));
WARN("reserved future opcode 0x%04x invoked", future_opcode);
+
+ ACCT_ILL(d->pc);
}
OP_IMPL(nbi_jsr) {
OP_NBI(nbi_jsr);
/* pushes the address of the next instruction to the stack, then sets PC to a */
+ ACCT_R(ev_a_addr);
+
d->ram[ --d->sp ] = d->pc;
d->pc = *a;
d->cycle += 2;
+
+ ACCT_W(d->sp + 1);
}
OP_IMPL(nbi__reserved2_) {
/* reserved */
WARN("reserved nbi opcode invoked");
+
+ ACCT_ILL(d->pc);
}
static const struct opcode_entry opcode_nbi_entries[] = {
/* basic opcodes */
/*
- N.B. the following function does not decode values, as the nbi
- instructions only have one operand.
+ N.B. the following function does not decode values, (thus does not advance pc &c)
+ Decoding is handled by the opcode functions it calls.
*/
OP_IMPL(_nbi_) {
- assert(d != NULL);
/* non-basic instruction */
/* don't do normal value decoding here */
- WORD nbi_opcode = val_a;
+ DCPU16_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) );
OP_BASIC(set);
/* sets a to b */
- /* only set non-literal target */
- if (val_a < 0x1f) {
- *a = *b;
- }
+ ACCT_R(ev_b_addr);
+
+ /*
+ if a is a literal, it's aimed at a scratch register,
+ so it's fine to update, as it won't have any effect.
+ */
+ *a = *b;
d->cycle += 1;
+
+ ACCT_W(ev_a_addr);
}
OP_IMPL(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;
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
+
+ *a = acc;
+ d->o = (acc > 0xffff);
d->cycle += 2;
+
+ ACCT_W(ev_a_addr);
}
OP_IMPL(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;
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
+
+ *a = acc;
+ d->o = (acc > 0xffff);
d->cycle += 2;
+
+ ACCT_W(ev_a_addr);
}
OP_IMPL(mul) {
/* sets a to a*b, sets O to ((a*b)>>16)&0xffff */
unsigned int acc = *a * *b;
- if (val_a < 0x1f) {
- *a = acc;
- }
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
+
+ *a = acc;
d->o = acc >> 16;
+
d->cycle += 2;
+
+ ACCT_W(ev_a_addr);
}
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. */
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
+
if (*b == 0) {
- if (val_a < 0x1f) {
- *a = 0;
- }
+ *a = 0;
d->o = 0;
} else {
- unsigned int acc = *a / *b;
-
- if (val_a < 0x1f) {
- *a = acc;
- }
-
- acc = (*a << 16) / *b;
- d->o = acc;
+ *a = *a / *b;
+ d->o = (*a << 16) / *b;
}
d->cycle += 3;
+
+ ACCT_W(ev_a_addr);
}
OP_IMPL(mod) {
OP_BASIC(mod);
/* sets a to a%b. if b==0, sets a to 0 instead. */
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
+
if (*b == 0) {
- if (val_a < 0x1f) {
- *a = 0;
- }
+ *a = 0;
} else {
- if (val_a < 0x1f) {
- *a = *a % *b;
- }
+ *a = *a % *b;
}
d->cycle += 3;
+
+ ACCT_W(ev_a_addr);
}
OP_IMPL(shl) {
/* sets a to a<<b, sets O to ((a<<b)>>16)&0xffff */
unsigned int acc = *a << *b;
- if (val_a < 0x1f) {
- *a = acc;
- }
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
+
+ *a = acc;
+
d->o = acc >> 16;
d->cycle += 2;
+
+ ACCT_W(ev_a_addr);
}
OP_IMPL(shr) {
/* sets a to a>>b, sets O to ((a<<16)>>b)&0xffff */
unsigned int acc = *a >> *b;
- if (val_a < 0x1f) {
- *a = acc;
- }
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
+
+ *a = acc;
d->o = (*a << 16) >> *b;
d->cycle += 2;
+
+ ACCT_W(ev_a_addr);
}
OP_IMPL(and) {
OP_BASIC(and);
/* sets a to a&b */
- if (val_a < 0x1f) {
- *a = *a & *b;
- }
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
+
+ *a = *a & *b;
d->cycle += 1;
+
+ ACCT_W(ev_a_addr);
}
OP_IMPL(bor) {
OP_BASIC(bor);
/* sets a to a|b */
- if (val_a < 0x1f) {
- *a = *a | *b;
- }
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
+
+ *a = *a | *b;
d->cycle += 1;
+
+ ACCT_W(ev_a_addr);
}
OP_IMPL(xor) {
OP_BASIC(xor);
/* sets a to a^b */
- if (val_a < 0x1f) {
- *a = *a ^ *b;
- }
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
+
+ *a = *a ^ *b;
d->cycle += 1;
+
+ ACCT_W(ev_a_addr);
}
OP_IMPL(ife) {
OP_BASIC(ife);
/* performs next instruction only if a==b */
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
+
if (*a == *b) {
/* */
} else {
OP_BASIC(ifn);
/* performs next instruction only if a!=b */
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
+
if (*a != *b) {
/* */
} else {
OP_BASIC(ifg);
/* performs next instruction only if a>b */
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
+
if (*a > *b) {
/* */
} else {
OP_BASIC(ifb);
/* performs next instruction only if (a&b)!=0 */
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
+
if ((*a & *b) != 0) {
/* */
} else {
{0x0, "", NULL }
};
-void dump_value(WORD value, WORD nextword) {
- if (value < 0x07) {
+static inline
+void dump_operand_value_(DCPU16_WORD value, DCPU16_WORD nextword) {
+ if (value <= 0x07) {
printf(" %c", regnames_[value]);
- } else if (value < 0x0f) {
+ } else if (value <= 0x0f) {
printf(" [%c]", regnames_[value & 0x07]);
- } else if (value < 0x17) {
+ } else if (value <= 0x17) {
printf(" [0x%04x + %c]", nextword, regnames_[value & 0x07]);
} else switch (value) {
case 0x18: printf(" POP"); break;
}
}
-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]);
+/* split a word into the parts of an instruction, and determine how many words it takes up in total */
+static inline
+void instruction_decode_(struct dcpu16 *d, DCPU16_WORD addr, DCPU16_WORD *opcode, DCPU16_WORD *a, DCPU16_WORD *b, DCPU16_WORD *instr_len) {
+ *opcode = d->ram[addr] & ((1 << OPCODE_BASIC_BITS) - 1);
+ *a = (d->ram[addr] >> OPCODE_BASIC_BITS) & ((1 << OPCODE_OPERAND_BITS) - 1);
+ *b = (d->ram[addr] >> (OPCODE_BASIC_BITS + OPCODE_OPERAND_BITS)) & ((1 << OPCODE_OPERAND_BITS) - 1);
+ if (instr_len) {
+ *instr_len = 1;
+ /* both basic and nbi opcodes use their b operand */
+ if ( (*b >= 0x10 && *b <= 0x17) || *b == 0x1e || *b == 0x1f )
+ *instr_len += 1;
+ /* but only basic uses a */
+ if (*opcode
+ && ((*a >= 0x10 && *a <= 0x17) || *a == 0x1e || *a == 0x1f) )
+ *instr_len += 1;
+ }
+}
- if (opcode != 0)
- {
- if (a == 0x1e || a == 0x1f) {
- printf(" %04x", d->ram[addr + instr_len]);
- instr_len++;
+/* dcpu16_disassemble_print
+ print the words of the instruction at addr, followed by its assembly representation
+ returns the length of the instruction in words
+ */
+DCPU16_WORD dcpu16_disassemble_print(struct dcpu16 *d, DCPU16_WORD addr) {
+ DCPU16_WORD opcode, a, b, instr_len, i;
+ const struct opcode_entry *e;
+ unsigned int indent = 0;
+ unsigned int partial = 0;
+
+ if (!d) return 0;
+
+ /*
+ Check the previous instruction, to see if this one should be
+ indented. This check isn't foolproof, as preceeding addresses
+ could be data which happen to match instructions..
+ */
+ for (i = 3; i; i--) {
+ instruction_decode_(d, addr - i, &opcode, &a, &b, &instr_len);
+ if (instr_len > i)
+ partial++;
+ if (instr_len == i && opcode >= 0xc) {
+ indent++;
+ break;
}
}
- if (b == 0x1e || b == 0x1f) {
- printf(" %04x", d->ram[addr + instr_len]);
- instr_len++;
- }
+
+ /* now get what we're really interested in */
+ instruction_decode_(d, addr, &opcode, &a, &b, &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)
+
+ /* show the raw words */
+ printf("%04x", d->ram[addr]);
+ for (i = 1; i < instr_len; i++) {
+ printf(" %04x", d->ram[addr + i]);
+ }
+
+ /* align things neatly, show the instruction */
+ printf("%s%s ;%s%s%s",
+ instr_len < 3 ? " " : "",
+ instr_len < 2 ? " " : "",
+ partial ? "*" : " ",
+ indent ? " " : "",
+ e->name);
+
+ /* show the operands */
+ i = 0;
+ if (opcode) {
+ dump_operand_value_(a, d->ram[addr + 1]);
+ if ((a >= 0x10 && a <= 0x17) || a == 0x1e || a == 0x1f)
addr++;
printf(",");
}
- dump_value(b, d->ram[addr + 1]);
+ if (opcode || a)
+ dump_operand_value_(b, d->ram[addr + 1]);
- printf("\n");
+ return instr_len;
}
-void dcpu16_execute_next_instruction(struct dcpu16 *d) {
- WORD opcode;
- WORD val_a, val_b;
+/* execute the next instruction */
+void dcpu16_step(struct dcpu16 *d) {
+ DCPU16_WORD opcode, a, b, instr_len;
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; */
- }
+ if (!d) return;
+
+ acct_event_(d, DCPU16_ACCT_EV_CYCLE, d->pc);
- 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);
+ /*
+ PC is advanced while decoding the operands by the opcode functions.
+ Things like this could be organized a little better..
+ */
+ instruction_decode_(d, d->pc, &opcode, &a, &b, NULL);
- d->pc++;
+ d->pc++; /* all instructions take at least one word */
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);
+ TRACE(">> %s 0x%04x, 0x%04x", e->name, a, b);
+ e->impl(d, a, 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));
+ /* and jump over next instr if needed */
+ if (d->skip_) {
+ instruction_decode_(d, d->pc, &opcode, &a, &b, &instr_len);
+ d->pc += instr_len;
+ d->skip_ = 0;
+ TRACE("++ SKIPPED %x words", instr_len);
}
-
- 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;
+/*
+ print the current state of the machine
+ shows current cycle count, registers, and next instruction
+*/
+void dcpu16_state_print(struct dcpu16 *d) {
+ unsigned int 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);
-}
+ if (!d) return;
-static
-void dump_cpu_state(struct dcpu16 *d) {
- unsigned int i;
+ printf(" ");
+ for (i = 0; i < 8; i++)
+ printf(" %c:0x%04x", regnames_[i], d->reg[i]);
+ printf("\n");
- printf("[--- cycle:0x%08llx %2s:0x%04x %2s:0x%04x %2s:0x%04x\n",
+ printf("(0x%08llx) %2s:0x%04x %2s:0x%04x %2s:0x%04x [%2s]:",
d->cycle,
- "PC", d->pc,
+ "O", d->o,
"SP", d->sp,
- "O", d->o);
- printf(" ");
- for (i = 0; i < 8; i++)
- printf(" %c:0x%04x", regnames_[i], d->reg[i]);
+ "PC", d->pc,
+ "PC");
+
+ dcpu16_disassemble_print(d, d->pc);
printf("\n");
}
-static
-void dump_ram(struct dcpu16 *d, WORD start, WORD stop) {
+/* dcpu16_dump_ram
+ * print raw ram contents from start to stop
+ */
+void dcpu16_dump_ram(struct dcpu16 *d, DCPU16_WORD start, DCPU16_WORD end) {
unsigned int i, j;
const unsigned int n = 8; /* words per line */
- for (i = start, j = 0; i <= stop; i++, j++) {
+ if (!d) return;
+
+ for (i = start, j = 0; i <= end; i++, j++) {
if (j % n == 0)
printf("0x%04x:\t", i);
printf(" %04x%s", d->ram[i], (j % n) == (n - 1) ? "\n" : "");
}
+ if ((j % n) != (n - 1))
+ printf("\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);
+/* dcpu16_acct_add
+ * Register callback fn to be triggered whenever event matching any events
+ * in bitwise mask occur.
+ */
+int dcpu16_acct_add(struct dcpu16 *vm, dcpu16_acct_event mask, dcpu16_ev_cb_t *fn, void *data) {
+ struct dcpu16_acct_cb cb;
+
+ cb.mask = mask;
+ cb.fn = fn;
+ cb.data = data;
+
+ if (vm->cb_table_entries_ == vm->cb_table_allocated_) {
+ size_t new_entries = vm->cb_table_allocated_ + 32;
+ void *tmp_ptr = realloc(vm->cb_table_, new_entries * sizeof *(vm->cb_table_));
+ if (tmp_ptr == NULL) {
+ fprintf(stderr, "%s():%s", "realloc", strerror(errno));
+ return -1;
+ }
+ vm->cb_table_ = tmp_ptr;
+ vm->cb_table_allocated_ += 32;
}
- 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;
+ memcpy(vm->cb_table_ + vm->cb_table_entries_, &cb, sizeof cb);
+ vm->cb_table_entries_++;
- case 'h':
- usage(argv[0], 1);
- exit(EX_OK);
+ return 0;
+}
- default:
- usage(argv[0], 0);
- exit(EX_USAGE);
- }
- }
+/* dcpu16_reset
+ * signals cpu to reset, clearing runstate and ram, then reload any init callbacks
+ */
+void dcpu16_reset(struct dcpu16 *d) {
+ if (!d) return;
+
+ d->cycle = 0;
+ memset(d->reg, 0, sizeof d->reg);
+ d->pc = 0;
+ d->sp = 0;
+ d->o = 0;
+ d->skip_ = 0;
+ memset(d->ram, 0, sizeof d->ram);
+
+ acct_event_(d, DCPU16_ACCT_EV_RESET, 0);
+}
- if (argc - optind)
- {
- /* read file */
- file_load(m, argv[optind]);
- }
+/* dcpu16_new
+ * allocate a new dcpu16 instance
+ */
+struct dcpu16 *dcpu16_new(void) {
+ struct dcpu16 *vm;
- 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));
+
+ return vm;
+}
- free(m);
+/* dcpu16_delete
+ * release a dcpu16 instance
+ */
+void dcpu16_delete(struct dcpu16 **vm) {
+ if (!vm || !*vm) return;
- exit(EX_OK);
+ free(*vm);
+ *vm = NULL;
}