merge: expanded accounting-event coverage, rewrote cycle timing
authorJustin Wind <justin.wind@gmail.com>
Wed, 23 May 2012 20:28:19 +0000 (13:28 -0700)
committerJustin Wind <justin.wind@gmail.com>
Wed, 23 May 2012 20:29:34 +0000 (13:29 -0700)
Registering an accounting hook now requires a range of addresses of interest.
Accounting events now cover register accesses, indexed as address.

Cycle timing now uses all struct timespec.  This introduced another portability condit

Makefile
common.c
common.h
dcpu16.c
dcpu16.h
vm-dcpu16.c

index 4dcde613241b5595f8829ebf4fe6f0e34138e8c8..1ecac46d40f73f94e96b87c3c86e1bea893befdc 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -28,6 +28,8 @@ ifeq ($(UNAME),Linux)
 # linux needs _GNU_SOURCE for fopencookie
 # linux needs _XOPEN_SOURCE=600 (provided by _GNU_SOURCE) for getopt and strdup
 CPPFLAGS += -DHAVE_FOPENCOOKIE -D_GNU_SOURCE
+# linux needs librt for clock_gettime
+LDFLAGS += -lrt
 endif
 
 CPPFLAGS += -DHAVE_LIBVNCSERVER -DHAVE_LIBPNG
index 5887c224815c43c4daadaa1eae0ce2656bb2b88e..bf94ef7c98341ea198a0618d847c9f79fde0c172 100644 (file)
--- a/common.c
+++ b/common.c
@@ -7,6 +7,13 @@
 #include <string.h>
 #include <errno.h>
 #include <assert.h>
+#include <time.h>
+#include <sys/time.h>
+
+#ifdef __MACH__
+#include <mach/clock.h>
+#include <mach/mach.h>
+#endif /* __MACH__ */
 
 #include "common.h"
 #include "dcpu16.h"
@@ -58,6 +65,17 @@ struct dynamic_array *dynarray_new(size_t entry_size, size_t grow_size) {
     return da;
 }
 
+void dynarray_empty(struct dynamic_array *da, void (*free_element)(void *)) {
+    while ( da->entries-- ) {
+        void *element = (void *)DYNARRAY_ITEM(*da, da->entries);
+
+        free_element(element);
+    }
+    free(da->a);
+    da->a = NULL;
+    da->allocated = 0;
+}
+
 /* copy item onto end of array */
 void *dynarray_add(struct dynamic_array *da, void *item) {
     void *dst;
@@ -205,6 +223,55 @@ char *strqtok_r(char *str, const char *sep, int esc, const char *quote, char **l
     return tok;
 }
 
+/* like gettimeofday, except with nanoseconds */
+inline
+int gettimespecofday(struct timespec *ts) {
+    int retval = 0;
+#ifdef __MACH__
+    clock_serv_t cclock;
+    mach_timespec_t mts;
+
+    host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
+    clock_get_time(cclock, &mts);
+    mach_port_deallocate(mach_task_self(), cclock);
+    ts->tv_sec = mts.tv_sec;
+    ts->tv_nsec = mts.tv_nsec;
+#else /* __MACH__ */
+    retval = clock_gettime(CLOCK_REALTIME, ts)
+#endif /* __MACH__ */
+
+    return retval;
+}
+
+inline
+int timespec_add(struct timespec *result, const struct timespec *x) {
+    result->tv_sec += x->tv_sec;
+    result->tv_nsec += x->tv_nsec;
+
+    result->tv_sec += result->tv_nsec / 1000000000;
+    result->tv_nsec %= 1000000000;
+
+    return 0;
+}
+
+inline
+int timespec_subtract(struct timespec *result, struct timespec *x, struct timespec *y) {
+    if (x->tv_nsec < y->tv_nsec) {
+        int z = (y->tv_nsec - x->tv_nsec) / 1000000000 + 1;
+        y->tv_nsec -= 1000000000 * z;
+        y->tv_sec += z;
+    }
+    if (x->tv_nsec - y->tv_nsec > 1000000000) {
+        int z = (x->tv_nsec - y->tv_nsec) / 1000000000;
+        y->tv_nsec += 1000000000 * z;
+        y->tv_sec -= z;
+    }
+    result->tv_sec = x->tv_sec - y->tv_sec;
+    result->tv_nsec = x->tv_nsec - y->tv_nsec;
+
+    return x->tv_sec < y->tv_sec;
+}
+
 int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y) {
   /* Perform the carry for the later subtraction by updating y. */
   if (x->tv_usec < y->tv_usec) {
index 02db4d77fdc131f63166d63d9cdb5d0ecca7d73c..09e1509c96eec5eab8961163dd70cecf8596c039 100644 (file)
--- a/common.h
+++ b/common.h
@@ -2,6 +2,7 @@
 #define COMMON_H_QPRCB1BH
 
 #include <stdlib.h>
+#include <time.h>
 
 struct dynamic_array {
     size_t entry_size;
@@ -16,11 +17,14 @@ struct dynamic_array {
 int dynarray_init(struct dynamic_array *da, size_t entry_size, size_t grow_size);
 struct dynamic_array *dynarray_new(size_t entry_size, size_t grow_size);
 void *dynarray_add(struct dynamic_array *da, void *item);
+void dynarray_empty(struct dynamic_array *da, void (*free_element)(void *));
 
 int str_to_word(char *s);
 
 char *strqtok_r(char *str, const char *sep, int esc, const char *quote, char **lastq, char **lasts);
 
-int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y);
+int gettimespecofday(struct timespec *ts);
+int timespec_add(struct timespec *result, const struct timespec *x);
+int timespec_subtract(struct timespec *result, struct timespec *x, struct timespec *y);
 
 #endif /* COMMON_H_QPRCB1BH */
index 817cfb0da6c54d6490f4d32fda344526d152ed1f..e8e24d137c8f8a25b8bb62f810a6c9b9dd725ad1 100644 (file)
--- a/dcpu16.c
+++ b/dcpu16.c
@@ -120,7 +120,8 @@ void acct_event_(struct dcpu16 *vm, dcpu16_acct_event ev, DCPU16_WORD addr) {
 
     for (i = 0; i < vm->cb_table_entries_; i++) {
         if ( (cb[i].mask & ev) )
-            cb[i].fn(vm, ev, addr, cb[i].data);
+            if (addr >= cb[i].addr_l && addr <= cb[i].addr_h)
+                cb[i].fn(vm, ev, addr, cb[i].data);
     }
 }
 
@@ -192,15 +193,19 @@ void dcpu16_cycle_inc(struct dcpu16 *vm, unsigned int n) {
  * decoding next operand..
  *
  */
+#define EWHAT_NONE  (0)
+#define EWHAT_REG   (1<<1)
+#define EWHAT_RAM   (1<<2)
 static inline
 void value_decode_(struct dcpu16 *vm, DCPU16_WORD value, unsigned int value_is_a, DCPU16_WORD value_data,
-                  DCPU16_WORD *work_v, DCPU16_WORD **v, DCPU16_WORD *e_addr,
+                  DCPU16_WORD *work_v, DCPU16_WORD **v, DCPU16_WORD *e_addr, enum dcpu16_register_indexes *e_reg, unsigned int *e_what,
                   short *pc_adjust, short *sp_adjust, unsigned int *cycle_adjust) {
     assert(value <= 0x3f);
 
     DCPU16_WORD pc = (DCPU16_WORD)(vm->reg[DCPU16_REG_PC] + *pc_adjust),
                 sp = (DCPU16_WORD)(vm->reg[DCPU16_REG_SP] + *sp_adjust);
 
+    (void)pc;
     TRACE("%s>> is_a:%u pc:0x%04x sp:0x%04x value_data:0x%04x\n", 
           __func__,
           value_is_a,
@@ -209,7 +214,9 @@ void value_decode_(struct dcpu16 *vm, DCPU16_WORD value, unsigned int value_is_a
           value_data);
 
     if (value <= 0x07) { /* register */
-        *v = vm->reg + value;
+        *e_what = EWHAT_REG;
+        *e_reg = value & 0x07;
+        *v = vm->reg + *e_reg;
         TRACE("%s>>     %s (0x%04x)",
               __func__,
               dcpu16_reg_names[value],
@@ -218,8 +225,10 @@ void value_decode_(struct dcpu16 *vm, DCPU16_WORD value, unsigned int value_is_a
     }
 
     if (value <= 0x0f) { /* [register] */
-        *v = &(vm->ram[ vm->reg[value & 0x07] ]);
+        *e_what = EWHAT_RAM;
         *e_addr = vm->reg[value & 0x07];
+        *v = &(vm->ram[ *e_addr ]);
+        acct_event_(vm, DCPU16_ACCT_EV_REG_READ, value & 0x07);
         TRACE("%s>>     [%s] [0x%04x] (0x%04x)",
               __func__,
               dcpu16_reg_names[value & 0x07],
@@ -229,9 +238,12 @@ void value_decode_(struct dcpu16 *vm, DCPU16_WORD value, unsigned int value_is_a
     }
 
     if (value <= 0x17) { /* [next word + register] */
+        acct_event_(vm, DCPU16_ACCT_EV_REG_WRITE, DCPU16_REG_PC);
         *pc_adjust += 1; /* consume next word */
         *cycle_adjust += 1;
+        *e_what = EWHAT_RAM;
         *e_addr = value_data + vm->reg[value & 0x07];
+        acct_event_(vm, DCPU16_ACCT_EV_REG_READ, value & 0x07);
         *v = vm->ram + *e_addr;
         TRACE("%s>>     [nextword + %s] [0x%04x + 0x%04x] (0x%04x)",
               __func__,
@@ -244,6 +256,9 @@ void value_decode_(struct dcpu16 *vm, DCPU16_WORD value, unsigned int value_is_a
 
     switch (value) {
         case 0x18: /* PUSH/[--SP] or POP/[SP++] */
+        *e_what = EWHAT_RAM;
+        acct_event_(vm, DCPU16_ACCT_EV_REG_READ, DCPU16_REG_SP);
+        acct_event_(vm, DCPU16_ACCT_EV_REG_WRITE, DCPU16_REG_SP);
         if (value_is_a == 0) { /* b */
             *v = &(vm->ram[sp - 1]);
             *sp_adjust -= 1;
@@ -258,6 +273,8 @@ void value_decode_(struct dcpu16 *vm, DCPU16_WORD value, unsigned int value_is_a
         break;
 
         case 0x19: /* PEEK/[SP] */
+        acct_event_(vm, DCPU16_ACCT_EV_REG_READ, DCPU16_REG_SP);
+        *e_what = EWHAT_RAM;
         *v = &(vm->ram[sp]);
         *e_addr = sp;
         TRACE("%s>>     PEEK [0x%04x] (0x%04x)",
@@ -267,6 +284,8 @@ void value_decode_(struct dcpu16 *vm, DCPU16_WORD value, unsigned int value_is_a
         break;
 
         case 0x1a: /* PICK n */
+        acct_event_(vm, DCPU16_ACCT_EV_REG_READ, DCPU16_REG_SP);
+        *e_what = EWHAT_RAM;
         *pc_adjust += 1;
         *cycle_adjust += 1;
         *e_addr = sp + value_data;
@@ -279,6 +298,8 @@ void value_decode_(struct dcpu16 *vm, DCPU16_WORD value, unsigned int value_is_a
         break;
 
         case 0x1b: /* SP */
+        *e_reg = DCPU16_REG_SP;
+        *e_what = EWHAT_REG;
         *v = &(vm->reg[DCPU16_REG_SP]);
         TRACE("%s>>     %s (0x%04x)",
               __func__,
@@ -287,6 +308,8 @@ void value_decode_(struct dcpu16 *vm, DCPU16_WORD value, unsigned int value_is_a
         break;
 
         case 0x1c: /* PC */
+        *e_reg = DCPU16_REG_PC;
+        *e_what = EWHAT_REG;
         *v = &(vm->reg[DCPU16_REG_PC]);
         TRACE("%s>>     %s (0x%04x)",
               __func__,
@@ -295,6 +318,8 @@ void value_decode_(struct dcpu16 *vm, DCPU16_WORD value, unsigned int value_is_a
         break;
 
         case 0x1d: /* EX */
+        *e_reg = DCPU16_REG_EX;
+        *e_what = EWHAT_REG;
         *v = &(vm->reg[DCPU16_REG_EX]);
         TRACE("%s>>     %s (0x%04x)",
               __func__,
@@ -303,6 +328,8 @@ void value_decode_(struct dcpu16 *vm, DCPU16_WORD value, unsigned int value_is_a
         break;
 
         case 0x1e: /* [next word] / [[pc++]] */
+        *e_what = EWHAT_RAM;
+        acct_event_(vm, DCPU16_ACCT_EV_REG_WRITE, DCPU16_REG_PC);
         *pc_adjust += 1;
         *cycle_adjust += 1;
         *e_addr = value_data;
@@ -314,6 +341,8 @@ void value_decode_(struct dcpu16 *vm, DCPU16_WORD value, unsigned int value_is_a
         break;
 
         case 0x1f: /* next word (literal) / [pc++] */
+        *e_what = EWHAT_NONE;
+        acct_event_(vm, DCPU16_ACCT_EV_REG_WRITE, DCPU16_REG_PC);
         *pc_adjust += 1;
         *cycle_adjust += 1;
         *work_v = value_data;
@@ -324,6 +353,7 @@ void value_decode_(struct dcpu16 *vm, DCPU16_WORD value, unsigned int value_is_a
         break;
 
         default: /* 0x20-0x3f: literal values 0xffff-0x1e */
+        *e_what = EWHAT_NONE;
         *work_v = (value & 0x1f) - 1;
         *v = work_v;
         TRACE("%s>>     literal (0x%04x)",
@@ -349,19 +379,21 @@ struct opcode_entry {
 
 #define OP_TYPE(op_type) DCPU16_WORD *a, *b;\
     DCPU16_WORD ev_a_addr = 0, ev_b_addr = 0;\
+    enum dcpu16_register_indexes ev_a_reg = DCPU16_REG__NUM, ev_b_reg = DCPU16_REG__NUM;\
+    unsigned int ev_a_what = 0, ev_b_what = 0;\
     short pc_adjust = 0, sp_adjust = 0;\
     unsigned int cycle_adjust = 0;\
     do {\
         op_type;\
         value_decode_(vm, val_a, 1, val_a_data,\
-                      &vm->reg_work_[1], &a, &ev_a_addr,\
+                      &vm->reg_work_[1], &a, &ev_a_addr, &ev_a_reg, &ev_a_what,\
                       &pc_adjust, &sp_adjust, &cycle_adjust);\
         vm->reg[DCPU16_REG_SP] += sp_adjust;\
         dcpu16_cycle_inc(vm, cycle_adjust);\
     } while (0)
-#define OP_NBI_ (void)val_b, (void)b, (void)ev_b_addr, (void)val_b_data
+#define OP_NBI_ (void)val_b, (void)b, (void)ev_b_addr, (void)val_b_data, (void)ev_b_reg, (void)ev_b_what
 #define OP_BASIC_ value_decode_(vm, val_b, 0, val_b_data,\
-                                &vm->reg_work_[0], &b, &ev_b_addr,\
+                                &vm->reg_work_[0], &b, &ev_b_addr, &ev_b_reg, &ev_b_what,\
                                 &pc_adjust, &sp_adjust, &cycle_adjust)
 #define OP_BASIC(x) OP_TYPE(OP_BASIC_)
 #define OP_NBI(x) OP_TYPE(OP_NBI_)
@@ -372,9 +404,20 @@ struct opcode_entry {
     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_(vm, DCPU16_ACCT_EV_READ, addr); } while (0)
-#define ACCT_W(addr) do { acct_event_(vm, DCPU16_ACCT_EV_WRITE, addr); } while (0)
 #define ACCT_ILL(addr) do { acct_event_(vm, DCPU16_ACCT_EV_NOP, addr); } while (0)
+#define ACCT_RAM_R(addr) do { acct_event_(vm, DCPU16_ACCT_EV_READ, addr); } while (0)
+#define ACCT_RAM_W(addr) do { acct_event_(vm, DCPU16_ACCT_EV_WRITE, addr); } while (0)
+#define ACCT_REG_R(reg) do { acct_event_(vm, DCPU16_ACCT_EV_REG_READ, reg); } while (0)
+#define ACCT_REG_W(reg) do { acct_event_(vm, DCPU16_ACCT_EV_REG_WRITE, reg); } while (0)
+
+#define ACCT_R(__x__) do {\
+    if (ev_##__x__##_what & EWHAT_REG) ACCT_REG_R(ev_##__x__##_reg);\
+    if (ev_##__x__##_what & EWHAT_RAM) ACCT_RAM_R(ev_##__x__##_addr);\
+} while (0)
+#define ACCT_W(__x__) do {\
+    if (ev_##__x__##_what & EWHAT_REG) ACCT_REG_W(ev_##__x__##_reg);\
+    if (ev_##__x__##_what & EWHAT_RAM) ACCT_RAM_W(ev_##__x__##_addr);\
+} while (0)
 
 /* extended opcodes */
 
@@ -391,23 +434,31 @@ OP_IMPL(nbi__reserved_) {
 
     /* fire an illegal instruction event for current instruction */
     DCPU16_WORD future_opcode = (vm->ram[vm->reg[DCPU16_REG_PC] - pc_adjust] >> (OPCODE_BASIC_BITS + OPCODE_OPERAND_B_BITS));
+
     WARN("reserved future opcode 0x%04x invoked", future_opcode);
 
     ACCT_ILL(vm->reg[DCPU16_REG_PC] - pc_adjust);
+
+    dcpu16_cycle_inc(vm, 1);
 }
 
 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);
+    ACCT_R(a);
 
+    ACCT_REG_R(DCPU16_REG_PC);
+    ACCT_REG_R(DCPU16_REG_SP);
     vm->ram[ --vm->reg[DCPU16_REG_SP] ] = vm->reg[DCPU16_REG_PC];
+    ACCT_REG_W(DCPU16_REG_SP);
+    ACCT_RAM_W(vm->reg[DCPU16_REG_SP] + 1);
+
     vm->reg[DCPU16_REG_PC] = *a;
+    ACCT_REG_W(DCPU16_REG_PC);
 
-    dcpu16_cycle_inc(vm, 2);
 
-    ACCT_W(vm->reg[DCPU16_REG_SP] + 1);
+    dcpu16_cycle_inc(vm, 2);
 }
 
 OP_IMPL(nbi__reserved2_) {
@@ -417,13 +468,16 @@ OP_IMPL(nbi__reserved2_) {
     WARN("reserved nbi opcode invoked");
 
     ACCT_ILL(vm->reg[DCPU16_REG_PC] - pc_adjust);
+
+    dcpu16_cycle_inc(vm, 1);
 }
 
 OP_IMPL(nbi_int) {
     OP_NBI(nbi_int);
 
-    ACCT_R(ev_a_addr);
+    ACCT_R(a);
 
+    ACCT_REG_R(DCPU16_REG_IA);
     if (vm->reg[DCPU16_REG_IA]) {
         if ( interrupt_enqueue_(vm, *a) ) {
             WARN("failed to queue interrupt");
@@ -434,10 +488,25 @@ OP_IMPL(nbi_int) {
             return;
 
         vm->interrupts_deferred_ = 1;
+
+        ACCT_REG_R(DCPU16_REG_PC);
+        ACCT_REG_R(DCPU16_REG_SP);
         vm->ram[--vm->reg[DCPU16_REG_SP]] = vm->reg[DCPU16_REG_PC];
+        ACCT_RAM_W(vm->reg[DCPU16_REG_SP] + 1);
+        ACCT_REG_W(DCPU16_REG_SP);
+
+        ACCT_REG_R(DCPU16_REG_A);
+        ACCT_REG_R(DCPU16_REG_SP);
         vm->ram[--vm->reg[DCPU16_REG_SP]] = vm->reg[DCPU16_REG_A];
+        ACCT_RAM_W(vm->reg[DCPU16_REG_SP] + 1);
+        ACCT_REG_W(DCPU16_REG_SP);
+
+        ACCT_REG_R(DCPU16_REG_IA);
         vm->reg[DCPU16_REG_PC] = vm->reg[DCPU16_REG_IA];
-        vm->reg[0] = *a;
+        ACCT_REG_W(DCPU16_REG_PC);
+
+        vm->reg[DCPU16_REG_A] = *a;
+        ACCT_REG_W(DCPU16_REG_A);
     }
 
     dcpu16_cycle_inc(vm, 4);
@@ -446,30 +515,45 @@ OP_IMPL(nbi_int) {
 OP_IMPL(nbi_iag) {
     OP_NBI(nbi_iag);
 
+    ACCT_REG_R(DCPU16_REG_IA);
     *a = vm->reg[DCPU16_REG_IA];
 
-    dcpu16_cycle_inc(vm, 1);
+    ACCT_W(a);
 
-    ACCT_W(ev_a_addr);
+    dcpu16_cycle_inc(vm, 1);
 }
 
 OP_IMPL(nbi_ias) {
     OP_NBI(nbi_ias);
 
+    ACCT_R(a);
+
     vm->reg[DCPU16_REG_IA] = *a;
+    ACCT_REG_W(DCPU16_REG_IA);
 
     dcpu16_cycle_inc(vm, 1);
-
-    ACCT_R(ev_a_addr);
 }
 
 /* does this just ignore its operand? */
 OP_IMPL(nbi_rfi) {
     OP_NBI(nbi_rfi);
 
+    /* well, it consumes the argument, currently, so I guess pretend like we care */
+    ACCT_R(a);
+
     vm->interrupts_deferred_ = 0;
+
+    ACCT_REG_R(DCPU16_REG_SP);
+    ACCT_RAM_R(vm->reg[DCPU16_REG_SP]);
     vm->reg[DCPU16_REG_A] = vm->ram[vm->reg[DCPU16_REG_SP]++];
+    ACCT_REG_W(DCPU16_REG_A);
+    ACCT_REG_W(DCPU16_REG_SP);
+
+    ACCT_REG_R(DCPU16_REG_SP);
+    ACCT_RAM_R(vm->reg[DCPU16_REG_SP]);
     vm->reg[DCPU16_REG_PC] = vm->ram[vm->reg[DCPU16_REG_SP]++];
+    ACCT_REG_W(DCPU16_REG_PC);
+    ACCT_REG_W(DCPU16_REG_SP);
 
     dcpu16_cycle_inc(vm, 3);
 }
@@ -477,6 +561,8 @@ OP_IMPL(nbi_rfi) {
 OP_IMPL(nbi_iaq) {
     OP_NBI(nbi_iaq);
 
+    ACCT_R(a);
+
     if (*a) {
         vm->interrupts_deferred_ = 1;
     } else {
@@ -484,16 +570,13 @@ OP_IMPL(nbi_iaq) {
     }
 
     dcpu16_cycle_inc(vm, 2);
-
-    ACCT_R(ev_a_addr);
 }
 
 OP_IMPL(nbi_hwn) {
     OP_NBI(nbi_hwn);
 
-    ACCT_W(ev_a_addr);
-
     *a = vm->hw_table_entries_;
+    ACCT_W(a);
 
     dcpu16_cycle_inc(vm, 2);
 }
@@ -501,7 +584,7 @@ OP_IMPL(nbi_hwn) {
 OP_IMPL(nbi_hwq) {
     OP_NBI(nbi_hwq);
 
-    ACCT_R(ev_a_addr);
+    ACCT_R(a);
 
     if (*a >= vm->hw_table_entries_) {
         WARN("hardware query for non-extant device 0x%04x", *a);
@@ -510,14 +593,19 @@ OP_IMPL(nbi_hwq) {
         vm->reg[DCPU16_REG_C] = 0;
         vm->reg[DCPU16_REG_X] = 0;
         vm->reg[DCPU16_REG_Y] = 0;
-        return;
+    } else {
+        vm->reg[DCPU16_REG_A] = vm->hw_table_[*a].mod->id_l;
+        vm->reg[DCPU16_REG_B] = vm->hw_table_[*a].mod->id_h;
+        vm->reg[DCPU16_REG_C] = vm->hw_table_[*a].mod->ver;
+        vm->reg[DCPU16_REG_X] = vm->hw_table_[*a].mod->mfg_l;
+        vm->reg[DCPU16_REG_Y] = vm->hw_table_[*a].mod->mfg_h;
     }
 
-    vm->reg[DCPU16_REG_A] = vm->hw_table_[*a].mod->id_l;
-    vm->reg[DCPU16_REG_B] = vm->hw_table_[*a].mod->id_h;
-    vm->reg[DCPU16_REG_C] = vm->hw_table_[*a].mod->ver;
-    vm->reg[DCPU16_REG_X] = vm->hw_table_[*a].mod->mfg_l;
-    vm->reg[DCPU16_REG_Y] = vm->hw_table_[*a].mod->mfg_h;
+    ACCT_REG_W(DCPU16_REG_A);
+    ACCT_REG_W(DCPU16_REG_B);
+    ACCT_REG_W(DCPU16_REG_C);
+    ACCT_REG_W(DCPU16_REG_X);
+    ACCT_REG_W(DCPU16_REG_Y);
 
     dcpu16_cycle_inc(vm, 4);
 }
@@ -525,24 +613,25 @@ OP_IMPL(nbi_hwq) {
 OP_IMPL(nbi_hwi) {
     OP_NBI(nbi_hwi);
 
-    ACCT_R(ev_a_addr);
+    ACCT_R(a);
 
     if (*a > vm->hw_table_entries_) {
         WARN("interrupt for non-extant device 0x%04x", *a);
         return;
     }
 
-    dcpu16_cycle_inc(vm, 4);
     if (vm->hw_table_[*a].mod->hwi)
         vm->hw_table_[*a].mod->hwi(vm, &vm->hw_table_[*a]);
     else
         WARN("hardware 0x%04x has no interrupt handler", *a);
+
+    dcpu16_cycle_inc(vm, 4);
 }
 
 OP_IMPL(nbi_hcf) {
     OP_NBI(nbi_hcf);
 
-    ACCT_R(ev_a_addr);
+    ACCT_R(a);
 
     vm->on_fire_ = 1;
     WARN("system on fire");
@@ -604,7 +693,7 @@ OP_IMPL(set) {
     OP_BASIC(set);
     /* sets b to a */
 
-    ACCT_R(ev_a_addr);
+    ACCT_R(a);
 
     /*
         if b is a literal, it's aimed at a scratch register,
@@ -612,9 +701,9 @@ OP_IMPL(set) {
      */
     *b = *a;
 
+    ACCT_W(b);
+    
     dcpu16_cycle_inc(vm, 1);
-
-    ACCT_W(ev_b_addr);
 }
 
 OP_IMPL(add) {
@@ -622,15 +711,17 @@ OP_IMPL(add) {
     /* sets b to b+a, sets EX to 0x0001 if there's an overflow, 0x0 otherwise */
     unsigned int acc = *b + *a;
 
-    ACCT_R(ev_b_addr);
-    ACCT_R(ev_a_addr);
+    ACCT_R(b);
+    ACCT_R(a);
 
     *b = acc;
     vm->reg[DCPU16_REG_EX] = (acc > 0xffff);
 
-    dcpu16_cycle_inc(vm, 2);
+    ACCT_REG_W(DCPU16_REG_EX);
 
-    ACCT_W(ev_b_addr);
+    ACCT_W(b);
+
+    dcpu16_cycle_inc(vm, 2);
 }
 
 OP_IMPL(sub) {
@@ -638,15 +729,17 @@ OP_IMPL(sub) {
     /* sets b to b-a, sets EX to 0xffff if there's an underflow, 0x0 otherwise */
     unsigned int acc = *b - *a;
 
-    ACCT_R(ev_b_addr);
-    ACCT_R(ev_a_addr);
+    ACCT_R(b);
+    ACCT_R(a);
 
     *b = acc;
     vm->reg[DCPU16_REG_EX] = (acc > 0xffff);
 
-    dcpu16_cycle_inc(vm, 2);
+    ACCT_REG_W(DCPU16_REG_EX);
 
-    ACCT_W(ev_b_addr);
+    ACCT_W(b);
+
+    dcpu16_cycle_inc(vm, 2);
 }
 
 OP_IMPL(mul) {
@@ -654,15 +747,17 @@ OP_IMPL(mul) {
     /* sets b to b*a, unsigned, sets EX to ((b*a)>>16)&0xffff */
     unsigned int acc = *b * *a;
 
-    ACCT_R(ev_b_addr);
-    ACCT_R(ev_a_addr);
+    ACCT_R(b);
+    ACCT_R(a);
 
     *b = acc;
     vm->reg[DCPU16_REG_EX] = acc >> 16;
 
-    dcpu16_cycle_inc(vm, 2);
+    ACCT_REG_W(DCPU16_REG_EX);
+
+    ACCT_W(b);
 
-    ACCT_W(ev_b_addr);
+    dcpu16_cycle_inc(vm, 2);
 }
 
 OP_IMPL(mli) {
@@ -670,23 +765,25 @@ OP_IMPL(mli) {
     /* sets b to b*a, signed */
     int acc = (short)*b * (short)*a;
 
-    ACCT_R(ev_b_addr);
-    ACCT_R(ev_a_addr);
+    ACCT_R(b);
+    ACCT_R(a);
 
     *b = acc;
     vm->reg[DCPU16_REG_EX] = acc >> 16;
 
-    dcpu16_cycle_inc(vm, 2);
+    ACCT_REG_W(DCPU16_REG_EX);
 
-    ACCT_W(ev_b_addr);
+    ACCT_W(b);
+
+    dcpu16_cycle_inc(vm, 2);
 }
 
 OP_IMPL(div) {
     OP_BASIC(div);
     /* sets b to b/a, sets EX to ((b<<16)/a)&0xffff. if a==0, sets a and EX to 0 instead. */
 
-    ACCT_R(ev_b_addr);
-    ACCT_R(ev_a_addr);
+    ACCT_R(b);
+    ACCT_R(a);
 
     if (*a == 0) {
         *b = 0;
@@ -696,17 +793,19 @@ OP_IMPL(div) {
         vm->reg[DCPU16_REG_EX] = (*b << 16) / *a;
     }
 
-    dcpu16_cycle_inc(vm, 3);
+    ACCT_REG_W(DCPU16_REG_EX);
 
-    ACCT_W(ev_b_addr);
+    ACCT_W(b);
+
+    dcpu16_cycle_inc(vm, 3);
 }
 
 OP_IMPL(dvi) {
     OP_BASIC(dvi);
     /* sets b to b/a, signed, round towards 0 */
 
-    ACCT_R(ev_b_addr);
-    ACCT_R(ev_a_addr);
+    ACCT_R(b);
+    ACCT_R(a);
 
     if (*a == 0) {
         *b = 0;
@@ -716,17 +815,18 @@ OP_IMPL(dvi) {
         vm->reg[DCPU16_REG_EX] = (short)(*b << 16) / (short)*a;
     }
 
-    dcpu16_cycle_inc(vm, 3);
+    ACCT_REG_W(DCPU16_REG_EX);
+    ACCT_W(b);
 
-    ACCT_W(ev_b_addr);
+    dcpu16_cycle_inc(vm, 3);
 }
 
 OP_IMPL(mod) {
     OP_BASIC(mod);
     /* sets b to b%a. if a==0, sets b to 0 instead. */
 
-    ACCT_R(ev_b_addr);
-    ACCT_R(ev_a_addr);
+    ACCT_R(b);
+    ACCT_R(a);
 
     if (*a == 0) {
         *b = 0;
@@ -734,17 +834,17 @@ OP_IMPL(mod) {
         *b = *b % *a;
     }
 
-    dcpu16_cycle_inc(vm, 3);
+    ACCT_W(a);
 
-    ACCT_W(ev_a_addr);
+    dcpu16_cycle_inc(vm, 3);
 }
 
 OP_IMPL(mdi) {
     OP_BASIC(mdi);
     /* sets b to b%a, signed */
 
-    ACCT_R(ev_b_addr);
-    ACCT_R(ev_a_addr);
+    ACCT_R(b);
+    ACCT_R(a);
 
     if (*a == 0) {
         *b = 0;
@@ -752,51 +852,51 @@ OP_IMPL(mdi) {
         *b = (short)*b % (short)*a;
     }
 
-    dcpu16_cycle_inc(vm, 3);
+    ACCT_W(b);
 
-    ACCT_W(ev_b_addr);
+    dcpu16_cycle_inc(vm, 3);
 }
 
 OP_IMPL(and) {
     OP_BASIC(and);
     /* sets b to b&a */
 
-    ACCT_R(ev_b_addr);
-    ACCT_R(ev_a_addr);
+    ACCT_R(b);
+    ACCT_R(a);
 
     *b = *b & *a;
 
-    dcpu16_cycle_inc(vm, 1);
+    ACCT_W(b);
 
-    ACCT_W(ev_b_addr);
+    dcpu16_cycle_inc(vm, 1);
 }
 
 OP_IMPL(bor) {
     OP_BASIC(bor);
     /* sets b to b|a */
 
-    ACCT_R(ev_b_addr);
-    ACCT_R(ev_a_addr);
+    ACCT_R(b);
+    ACCT_R(a);
 
     *b = *b | *a;
 
-    dcpu16_cycle_inc(vm, 1);
+    ACCT_W(b);
 
-    ACCT_W(ev_b_addr);
+    dcpu16_cycle_inc(vm, 1);
 }
 
 OP_IMPL(xor) {
     OP_BASIC(xor);
     /* sets b to b^a */
 
-    ACCT_R(ev_b_addr);
-    ACCT_R(ev_a_addr);
+    ACCT_R(b);
+    ACCT_R(a);
 
     *b = *b ^ *a;
 
     dcpu16_cycle_inc(vm, 1);
 
-    ACCT_W(ev_b_addr);
+    ACCT_W(b);
 }
 
 OP_IMPL(shr) {
@@ -804,17 +904,18 @@ OP_IMPL(shr) {
     /* sets b to b>>>a, sets EX to ((b<<16)>>a)&0xffff */
     unsigned int acc = *b >> *a;
 
-    ACCT_R(ev_b_addr);
-    ACCT_R(ev_a_addr);
+    ACCT_R(b);
+    ACCT_R(a);
 
     *b = acc & 0xffff;
     vm->reg[DCPU16_REG_EX] = (*b << 16) >> *a;
 
-    dcpu16_cycle_inc(vm, 2);
-
     WARN("IMPLEMENT");
 
-    ACCT_W(ev_b_addr);
+    ACCT_REG_W(DCPU16_REG_EX);
+    ACCT_W(b);
+
+    dcpu16_cycle_inc(vm, 2);
 }
 
 OP_IMPL(asr) {
@@ -822,17 +923,18 @@ OP_IMPL(asr) {
     /* sets b to b>>a, sets EX to ((b<<16)>>>a)&0xffff (arithmetic shift) (treats b as signed) */
     unsigned int acc = *b << *a;
 
-    ACCT_R(ev_b_addr);
-    ACCT_R(ev_a_addr);
+    ACCT_R(b);
+    ACCT_R(a);
 
     *b = acc & 0xffff;
     vm->reg[DCPU16_REG_EX] = (*b << 16) >> *a;
 
-    dcpu16_cycle_inc(vm, 2);
-
     WARN("IMPLEMENT");
 
-    ACCT_W(ev_b_addr);
+    ACCT_REG_W(DCPU16_REG_EX);
+    ACCT_W(b);
+
+    dcpu16_cycle_inc(vm, 2);
 }
 
 OP_IMPL(shl) {
@@ -840,24 +942,25 @@ OP_IMPL(shl) {
     /* sets b to b<<a, sets EX to ((b<<a)>>16)&0xffff */
     unsigned int acc = *b << *a;
 
-    ACCT_R(ev_b_addr);
-    ACCT_R(ev_a_addr);
+    ACCT_R(b);
+    ACCT_R(a);
 
     *b = acc;
 
     vm->reg[DCPU16_REG_EX] = acc >> 16;
 
-    dcpu16_cycle_inc(vm, 2);
+    ACCT_REG_W(DCPU16_REG_EX);
+    ACCT_W(b);
 
-    ACCT_W(ev_b_addr);
+    dcpu16_cycle_inc(vm, 2);
 }
 
 OP_IMPL(ifb) {
     OP_BASIC(ifb);
     /* performs next instruction only if (b&a)!=0 */
 
-    ACCT_R(ev_b_addr);
-    ACCT_R(ev_a_addr);
+    ACCT_R(b);
+    ACCT_R(a);
 
     if ((*b & *a) != 0) {
         /* */
@@ -873,8 +976,8 @@ OP_IMPL(ifc) {
     OP_BASIC(ifc);
     /* performs next instruction only if (b&a)==0 */
 
-    ACCT_R(ev_b_addr);
-    ACCT_R(ev_a_addr);
+    ACCT_R(b);
+    ACCT_R(a);
 
     if ((*b & *a) == 0) {
         /* */
@@ -890,8 +993,8 @@ OP_IMPL(ife) {
     OP_BASIC(ife);
     /* performs next instruction only if b==a */
 
-    ACCT_R(ev_b_addr);
-    ACCT_R(ev_a_addr);
+    ACCT_R(b);
+    ACCT_R(a);
 
     if (*b == *a) {
         /* */
@@ -907,8 +1010,8 @@ OP_IMPL(ifn) {
     OP_BASIC(ifn);
     /* performs next instruction only if b!=a */
 
-    ACCT_R(ev_b_addr);
-    ACCT_R(ev_a_addr);
+    ACCT_R(b);
+    ACCT_R(a);
 
     if (*b != *a) {
         /* */
@@ -924,8 +1027,8 @@ OP_IMPL(ifg) {
     OP_BASIC(ifg);
     /* performs next instruction only if b>a */
 
-    ACCT_R(ev_b_addr);
-    ACCT_R(ev_a_addr);
+    ACCT_R(b);
+    ACCT_R(a);
 
     if (*b > *a) {
         /* */
@@ -941,8 +1044,8 @@ OP_IMPL(ifa) {
     OP_BASIC(ifa);
     /* performs next instruction only if b>a (signed) */
 
-    ACCT_R(ev_b_addr);
-    ACCT_R(ev_a_addr);
+    ACCT_R(b);
+    ACCT_R(a);
 
     if (*b > *a) {
         /* */
@@ -958,8 +1061,8 @@ OP_IMPL(ifl) {
     OP_BASIC(ifl);
     /* performs next instruction only if b<a */
 
-    ACCT_R(ev_b_addr);
-    ACCT_R(ev_a_addr);
+    ACCT_R(b);
+    ACCT_R(a);
 
     if (*b < *a) {
         /* */
@@ -975,8 +1078,8 @@ OP_IMPL(ifu) {
     OP_BASIC(ifu);
     /* performs next instruction only if b<a (signed) */
 
-    ACCT_R(ev_b_addr);
-    ACCT_R(ev_a_addr);
+    ACCT_R(b);
+    ACCT_R(a);
 
     if (*b < *a) {
         /* */
@@ -993,8 +1096,10 @@ OP_IMPL(adx) {
     /* sets b to b+a+EX, sets EX to 0x0001 if overflow, 0x0 otherwise */
     unsigned int acc;
 
-    ACCT_R(ev_b_addr);
-    ACCT_R(ev_a_addr);
+    ACCT_R(b);
+    ACCT_R(a);
+
+    ACCT_REG_R(DCPU16_REG_EX);
 
     acc = *b + *a + vm->reg[DCPU16_REG_EX];
     *b = acc & 0xffff;
@@ -1003,9 +1108,10 @@ OP_IMPL(adx) {
     else
         vm->reg[DCPU16_REG_EX] = 0x0000;
 
-    dcpu16_cycle_inc(vm, 3);
+    ACCT_REG_W(DCPU16_REG_EX);
+    ACCT_W(b);
 
-    ACCT_W(ev_b_addr);
+    dcpu16_cycle_inc(vm, 3);
 }
 
 OP_IMPL(sbx) {
@@ -1013,8 +1119,10 @@ OP_IMPL(sbx) {
     /* sets b to b-a+EX, sets EX to 0xffff if underflow, 0x0 otherwise */
     unsigned int acc;
 
-    ACCT_R(ev_b_addr);
-    ACCT_R(ev_a_addr);
+    ACCT_R(b);
+    ACCT_R(a);
+
+    ACCT_REG_R(DCPU16_REG_EX);
 
     acc = *b - *a + vm->reg[DCPU16_REG_EX];
     *b = acc & 0xffff;
@@ -1023,41 +1131,49 @@ OP_IMPL(sbx) {
     else
         vm->reg[DCPU16_REG_EX] = 0;
 
-    dcpu16_cycle_inc(vm, 3);
+    ACCT_REG_W(DCPU16_REG_EX);
+
+    ACCT_W(b);
 
-    ACCT_W(ev_b_addr);
+    dcpu16_cycle_inc(vm, 3);
 }
 
 OP_IMPL(sti) {
     OP_BASIC(sti);
     /* sets b to a, then increases I and J by 1 */
 
-    ACCT_R(ev_b_addr);
-    ACCT_R(ev_a_addr);
+    ACCT_R(b);
+    ACCT_R(a);
 
     *b = *a;
     vm->reg[DCPU16_REG_I] += 1;
     vm->reg[DCPU16_REG_J] += 1;
 
-    dcpu16_cycle_inc(vm, 2);
+    ACCT_REG_W(DCPU16_REG_I);
+    ACCT_REG_W(DCPU16_REG_J);
+
+    ACCT_W(b);
 
-    ACCT_W(ev_b_addr);
+    dcpu16_cycle_inc(vm, 2);
 }
 
 OP_IMPL(std) {
     OP_BASIC(std);
     /* sets b to a, then decreases I and J by 1 */
 
-    ACCT_R(ev_b_addr);
-    ACCT_R(ev_a_addr);
+    ACCT_R(b);
+    ACCT_R(a);
 
     *b = *a;
     vm->reg[DCPU16_REG_I] -= 1;
     vm->reg[DCPU16_REG_J] -= 1;
 
-    dcpu16_cycle_inc(vm, 2);
+    ACCT_REG_W(DCPU16_REG_I);
+    ACCT_REG_W(DCPU16_REG_J);
 
-    ACCT_W(ev_b_addr);
+    ACCT_W(b);
+
+    dcpu16_cycle_inc(vm, 2);
 }
 
 OP_IMPL(_reserved_) {
@@ -1413,13 +1529,21 @@ int dcpu16_hw_attach(struct dcpu16 *vm, struct dcpu16_hw *hw) {
  *  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;
+int dcpu16_acct_add(struct dcpu16 *vm, dcpu16_acct_event mask, dcpu16_ev_cb_t *fn, DCPU16_WORD addr_l, DCPU16_WORD addr_h, void *data) {
+    struct dcpu16_acct_cb cb = {
+        .mask = mask,
+        .addr_l = addr_l,
+        .addr_h = addr_h,
+        .fn = fn,
+        .data = data,
+    };
 
     if (!vm)
         return -1;
 
     cb.mask = mask;
+    cb.addr_l = addr_l;
+    cb.addr_h = addr_h;
     cb.fn = fn;
     cb.data = data;
 
index d0b366ac6d1a7e223eb357d7da6acf4012e4ff83..94e70a3fc02cf901c064ab622ecb2a57a561553e 100644 (file)
--- a/dcpu16.h
+++ b/dcpu16.h
@@ -59,12 +59,16 @@ typedef void (dcpu16_ev_cb_t)(struct dcpu16 *, dcpu16_acct_event, DCPU16_WORD, v
 #define DCPU16_ACCT_EV_CYCLE    (1<<1)
 #define DCPU16_ACCT_EV_READ     (1<<2)
 #define DCPU16_ACCT_EV_WRITE    (1<<3)
-#define DCPU16_ACCT_EV_NOP      (1<<4)
-#define DCPU16_ACCT_EV_RESET    (1<<5)
+#define DCPU16_ACCT_EV_REG_READ (1<<4)
+#define DCPU16_ACCT_EV_REG_WRITE (1<<5)
+#define DCPU16_ACCT_EV_NOP      (1<<6)
+#define DCPU16_ACCT_EV_RESET    (1<<7)
 struct dcpu16_acct_cb {
-    dcpu16_ev_cb_t *fn;
-    void *data;
-    dcpu16_acct_event mask;
+    dcpu16_ev_cb_t *fn; /* call this */
+    void *data;         /* also mention this */
+    dcpu16_acct_event mask; /* when (mask & event) is true and */
+    DCPU16_WORD addr_l, /* addr is this or higher and */
+                addr_h; /* addr is this or lower */
 };
 
 typedef void (dcpu16_hw_signal_t)(struct dcpu16 *, struct dcpu16_hw *);
@@ -128,7 +132,7 @@ int dcpu16_hw_ctl(struct dcpu16_hw *, const char *, void *, void *);
 int dcpu16_hw_attach(struct dcpu16 *, struct dcpu16_hw *);
 
 /* register a callback for an accounting event */
-int dcpu16_acct_add(struct dcpu16 *, dcpu16_acct_event, dcpu16_ev_cb_t *, void *);
+int dcpu16_acct_add(struct dcpu16 *, dcpu16_acct_event, dcpu16_ev_cb_t *, DCPU16_WORD, DCPU16_WORD, void *);
 
 /* execute the next instruction */
 void dcpu16_step(struct dcpu16 *);
index b4798838328aeae8e7d9698c9af64c25b79014a3..f7f4c156f7e9d2b443c7c64ec507fbb85c9f0c70 100644 (file)
@@ -228,6 +228,9 @@ struct rfb_instance_ *rfbScreen_next_available_display_(struct dynamic_array *rf
         return NULL;
     }
 
+    new_instance.screen->port += rfbScreens->entries;
+    new_instance.screen->ipv6port += rfbScreens->entries;
+
     new_instance.attached_display = NULL;
     new_instance.attached_keyboard = NULL;
     s = dynarray_add(rfbScreens, &new_instance);
@@ -496,20 +499,21 @@ COMMAND_HELP(set) {
     fprintf(f, "Sets addr to value.");
 }
 
-#define MICROSECONDS_PER_CYCLE 10
+#define NANOSECONDS_PER_CYCLE 10000
+#define MIN_NANOSLEEP 31000
 COMMAND_IMPL(run) {
     struct sigaction act;
-    struct timeval run_start_tv, run_stop_tv;
-    long long run_cycle_start;
-    struct timeval start_tv, now_tv, diff_tv;
+    long long run_cycle_start, run_cycle_end;
     long long cycle_start, cycles_to_wait;
-    struct timespec sleep_time, rem_time;
-    long long run_usec;
+
+    struct timespec ts_run_start, ts_run_end, ts_run_diff;
+    struct timespec ts_cycle_start, ts_cycle_end_target, ts_cycle_end, ts_cycle_waste, ts_cycle_rem;
+    const struct timespec ts_cycle_time = { .tv_sec = 0, .tv_nsec = NANOSECONDS_PER_CYCLE };
 
     (void)arg_count, (void)arg_vector;
 
     running_ = 1;
-    gettimeofday(&run_start_tv, NULL);
+    gettimespecofday(&ts_run_start);
     run_cycle_start = vm->cycle_;
 
     memset(&act, 0, sizeof act);
@@ -522,7 +526,9 @@ COMMAND_IMPL(run) {
     }
 
     while (running_) {
-        gettimeofday(&start_tv, NULL);
+        gettimespecofday(&ts_cycle_start);
+        ts_cycle_end_target = ts_cycle_start;
+
         cycle_start = vm->cycle_;
 
         dcpu16_step(vm);
@@ -536,37 +542,44 @@ COMMAND_IMPL(run) {
         /* how many cycles did this instr use? */
         cycles_to_wait = vm->cycle_ - cycle_start;
 
-        if (cycles_to_wait == 0)
-            continue;
-
-        /* each cycle wants 10 microseconds */
+        /* each cycle wants to take 10 microseconds */
+        while (cycles_to_wait--)
+            timespec_add(&ts_cycle_end_target, &ts_cycle_time);
 
         /* how much of that did we spend already */
-        gettimeofday(&now_tv, NULL);
-        timeval_subtract(&diff_tv, &now_tv, &start_tv);
-        /* do we have time to kill? */
-        if (cycles_to_wait * MICROSECONDS_PER_CYCLE > diff_tv.tv_usec) {
-            sleep_time.tv_sec = diff_tv.tv_sec;
-            /* this is not accurate.. */
-            sleep_time.tv_nsec = 250 * ( (cycles_to_wait * MICROSECONDS_PER_CYCLE) - diff_tv.tv_usec);
+        gettimespecofday(&ts_cycle_end);
 
+        /* do we have time to kill? */
+        if (timespec_subtract(&ts_cycle_waste, &ts_cycle_end_target, &ts_cycle_end) == 0) {
             /* nanosleep doesn't interfere with libvncserver, unlike usleep */
-            while ( nanosleep(&sleep_time, &rem_time) ) {
-                sleep_time = rem_time;
-                fprintf(stderr, "rem:%ld %ld\n", rem_time.tv_sec, rem_time.tv_nsec);
-            }
+            if (ts_cycle_waste.tv_sec == 0 && ts_cycle_waste.tv_nsec >= MIN_NANOSLEEP)
+                while ( nanosleep(&ts_cycle_waste, &ts_cycle_rem) )
+                    ts_cycle_waste = ts_cycle_rem;
+        } else {
+            /* negative, we've already blown our time */
+#if 0
+            fprintf(stderr, "cycle time overrun %ld.%09lds\n", ts_cycle_waste.tv_sec, ts_cycle_waste.tv_nsec);
+#endif
         }
+
+#if 0
+        /* how did we do */
+        gettimespecofday(&ts_cycle_end);
+        timespec_subtract(&ts_cycle_rem, &ts_cycle_end_target, &ts_cycle_end);
+        fprintf(stderr, "projected end: %ld.%09ld  actual end: %ld.%09ld  diff: %ld.%09ld\n",
+                ts_cycle_end_target.tv_sec, ts_cycle_end_target.tv_nsec,
+                ts_cycle_end.tv_sec, ts_cycle_end.tv_nsec,
+                ts_cycle_rem.tv_sec, ts_cycle_rem.tv_nsec);
+#endif
+
     }
 
-    gettimeofday(&run_stop_tv, NULL);
-    timeval_subtract(&diff_tv, &run_stop_tv, &run_start_tv);
-    run_usec = diff_tv.tv_sec * 1000000;
-    run_usec += diff_tv.tv_usec;
-    fprintf(stderr, "ran %llu cycles in %lds %dus (%lldus)\n",
-            vm->cycle_ - run_cycle_start,
-            diff_tv.tv_sec,
-            diff_tv.tv_usec,
-            run_usec);
+    run_cycle_end = vm->cycle_;
+    gettimespecofday(&ts_run_end);
+    timespec_subtract(&ts_run_diff, &ts_run_end, &ts_run_start);
+    fprintf(stderr, "ran %lld cycles in %ld.%09lds\n",
+            run_cycle_end - run_cycle_start,
+            ts_run_diff.tv_sec, ts_run_diff.tv_nsec);
 
     printf("interrupted...\n");