From 0b4991bc7e50f503769a2d2a1a4b5c1690251899 Mon Sep 17 00:00:00 2001 From: Justin Wind Date: Thu, 12 Apr 2012 20:37:34 -0700 Subject: [PATCH] added callback support for memory access Basic support in place for registering callbacks which get called on ram access events. --- dcpu16.c | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- dcpu16.h | 33 +++++++++---- 2 files changed, 163 insertions(+), 14 deletions(-) diff --git a/dcpu16.c b/dcpu16.c index 2a59d24..7d67cb1 100644 --- a/dcpu16.c +++ b/dcpu16.c @@ -21,12 +21,11 @@ * 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$"; @@ -82,17 +81,66 @@ void dcpu16_trace_cb_set(void (*fn)(char *fmt, ...)) { 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; @@ -117,6 +165,8 @@ static unsigned int value_decode(struct dcpu16 *d, WORD value, WORD *work_v, WOR 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++ ]; @@ -127,6 +177,8 @@ static unsigned int value_decode(struct dcpu16 *d, WORD value, WORD *work_v, WOR nextword, d->reg[(value & 0x07)], **v); + *e |= DCPU16_ACCT_RAM; + *e_addr = nextword + d->reg[(value & 0x07)]; } else switch (value) { case 0x18: /* POP / [sp++] */ @@ -134,6 +186,8 @@ static unsigned int value_decode(struct dcpu16 *d, WORD value, WORD *work_v, WOR TRACE(">> POP [0x%04x] (0x%04x)", d->sp - 1, **v); + *e |= DCPU16_ACCT_RAM; + *e_addr = d->sp - 1; break; case 0x19: /* PEEK / [sp] */ @@ -141,6 +195,8 @@ static unsigned int value_decode(struct dcpu16 *d, WORD value, WORD *work_v, WOR TRACE(">> PEEK [0x%04x] (0x%04x)", d->sp, **v); + *e |= DCPU16_ACCT_RAM; + *e_addr = d->sp; break; case 0x1a: /* PUSH / [--sp] */ @@ -148,6 +204,8 @@ static unsigned int value_decode(struct dcpu16 *d, WORD value, WORD *work_v, WOR TRACE(">> PUSH [0x%04x] (0x%04x)", d->sp + 1, **v); + *e |= DCPU16_ACCT_RAM; + *e_addr = d->sp + 1; break; case 0x1b: /* SP */ @@ -173,6 +231,8 @@ static unsigned int value_decode(struct dcpu16 *d, WORD value, WORD *work_v, WOR TRACE(">> [nextword] [0x%04x] (0x%04x)", nextword, **v); + *e |= DCPU16_ACCT_RAM; + *e_addr = nextword; break; case 0x1f: /* next word (literal) / [pc++] */ @@ -212,12 +272,14 @@ 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, (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");\ @@ -228,6 +290,9 @@ struct opcode_entry { #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 */ @@ -250,6 +315,8 @@ 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); + d->ram[ --d->sp ] = d->pc; d->pc = *a; @@ -298,6 +365,9 @@ OP_IMPL(set) { 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; @@ -311,12 +381,17 @@ OP_IMPL(add) { /* 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) { @@ -324,12 +399,17 @@ 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) { @@ -337,17 +417,25 @@ 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; @@ -365,12 +453,17 @@ OP_IMPL(div) { } 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; @@ -382,6 +475,8 @@ OP_IMPL(mod) { } d->cycle += 3; + + ACCT_W(ev_a); } OP_IMPL(shl) { @@ -389,12 +484,17 @@ OP_IMPL(shl) { /* sets a to a<>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) { @@ -402,44 +502,64 @@ 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; } @@ -447,6 +567,9 @@ OP_IMPL(ife) { OP_BASIC(ife); /* performs next instruction only if a==b */ + ACCT_R(ev_b); + ACCT_R(ev_a); + if (*a == *b) { /* */ } else { @@ -461,6 +584,9 @@ OP_IMPL(ifn) { OP_BASIC(ifn); /* performs next instruction only if a!=b */ + ACCT_R(ev_b); + ACCT_R(ev_a); + if (*a != *b) { /* */ } else { @@ -475,6 +601,9 @@ OP_IMPL(ifg) { OP_BASIC(ifg); /* performs next instruction only if a>b */ + ACCT_R(ev_b); + ACCT_R(ev_a); + if (*a > *b) { /* */ } else { @@ -489,6 +618,9 @@ OP_IMPL(ifb) { OP_BASIC(ifb); /* performs next instruction only if (a&b)!=0 */ + ACCT_R(ev_b); + ACCT_R(ev_a); + if ((*a & *b) != 0) { /* */ } else { diff --git a/dcpu16.h b/dcpu16.h index edfccdf..af964e7 100644 --- a/dcpu16.h +++ b/dcpu16.h @@ -7,16 +7,31 @@ typedef unsigned short DCPU16_WORD; /* 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 */ @@ -34,6 +49,8 @@ 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); +/* 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 *); -- 2.43.2