added callback support for memory access
authorJustin Wind <justin.wind@gmail.com>
Fri, 13 Apr 2012 03:37:34 +0000 (20:37 -0700)
committerJustin Wind <justin.wind@gmail.com>
Fri, 13 Apr 2012 03:37:34 +0000 (20:37 -0700)
Basic support in place for registering callbacks which get called on ram access events.

dcpu16.c
dcpu16.h

index 2a59d24e1cca31f5e161163fc8578d14fdcdc968..7d67cb178f88e15aea357c54ee221bb77ae2ff31 100644 (file)
--- a/dcpu16.c
+++ b/dcpu16.c
  *    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<<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) {
@@ -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 {
index edfccdfb53be7edbb1f0b41c7129c8fa0a736c11..af964e7e46c561f1923e5f49ff8dc5f8768623e8 100644 (file)
--- 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 *);