+
+/* 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);
+ }
+ }
+}
+