* 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
* drop checks for assigning to literals -- it won't affect anything anyhow
* debug short literal decoding
- * add callbacks queues for set/get, attach your own filters
- * such as a display
*/
static const char * const src_id_ = "$Id$";
trace_cb_ = fn;
}
+
+/* dcpu16_acct_add
+ * Register callback fn to be triggered whenever event matching exactly mask_ev
+ * and additionally matching any of mask events occur.
+ */
+int dcpu16_acct_add(struct dcpu16 *vm, dcpu16_acct_event_ match_all, dcpu16_acct_event_ match_any, void (*fn)(dcpu16_acct_event_, DCPU16_WORD)) {
+ struct dcpu16_acct_cb cb;
+
+ cb.match_all = match_all;
+ cb.match_any = match_any;
+ cb.fn = fn;
+
+ /* add to vm->cb_table_, vm->cb_table_entries_, vm->cb_table_allocated_ */
+ 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;
+ }
+
+ memcpy(vm->cb_table_ + vm->cb_table_entries_, &cb, sizeof cb);
+ vm->cb_table_entries_++;
+
+ return 0;
+}
+
+/* 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;
+ size_t i;
+
+ for (i = 0; i < vm->cb_table_entries_; i++) {
+ if ( (cb[i].match_all & ev) == cb[i].match_all /* exact match on event flags */
+ && (cb[i].match_any & ev) ) { /* any match on rest */
+ cb[i].fn(ev, addr);
+ }
+ }
+}
+
/* 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) {
+static
+unsigned int value_decode(struct dcpu16 *d, WORD value, WORD *work_v, WORD **v, dcpu16_acct_event_ *e, WORD *e_addr) {
WORD nextword;
unsigned int retval = 0;
assert(value <= 0x3f);
+ *e = 0;
+
/* does this value indicate a literal */
if (value >= 0x1f)
retval = 1;
regnames_[value&0x07],
d->reg[value&0x07],
**v);
+ *e |= DCPU16_ACCT_RAM;
+ *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 |= DCPU16_ACCT_RAM;
+ *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 |= DCPU16_ACCT_RAM;
+ *e_addr = d->sp - 1;
break;
case 0x19: /* PEEK / [sp] */
TRACE(">> PEEK [0x%04x] (0x%04x)",
d->sp,
**v);
+ *e |= DCPU16_ACCT_RAM;
+ *e_addr = d->sp;
break;
case 0x1a: /* PUSH / [--sp] */
TRACE(">> PUSH [0x%04x] (0x%04x)",
d->sp + 1,
**v);
+ *e |= DCPU16_ACCT_RAM;
+ *e_addr = d->sp + 1;
break;
case 0x1b: /* SP */
TRACE(">> [nextword] [0x%04x] (0x%04x)",
nextword,
**v);
+ *e |= DCPU16_ACCT_RAM;
+ *e_addr = nextword;
break;
case 0x1f: /* next word (literal) / [pc++] */
#define OP_IMPL(x) static void op_##x(struct dcpu16 *d, WORD val_a, WORD 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_NBI_ (void)val_b, (void)b, (void)ev_b, (void)ev_b_addr
+#define OP_BASIC_ (void)value_decode(d, val_b, &d->reg_work_[0], &b, &ev_b, &ev_b_addr)
#define OP_TYPE(op_type) WORD *a, *b;\
unsigned int lit_a;\
+ dcpu16_acct_event_ ev_a = 0, ev_b = 0;\
+ WORD ev_a_addr = 0, ev_b_addr = 0;\
do {\
- 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, &ev_a_addr);\
op_type;\
if (d->skip_) {\
TRACE("++ SKIPPED");\
#define OP_BASIC(x) OP_TYPE(OP_BASIC_)
#define OP_NBI(x) OP_TYPE(OP_NBI_)
+/* accounting helpers */
+#define ACCT_R(ev) do { if (ev) { acct_event_(d, ev | DCPU16_ACCT_EV_READ, ev##_addr); } } while (0)
+#define ACCT_W(ev) do { if (ev) { acct_event_(d, ev | DCPU16_ACCT_EV_WRITE, ev##_addr); } } while (0)
/* extended opcodes */
OP_NBI(nbi_jsr);
/* pushes the address of the next instruction to the stack, then sets PC to a */
+ ACCT_R(ev_a);
+
d->ram[ --d->sp ] = d->pc;
d->pc = *a;
OP_BASIC(set);
/* sets a to b */
+ ACCT_R(ev_b);
+ ACCT_W(ev_a);
+
/* only set non-literal target */
if (val_a < 0x1f) {
*a = *b;
/* sets a to a+b, sets O to 0x0001 if there's an overflow, 0x0 otherwise */
unsigned int acc = *a + *b;
+ ACCT_R(ev_b);
+ ACCT_R(ev_a);
+
if (val_a < 0x1f) {
*a = acc;
}
d->o = (acc > 0xffff);
d->cycle += 2;
+
+ ACCT_W(ev_a);
}
OP_IMPL(sub) {
/* sets a to a-b, sets O to 0xffff if there's an underflow, 0x0 otherwise */
unsigned int acc = *a - *b;
+ ACCT_R(ev_b);
+ ACCT_R(ev_a);
+
if (val_a < 0x1f) {
*a = acc;
d->o = (acc > 0xffff);
}
d->cycle += 2;
+
+ ACCT_W(ev_a);
}
OP_IMPL(mul) {
/* sets a to a*b, sets O to ((a*b)>>16)&0xffff */
unsigned int acc = *a * *b;
+ ACCT_R(ev_b);
+ ACCT_R(ev_a);
+
if (val_a < 0x1f) {
*a = acc;
}
d->o = acc >> 16;
d->cycle += 2;
+
+ ACCT_W(ev_a);
}
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);
+ ACCT_R(ev_a);
+
if (*b == 0) {
if (val_a < 0x1f) {
*a = 0;
}
d->cycle += 3;
+
+ ACCT_W(ev_a);
}
OP_IMPL(mod) {
OP_BASIC(mod);
/* sets a to a%b. if b==0, sets a to 0 instead. */
+ ACCT_R(ev_b);
+ ACCT_R(ev_a);
+
if (*b == 0) {
if (val_a < 0x1f) {
*a = 0;
}
d->cycle += 3;
+
+ ACCT_W(ev_a);
}
OP_IMPL(shl) {
/* sets a to a<<b, sets O to ((a<<b)>>16)&0xffff */
unsigned int acc = *a << *b;
+ ACCT_R(ev_b);
+ ACCT_R(ev_a);
+
if (val_a < 0x1f) {
*a = acc;
}
d->o = acc >> 16;
d->cycle += 2;
+
+ ACCT_W(ev_a);
}
OP_IMPL(shr) {
/* sets a to a>>b, sets O to ((a<<16)>>b)&0xffff */
unsigned int acc = *a >> *b;
+ ACCT_R(ev_b);
+ ACCT_R(ev_a);
+
if (val_a < 0x1f) {
*a = acc;
}
d->o = (*a << 16) >> *b;
d->cycle += 2;
+
+ ACCT_W(ev_a);
}
OP_IMPL(and) {
OP_BASIC(and);
/* sets a to a&b */
+ ACCT_R(ev_b);
+ ACCT_R(ev_a);
+
if (val_a < 0x1f) {
*a = *a & *b;
}
d->cycle += 1;
+
+ ACCT_W(ev_a);
}
OP_IMPL(bor) {
OP_BASIC(bor);
/* sets a to a|b */
+ ACCT_R(ev_b);
+ ACCT_R(ev_a);
+
if (val_a < 0x1f) {
*a = *a | *b;
}
d->cycle += 1;
+
+ ACCT_W(ev_a);
}
OP_IMPL(xor) {
OP_BASIC(xor);
/* sets a to a^b */
+ ACCT_R(ev_b);
+ ACCT_R(ev_a);
+
if (val_a < 0x1f) {
*a = *a ^ *b;
}
+ ACCT_W(ev_a);
+
d->cycle += 1;
}
OP_BASIC(ife);
/* performs next instruction only if a==b */
+ ACCT_R(ev_b);
+ ACCT_R(ev_a);
+
if (*a == *b) {
/* */
} else {
OP_BASIC(ifn);
/* performs next instruction only if a!=b */
+ ACCT_R(ev_b);
+ ACCT_R(ev_a);
+
if (*a != *b) {
/* */
} else {
OP_BASIC(ifg);
/* performs next instruction only if a>b */
+ ACCT_R(ev_b);
+ ACCT_R(ev_a);
+
if (*a > *b) {
/* */
} else {
OP_BASIC(ifb);
/* performs next instruction only if (a&b)!=0 */
+ ACCT_R(ev_b);
+ ACCT_R(ev_a);
+
if ((*a & *b) != 0) {
/* */
} else {
/* how much ram the system has */
#define DCPU16_RAM 0x10000
+/* these are used for accounting/watchpointing/&c */
+typedef unsigned int dcpu16_acct_event_;
+#define DCPU16_ACCT_EV_READ (1<<1)
+#define DCPU16_ACCT_EV_WRITE (1<<2)
+#define DCPU16_ACCT_RAM (1<<3)
+
+struct dcpu16_acct_cb {
+ void (*fn)(dcpu16_acct_event_ e, DCPU16_WORD addr);
+ dcpu16_acct_event_ match_all;
+ dcpu16_acct_event_ match_any;
+};
+
/* 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 */
+ 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 */
+ struct dcpu16_acct_cb *cb_table_; /* list of callbacks to invoke under certain conditions */
+ size_t cb_table_entries_; /* callback list maintenance */
+ size_t cb_table_allocated_; /* callback list maintenance */
};
/* instantiate a new core */
/* print the instruction at the specified address */
void dcpu16_disassemble_print(struct dcpu16 *, DCPU16_WORD);
+/* register a callback for an accounting event */
+int dcpu16_acct_add(struct dcpu16 *, dcpu16_acct_event_ match_all, dcpu16_acct_event_ match_any, void (*fn)(dcpu16_acct_event_, DCPU16_WORD));
/* execute the next instruction */
void dcpu16_step(struct dcpu16 *);