further changes for v1.7: cpu fixes, support for 'hardware' devices, display to vnc
authorJustin Wind <justin.wind@gmail.com>
Sat, 12 May 2012 18:55:51 +0000 (11:55 -0700)
committerJustin Wind <justin.wind@gmail.com>
Sat, 12 May 2012 18:55:51 +0000 (11:55 -0700)
small changes throughout for 1.7, normalized some variable names, all registers now indexable in array.  fixed behavior of branch-skip.  changes to hw interfaces.
added initial clock, keyboard, and display modules.
display module now supports rendering as vnc server.

hardware devices are still somewhat incomplete and lack testing.

12 files changed:
Makefile
dcpu16.c
dcpu16.h
display.c [deleted file]
display.h [deleted file]
hw_clock.c [new file with mode: 0644]
hw_clock.h [new file with mode: 0644]
hw_keyboard.c [new file with mode: 0644]
hw_keyboard.h [new file with mode: 0644]
hw_lem1802.c [new file with mode: 0644]
hw_lem1802.h [new file with mode: 0644]
vm-dcpu16.c

index f0f3b717c7a923d97f23943f323c954f0cb484f1..263c8e33bff371276c1e1a6af1ff872a98a8551b 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -6,7 +6,8 @@ CC = clang
 endif
 
 PROGRAMS = as-dcpu16 vm-dcpu16
-SOURCES = common.c dcpu16.c as-dcpu16.c vm-dcpu16.c display.c
+SOURCES = common.c dcpu16.c as-dcpu16.c vm-dcpu16.c \
+                 hw_clock.c hw_keyboard.c hw_lem1802.c
 
 CPPFLAGS += -DDEBUG
 
@@ -25,6 +26,8 @@ ifeq ($(UNAME),Linux)
 CPPFLAGS += -DHAVE_FOPENCOOKIE -D_GNU_SOURCE
 endif
 
+CPPFLAGS += -DHAVE_LIBVNCSERVER -I/opt/local/include
+LDFLAGS += -L/opt/local/lib -lvncserver
 
 all:   $(PROGRAMS)
 
@@ -37,7 +40,7 @@ depend:       .depend
 include .depend
 
 
-vm-dcpu16:     vm-dcpu16.o dcpu16.o common.o display.o
+vm-dcpu16:     vm-dcpu16.o dcpu16.o common.o hw_clock.o hw_keyboard.o hw_lem1802.o
 
 as-dcpu16:     as-dcpu16.o dcpu16.o common.o
 
index 75c6cf0df39c3d0d33ce0bd06cf6bad984dd17b2..d7b3d4822eb25cf61edf0cd2c4b9fd1f5f6dbd48 100644 (file)
--- a/dcpu16.c
+++ b/dcpu16.c
  *  
  *  Justin Wind <justin.wind@gmail.com>
  *    2012 04 05 - implementation started
- *    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
  *    2012 05 05 - start of v1.7 revisions
  *    2012 05 08 - v1.7 revisions mostly complete
  *
@@ -42,7 +38,23 @@ static const char * const src_id_ = "$Id$";
 #define OPCODE_OPERAND_B_BITS 5
 #define OPCODE_OPERAND_A_BITS 6
 
-static const char * const regnames_ = "ABCXYZIJ";
+const char * const dcpu16_reg_names[] = {
+    "A",
+    "B",
+    "C",
+    "X",
+    "Y",
+    "Z",
+    "I",
+    "J",
+    "PC",
+    "SP",
+    "EX",
+    "IA",
+    NULL
+};
+
+static void printf_null_(char *fmt, ...) { (void)fmt; }
 
 /* some default warning and debug reporting functions, which can be overridden by clients */
 #define WARN(...) do { if (warn_cb_) warn_cb_(__VA_ARGS__); } while (0)
@@ -60,7 +72,10 @@ void warn_(char *fmt, ...) {
 }
 static void (*warn_cb_)(char *fmt, ...) = warn_;
 void dcpu16_warn_cb_set(void (*fn)(char *fmt, ...)) {
-    warn_cb_ = fn;
+    if (fn)
+        warn_cb_ = fn;
+    else
+        warn_cb_ = printf_null_;
 }
 
 #ifdef DEBUG
@@ -88,7 +103,10 @@ static void (*trace_cb_)(char *fmt, ...) =
 #endif
     ;
 void dcpu16_trace_cb_set(void (*fn)(char *fmt, ...)) {
-    trace_cb_ = fn;
+    if (fn)
+        trace_cb_ = fn;
+    else
+        trace_cb_ = printf_null_;
 }
 
 
@@ -106,21 +124,16 @@ void acct_event_(struct dcpu16 *vm, dcpu16_acct_event ev, DCPU16_WORD addr) {
     }
 }
 
-/* convert register name to index into register array */
-static inline
-off_t reg_index_(int reg) {
-    return strchr(regnames_, reg) - regnames_;
-}
 
 /* add an entry to the interrupt queue */
 static
-int interrupt_enqueue_(struct dcpu16 *d, DCPU16_WORD message) {
-    d->interrupts_[d->interrupts_tail_] = message;
-    d->interrupts_tail_ += 1;
-    d->interrupts_tail_ %= DCPU16_INTERRUPT_QUEUE_SIZE;
+int interrupt_enqueue_(struct dcpu16 *vm, DCPU16_WORD message) {
+    vm->interrupts_[vm->interrupts_tail_] = message;
+    vm->interrupts_tail_ += 1;
+    vm->interrupts_tail_ %= DCPU16_INTERRUPT_QUEUE_SIZE;
 
-    if (d->interrupts_tail_ == d->interrupts_head_) {
-        d->on_fire_ = 1;
+    if (vm->interrupts_tail_ == vm->interrupts_head_) {
+        vm->on_fire_ = 1;
         WARN("interrupt queue overflow (system is now on fire)");
         return -1;
     }
@@ -129,17 +142,17 @@ int interrupt_enqueue_(struct dcpu16 *d, DCPU16_WORD message) {
 }
 
 static
-DCPU16_WORD interrupt_dequeue_(struct dcpu16 *d) {
+DCPU16_WORD interrupt_dequeue_(struct dcpu16 *vm) {
     DCPU16_WORD message;
 
-    if (d->interrupts_tail_ == d->interrupts_head_) {
+    if (vm->interrupts_tail_ == vm->interrupts_head_) {
         WARN("interrupt underflow");
         return 0;
     }
 
-    message = d->interrupts_[d->interrupts_head_];
-    d->interrupts_head_ += 1;
-    d->interrupts_head_ %= DCPU16_INTERRUPT_QUEUE_SIZE;
+    message = vm->interrupts_[vm->interrupts_head_];
+    vm->interrupts_head_ += 1;
+    vm->interrupts_head_ %= DCPU16_INTERRUPT_QUEUE_SIZE;
 
     return message;
 }
@@ -152,20 +165,20 @@ DCPU16_WORD interrupt_dequeue_(struct dcpu16 *d) {
  * e_addr is set to a referenced address, for accounting callback
  * pc_adjust is set to how to change the program counter
  * stack_adjust is set to how to change the stack pointer
- * cycles is set to number of cycles spent looking up operand
+ * cycle_adjust is set to number of cycles spent looking up operand
  *
  * zero all adjustables before decoding first operand, and pass in these values when
  * decoding next operand..
  *
  */
 static inline
-void value_decode_(struct dcpu16 *d, DCPU16_WORD value, unsigned int value_is_a, DCPU16_WORD value_data,
+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,
                   short *pc_adjust, short *sp_adjust, unsigned int *cycle_adjust) {
     assert(value <= 0x3f);
 
-    DCPU16_WORD pc = (DCPU16_WORD)(d->pc + *pc_adjust),
-                sp = (DCPU16_WORD)(d->sp + *sp_adjust);
+    DCPU16_WORD pc = (DCPU16_WORD)(vm->reg[DCPU16_REG_PC] + *pc_adjust),
+                sp = (DCPU16_WORD)(vm->reg[DCPU16_REG_PC] + *sp_adjust);
 
     TRACE("%s: pc:0x%04x sp:0x%04x value_data:0x%04x\n", 
           __func__,
@@ -174,21 +187,21 @@ void value_decode_(struct dcpu16 *d, DCPU16_WORD value, unsigned int value_is_a,
           value_data);
 
     if (value <= 0x07) { /* register */
-        *v = d->reg + value;
-        TRACE("%s>>     %c (0x%04x)",
+        *v = vm->reg + value;
+        TRACE("%s>>     %s (0x%04x)",
               __func__,
-              regnames_[value],
+              dcpu16_reg_names[value],
               **v);
         return;
     }
 
     if (value <= 0x0f) { /* [register] */
-        *v = &(d->ram[ d->reg[value & 0x07] ]);
-        *e_addr = d->reg[value & 0x07];
-        TRACE("%s>>     [%c] [0x%04x] (0x%04x)",
+        *v = &(vm->ram[ vm->reg[value & 0x07] ]);
+        *e_addr = vm->reg[value & 0x07];
+        TRACE("%s>>     [%s] [0x%04x] (0x%04x)",
               __func__,
-              regnames_[value & 0x07],
-              d->reg[value & 0x07],
+              dcpu16_reg_names[value & 0x07],
+              vm->reg[value & 0x07],
               **v);
         return;
     }
@@ -196,13 +209,13 @@ void value_decode_(struct dcpu16 *d, DCPU16_WORD value, unsigned int value_is_a,
     if (value <= 0x17) { /* [next word + register] */
         *pc_adjust += 1; /* consume next word */
         *cycle_adjust += 1;
-        *e_addr = value_data + d->reg[value & 0x07];
-        *v = d->ram + *e_addr;
-        TRACE("%s>>     [nextword + %c] [0x%04x + 0x%04x] (0x%04x)",
+        *e_addr = value_data + vm->reg[value & 0x07];
+        *v = vm->ram + *e_addr;
+        TRACE("%s>>     [nextword + %s] [0x%04x + 0x%04x] (0x%04x)",
               __func__,
-              regnames_[value & 0x07],
+              dcpu16_reg_names[value & 0x07],
               value_data,
-              d->reg[value & 0x07],
+              vm->reg[value & 0x07],
               **v);
         return;
     }
@@ -210,12 +223,12 @@ void value_decode_(struct dcpu16 *d, DCPU16_WORD value, unsigned int value_is_a,
     switch (value) {
         case 0x18: /* PUSH/[--SP] or POP/[SP++] */
         if (value_is_a == 0) { /* b */
-            *v = &(d->ram[sp - 1]);
+            *v = &(vm->ram[sp - 1]);
             *sp_adjust -= 1;
             *e_addr = sp - 1;
             TRACE("%s>>     PUSH [0x%04x] (0x%04x)", __func__, sp - 1, **v);
         } else { /* a */
-            *v = &(d->ram[sp]);
+            *v = &(vm->ram[sp]);
             *sp_adjust += 1;
             *e_addr = sp;
             TRACE("%s>>     POP [0x%04x] (0x%04x)", __func__, sp, **v);
@@ -223,7 +236,7 @@ void value_decode_(struct dcpu16 *d, DCPU16_WORD value, unsigned int value_is_a,
         break;
 
         case 0x19: /* PEEK/[SP] */
-        *v = &(d->ram[sp]);
+        *v = &(vm->ram[sp]);
         *e_addr = sp;
         TRACE("%s>>     PEEK [0x%04x] (0x%04x)",
               __func__,
@@ -235,7 +248,7 @@ void value_decode_(struct dcpu16 *d, DCPU16_WORD value, unsigned int value_is_a,
         *pc_adjust += 1;
         *cycle_adjust += 1;
         *e_addr = sp + value_data;
-        *v = d->ram + *e_addr;
+        *v = vm->ram + *e_addr;
         TRACE("%s>>     PICK 0x%04x [0x%04x] (0x%04x)",
               __func__,
               value_data,
@@ -244,26 +257,26 @@ void value_decode_(struct dcpu16 *d, DCPU16_WORD value, unsigned int value_is_a,
         break;
 
         case 0x1b: /* SP */
-        *v = &(d->sp);
+        *v = &(vm->reg[DCPU16_REG_SP]);
         TRACE("%s>>     %s (0x%04x)",
               __func__,
-              "SP",
+              dcpu16_reg_names[DCPU16_REG_SP],
               **v);
         break;
 
         case 0x1c: /* PC */
-        *v = &(d->pc);
+        *v = &(vm->reg[DCPU16_REG_PC]);
         TRACE("%s>>     %s (0x%04x)",
               __func__,
-              "PC",
+              dcpu16_reg_names[DCPU16_REG_PC],
               **v);
         break;
 
         case 0x1d: /* EX */
-        *v = &(d->ex);
+        *v = &(vm->reg[DCPU16_REG_EX]);
         TRACE("%s>>     %s (0x%04x)",
               __func__,
-              "EX",
+              dcpu16_reg_names[DCPU16_REG_EX],
               **v);
         break;
 
@@ -271,7 +284,7 @@ void value_decode_(struct dcpu16 *d, DCPU16_WORD value, unsigned int value_is_a,
         *pc_adjust += 1;
         *cycle_adjust += 1;
         *e_addr = value_data;
-        *v = d->ram + *e_addr;
+        *v = vm->ram + *e_addr;
         TRACE("%s>>     [nextword] [0x%04x] (0x%04x)",
               __func__,
               value_data,
@@ -310,7 +323,7 @@ struct opcode_entry {
 /* opcode doesn't adjust its own PC, the step function which invoked it handles that */
 /* opcode does adjust stack and cycle count */
 
-#define OP_IMPL(x) static void op_##x(struct dcpu16 *d, DCPU16_WORD val_b, DCPU16_WORD val_b_data, DCPU16_WORD val_a, DCPU16_WORD val_a_data)
+#define OP_IMPL(x) static void op_##x(struct dcpu16 *vm, DCPU16_WORD val_b, DCPU16_WORD val_b_data, DCPU16_WORD val_a, DCPU16_WORD val_a_data)
 
 #define OP_TYPE(op_type) DCPU16_WORD *a, *b;\
     DCPU16_WORD ev_a_addr = 0, ev_b_addr = 0;\
@@ -318,15 +331,15 @@ struct opcode_entry {
     unsigned int cycle_adjust = 0;\
     do {\
         op_type;\
-        value_decode_(d, val_a, 1, val_a_data,\
-                      &d->reg_work_[1], &a, &ev_a_addr,\
+        value_decode_(vm, val_a, 1, val_a_data,\
+                      &vm->reg_work_[1], &a, &ev_a_addr,\
                       &pc_adjust, &sp_adjust, &cycle_adjust);\
-        d->sp += sp_adjust;\
-        d->cycle += cycle_adjust;\
+        vm->reg[DCPU16_REG_SP] += sp_adjust;\
+        vm->cycle += cycle_adjust;\
     } while (0)
 #define OP_NBI_ (void)val_b, (void)b, (void)ev_b_addr, (void)val_b_data
-#define OP_BASIC_ value_decode_(d, val_b, 0, val_b_data,\
-                                &d->reg_work_[0], &b, &ev_b_addr,\
+#define OP_BASIC_ value_decode_(vm, val_b, 0, val_b_data,\
+                                &vm->reg_work_[0], &b, &ev_b_addr,\
                                 &pc_adjust, &sp_adjust, &cycle_adjust)
 #define OP_BASIC(x) OP_TYPE(OP_BASIC_)
 #define OP_NBI(x) OP_TYPE(OP_NBI_)
@@ -337,9 +350,9 @@ 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_(d, DCPU16_ACCT_EV_READ, addr); } while (0)
-#define ACCT_W(addr) do { acct_event_(d, DCPU16_ACCT_EV_WRITE, addr); } while (0)
-#define ACCT_ILL(addr) do { acct_event_(d, DCPU16_ACCT_EV_NOP, addr); } while (0)
+#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)
 
 /* extended opcodes */
 
@@ -355,10 +368,10 @@ OP_IMPL(nbi__reserved_) {
     /* reserved for future expansion */
 
     /* fire an illegal instruction event for current instruction */
-    DCPU16_WORD future_opcode = (d->ram[d->pc - pc_adjust] >> (OPCODE_BASIC_BITS + OPCODE_OPERAND_B_BITS));
+    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(d->pc - pc_adjust);
+    ACCT_ILL(vm->reg[DCPU16_REG_PC] - pc_adjust);
 }
 
 OP_IMPL(nbi_jsr) {
@@ -367,12 +380,12 @@ OP_IMPL(nbi_jsr) {
 
     ACCT_R(ev_a_addr);
 
-    d->ram[ --d->sp ] = d->pc;
-    d->pc = *a;
+    vm->ram[ --vm->reg[DCPU16_REG_SP] ] = vm->reg[DCPU16_REG_PC];
+    vm->reg[DCPU16_REG_PC] = *a;
 
-    d->cycle += 2;
+    vm->cycle += 2;
 
-    ACCT_W(d->sp + 1);
+    ACCT_W(vm->reg[DCPU16_REG_SP] + 1);
 }
 
 OP_IMPL(nbi__reserved2_) {
@@ -381,7 +394,7 @@ OP_IMPL(nbi__reserved2_) {
 
     WARN("reserved nbi opcode invoked");
 
-    ACCT_ILL(d->pc - pc_adjust);
+    ACCT_ILL(vm->reg[DCPU16_REG_PC] - pc_adjust);
 }
 
 OP_IMPL(nbi_int) {
@@ -389,29 +402,29 @@ OP_IMPL(nbi_int) {
 
     ACCT_R(ev_a_addr);
 
-    if (d->ia) {
-        if ( interrupt_enqueue_(d, *a) ) {
+    if (vm->reg[DCPU16_REG_IA]) {
+        if ( interrupt_enqueue_(vm, *a) ) {
             WARN("failed to queue interrupt");
             return;
         }
 
-        if (d->interrupts_deferred_)
+        if (vm->interrupts_deferred_)
             return;
 
-        d->interrupts_deferred_ = 1;
-        d->ram[--d->sp] = d->pc;
-        d->ram[--d->sp] = d->reg[reg_index_('A')];
-        d->pc = d->ia;
-        d->reg[0] = *a;
+        vm->interrupts_deferred_ = 1;
+        vm->ram[--vm->reg[DCPU16_REG_SP]] = vm->reg[DCPU16_REG_PC];
+        vm->ram[--vm->reg[DCPU16_REG_SP]] = vm->reg[DCPU16_REG_A];
+        vm->reg[DCPU16_REG_PC] = vm->reg[DCPU16_REG_IA];
+        vm->reg[0] = *a;
     }
 
-    d->cycle += 4;
+    vm->cycle += 4;
 }
 
 OP_IMPL(nbi_iag) {
     OP_NBI(nbi_iag);
 
-    *a = d->ia;
+    *a = vm->reg[DCPU16_REG_IA];
 
     ACCT_W(ev_a_addr);
 }
@@ -419,7 +432,7 @@ OP_IMPL(nbi_iag) {
 OP_IMPL(nbi_ias) {
     OP_NBI(nbi_ias);
 
-    d->ia = *a;
+    vm->reg[DCPU16_REG_IA] = *a;
 
     ACCT_R(ev_a_addr);
 }
@@ -428,18 +441,18 @@ OP_IMPL(nbi_ias) {
 OP_IMPL(nbi_rfi) {
     OP_NBI(nbi_rfi);
 
-    d->interrupts_deferred_ = 0;
-    d->reg[reg_index_('A')] = d->ram[d->sp++];
-    d->pc = d->ram[d->sp++];
+    vm->interrupts_deferred_ = 0;
+    vm->reg[DCPU16_REG_A] = vm->ram[vm->reg[DCPU16_REG_SP]++];
+    vm->reg[DCPU16_REG_PC] = vm->ram[vm->reg[DCPU16_REG_SP]++];
 }
 
 OP_IMPL(nbi_iaq) {
     OP_NBI(nbi_iaq);
 
     if (*a) {
-        d->interrupts_deferred_ = 1;
+        vm->interrupts_deferred_ = 1;
     } else {
-        d->interrupts_deferred_ = 0;
+        vm->interrupts_deferred_ = 0;
     }
 
     ACCT_R(ev_a_addr);
@@ -450,9 +463,9 @@ OP_IMPL(nbi_hwn) {
 
     ACCT_W(ev_a_addr);
 
-    *a = d->hw_table_entries_;
+    *a = vm->hw_table_entries_;
 
-    d->cycle += 2;
+    vm->cycle += 2;
 }
 
 OP_IMPL(nbi_hwq) {
@@ -460,23 +473,23 @@ OP_IMPL(nbi_hwq) {
 
     ACCT_R(ev_a_addr);
 
-    if (*a >= d->hw_table_entries_) {
+    if (*a >= vm->hw_table_entries_) {
         WARN("hardware query for non-extant device 0x%04x", *a);
-        d->reg[reg_index_('A')] = 0;
-        d->reg[reg_index_('B')] = 0;
-        d->reg[reg_index_('C')] = 0;
-        d->reg[reg_index_('X')] = 0;
-        d->reg[reg_index_('Y')] = 0;
+        vm->reg[DCPU16_REG_A] = 0;
+        vm->reg[DCPU16_REG_B] = 0;
+        vm->reg[DCPU16_REG_C] = 0;
+        vm->reg[DCPU16_REG_X] = 0;
+        vm->reg[DCPU16_REG_Y] = 0;
         return;
     }
 
-    d->reg[reg_index_('A')] = d->hw_table_[*a].id_l;
-    d->reg[reg_index_('B')] = d->hw_table_[*a].id_h;
-    d->reg[reg_index_('C')] = d->hw_table_[*a].ver;
-    d->reg[reg_index_('X')] = d->hw_table_[*a].mfg_l;
-    d->reg[reg_index_('Y')] = d->hw_table_[*a].mfg_h;
+    vm->reg[DCPU16_REG_A] = vm->hw_table_[*a].id_l;
+    vm->reg[DCPU16_REG_B] = vm->hw_table_[*a].id_h;
+    vm->reg[DCPU16_REG_C] = vm->hw_table_[*a].ver;
+    vm->reg[DCPU16_REG_X] = vm->hw_table_[*a].mfg_l;
+    vm->reg[DCPU16_REG_Y] = vm->hw_table_[*a].mfg_h;
 
-    d->cycle += 4;
+    vm->cycle += 4;
 }
 
 OP_IMPL(nbi_hwi) {
@@ -484,13 +497,16 @@ OP_IMPL(nbi_hwi) {
 
     ACCT_R(ev_a_addr);
 
-    if (*a > d->hw_table_entries_) {
+    if (*a > vm->hw_table_entries_) {
         WARN("interrupt for non-extant device 0x%04x", *a);
         return;
     }
 
-    d->cycle += 4;
-    d->hw_table_[*a].int_fn(d, d->hw_table_[*a].data);
+    vm->cycle += 4;
+    if (vm->hw_table_[*a].hwi)
+        vm->hw_table_[*a].hwi(vm, vm->hw_table_[*a].data);
+    else
+        WARN("hardware 0x%04x has no interrupt handler", *a);
 }
 
 OP_IMPL(nbi_hcf) {
@@ -498,10 +514,10 @@ OP_IMPL(nbi_hcf) {
 
     ACCT_R(ev_a_addr);
 
-    d->on_fire_ = 1;
+    vm->on_fire_ = 1;
     WARN("system on fire");
 
-    d->cycle += 9;
+    vm->cycle += 9;
 }
 
 static const struct opcode_entry opcode_nbi_entries[] = {
@@ -533,7 +549,7 @@ static const struct opcode_entry opcode_nbi_entries[] = {
 /* basic opcodes */
 
 /*
-    N.B. the following function does not decode values, (thus does not advance sp)
+    N.B. the following function does not decode values.
     Decoding is handled by the secondary opcode functions it calls.
 */
 OP_IMPL(_nbi_) {
@@ -551,7 +567,7 @@ OP_IMPL(_nbi_) {
     assert(e->impl != NULL);
 
     TRACE(">> %s 0x%04x", e->name, val_b);
-    e->impl(d, 0, 0, val_a, val_a_data);
+    e->impl(vm, 0, 0, val_a, val_a_data);
 }
 
 OP_IMPL(set) {
@@ -566,7 +582,7 @@ OP_IMPL(set) {
      */
     *b = *a;
 
-    d->cycle += 1;
+    vm->cycle += 1;
 
     ACCT_W(ev_b_addr);
 }
@@ -580,9 +596,9 @@ OP_IMPL(add) {
     ACCT_R(ev_a_addr);
 
     *b = acc;
-    d->ex = (acc > 0xffff);
+    vm->reg[DCPU16_REG_EX] = (acc > 0xffff);
 
-    d->cycle += 2;
+    vm->cycle += 2;
 
     ACCT_W(ev_b_addr);
 }
@@ -596,9 +612,9 @@ OP_IMPL(sub) {
     ACCT_R(ev_a_addr);
 
     *b = acc;
-    d->ex = (acc > 0xffff);
+    vm->reg[DCPU16_REG_EX] = (acc > 0xffff);
 
-    d->cycle += 2;
+    vm->cycle += 2;
 
     ACCT_W(ev_b_addr);
 }
@@ -612,9 +628,9 @@ OP_IMPL(mul) {
     ACCT_R(ev_a_addr);
 
     *b = acc;
-    d->ex = acc >> 16;
+    vm->reg[DCPU16_REG_EX] = acc >> 16;
 
-    d->cycle += 2;
+    vm->cycle += 2;
 
     ACCT_W(ev_b_addr);
 }
@@ -628,9 +644,9 @@ OP_IMPL(mli) {
     ACCT_R(ev_a_addr);
 
     *b = acc;
-    d->ex = acc >> 16;
+    vm->reg[DCPU16_REG_EX] = acc >> 16;
 
-    d->cycle += 2;
+    vm->cycle += 2;
 
     ACCT_W(ev_b_addr);
 }
@@ -644,13 +660,13 @@ OP_IMPL(div) {
 
     if (*a == 0) {
         *b = 0;
-        d->ex = 0;
+        vm->reg[DCPU16_REG_EX] = 0;
     } else {
         *b = *b / *a;
-        d->ex = (*b << 16) / *a;
+        vm->reg[DCPU16_REG_EX] = (*b << 16) / *a;
     }
 
-    d->cycle += 3;
+    vm->cycle += 3;
 
     ACCT_W(ev_b_addr);
 }
@@ -664,13 +680,13 @@ OP_IMPL(dvi) {
 
     if (*a == 0) {
         *b = 0;
-        d->ex = 0;
+        vm->reg[DCPU16_REG_EX] = 0;
     } else {
         *b = (short)*b / (short)*a;
-        d->ex = (short)(*b << 16) / (short)*a;
+        vm->reg[DCPU16_REG_EX] = (short)(*b << 16) / (short)*a;
     }
 
-    d->cycle += 3;
+    vm->cycle += 3;
 
     ACCT_W(ev_b_addr);
 }
@@ -688,7 +704,7 @@ OP_IMPL(mod) {
         *b = *b % *a;
     }
 
-    d->cycle += 3;
+    vm->cycle += 3;
 
     ACCT_W(ev_a_addr);
 }
@@ -706,7 +722,7 @@ OP_IMPL(mdi) {
         *b = (short)*b % (short)*a;
     }
 
-    d->cycle += 3;
+    vm->cycle += 3;
 
     ACCT_W(ev_b_addr);
 }
@@ -720,7 +736,7 @@ OP_IMPL(and) {
 
     *b = *b & *a;
 
-    d->cycle += 1;
+    vm->cycle += 1;
 
     ACCT_W(ev_b_addr);
 }
@@ -734,7 +750,7 @@ OP_IMPL(bor) {
 
     *b = *b | *a;
 
-    d->cycle += 1;
+    vm->cycle += 1;
 
     ACCT_W(ev_b_addr);
 }
@@ -748,7 +764,7 @@ OP_IMPL(xor) {
 
     *b = *b ^ *a;
 
-    d->cycle += 1;
+    vm->cycle += 1;
 
     ACCT_W(ev_b_addr);
 }
@@ -762,9 +778,9 @@ OP_IMPL(shr) {
     ACCT_R(ev_a_addr);
 
     *b = acc & 0xffff;
-    d->ex = (*b << 16) >> *a;
+    vm->reg[DCPU16_REG_EX] = (*b << 16) >> *a;
 
-    d->cycle += 2;
+    vm->cycle += 2;
 
     WARN("IMPLEMENT");
 
@@ -780,9 +796,9 @@ OP_IMPL(asr) {
     ACCT_R(ev_a_addr);
 
     *b = acc & 0xffff;
-    d->ex = (*b << 16) >> *a;
+    vm->reg[DCPU16_REG_EX] = (*b << 16) >> *a;
 
-    d->cycle += 2;
+    vm->cycle += 2;
 
     WARN("IMPLEMENT");
 
@@ -799,9 +815,9 @@ OP_IMPL(shl) {
 
     *b = acc;
 
-    d->ex = acc >> 16;
+    vm->reg[DCPU16_REG_EX] = acc >> 16;
 
-    d->cycle += 2;
+    vm->cycle += 2;
 
     ACCT_W(ev_b_addr);
 }
@@ -816,11 +832,11 @@ OP_IMPL(ifb) {
     if ((*b & *a) != 0) {
         /* */
     } else {
-        d->skip_ = 1;
-        d->cycle += 1;
+        vm->skip_ = 1;
+        vm->cycle += 1;
     }
 
-    d->cycle += 2;
+    vm->cycle += 2;
 }
 
 OP_IMPL(ifc) {
@@ -831,13 +847,13 @@ OP_IMPL(ifc) {
     ACCT_R(ev_a_addr);
 
     if ((*b & *a) == 0) {
-        
+        /* */
     } else {
-        d->skip_ = 1;
-        d->cycle += 1;
+        vm->skip_ = 1;
+        vm->cycle += 1;
     }
 
-    d->cycle += 2;
+    vm->cycle += 2;
 }
 
 OP_IMPL(ife) {
@@ -850,11 +866,11 @@ OP_IMPL(ife) {
     if (*b == *a) {
         /* */
     } else {
-        d->skip_ = 1;
-        d->cycle += 1;
+        vm->skip_ = 1;
+        vm->cycle += 1;
     }
 
-    d->cycle += 2;
+    vm->cycle += 2;
 }
 
 OP_IMPL(ifn) {
@@ -867,11 +883,11 @@ OP_IMPL(ifn) {
     if (*b != *a) {
         /* */
     } else {
-        d->skip_ = 1;
-        d->cycle++;
+        vm->skip_ = 1;
+        vm->cycle++;
     }
 
-    d->cycle += 2;
+    vm->cycle += 2;
 }
 
 OP_IMPL(ifg) {
@@ -884,11 +900,11 @@ OP_IMPL(ifg) {
     if (*b > *a) {
         /* */
     } else {
-        d->skip_ = 1;
-        d->cycle++;
+        vm->skip_ = 1;
+        vm->cycle++;
     }
 
-    d->cycle += 2;
+    vm->cycle += 2;
 }
 
 OP_IMPL(ifa) {
@@ -901,11 +917,11 @@ OP_IMPL(ifa) {
     if (*b > *a) {
         /* */
     } else {
-        d->skip_ = 1;
-        d->cycle += 1;
+        vm->skip_ = 1;
+        vm->cycle += 1;
     }
 
-    d->cycle += 2;
+    vm->cycle += 2;
 }
 
 OP_IMPL(ifl) {
@@ -918,11 +934,11 @@ OP_IMPL(ifl) {
     if (*b < *a) {
         /* */
     } else {
-        d->skip_ = 1;
-        d->cycle++;
+        vm->skip_ = 1;
+        vm->cycle++;
     }
 
-    d->cycle += 2;
+    vm->cycle += 2;
 }
 
 OP_IMPL(ifu) {
@@ -935,11 +951,11 @@ OP_IMPL(ifu) {
     if (*b < *a) {
         /* */
     } else {
-        d->skip_ = 1;
-        d->cycle += 1;
+        vm->skip_ = 1;
+        vm->cycle += 1;
     }
 
-    d->cycle += 2;
+    vm->cycle += 2;
 }
 
 OP_IMPL(adx) {
@@ -950,14 +966,14 @@ OP_IMPL(adx) {
     ACCT_R(ev_b_addr);
     ACCT_R(ev_a_addr);
 
-    acc = *b + *a + d->ex;
+    acc = *b + *a + vm->reg[DCPU16_REG_EX];
     *b = acc & 0xffff;
     if (acc > 0xffff)
-        d->ex = 0x0001;
+        vm->reg[DCPU16_REG_EX] = 0x0001;
     else
-        d->ex = 0x0000;
+        vm->reg[DCPU16_REG_EX] = 0x0000;
 
-    d->cycle += 3;
+    vm->cycle += 3;
 
     ACCT_W(ev_b_addr);
 }
@@ -970,14 +986,14 @@ OP_IMPL(sbx) {
     ACCT_R(ev_b_addr);
     ACCT_R(ev_a_addr);
 
-    acc = *b - *a + d->ex;
+    acc = *b - *a + vm->reg[DCPU16_REG_EX];
     *b = acc & 0xffff;
     if (acc > 0xffff)
-        d->ex = 0xffff;
+        vm->reg[DCPU16_REG_EX] = 0xffff;
     else
-        d->ex = 0;
+        vm->reg[DCPU16_REG_EX] = 0;
 
-    d->cycle += 3;
+    vm->cycle += 3;
 
     ACCT_W(ev_b_addr);
 }
@@ -990,10 +1006,10 @@ OP_IMPL(sti) {
     ACCT_R(ev_a_addr);
 
     *b = *a;
-    d->reg[reg_index_('I')] += 1;
-    d->reg[reg_index_('J')] += 1;
+    vm->reg[DCPU16_REG_I] += 1;
+    vm->reg[DCPU16_REG_J] += 1;
 
-    d->cycle += 2;
+    vm->cycle += 2;
 
     ACCT_W(ev_b_addr);
 }
@@ -1006,10 +1022,10 @@ OP_IMPL(std) {
     ACCT_R(ev_a_addr);
 
     *b = *a;
-    d->reg[reg_index_('I')] -= 1;
-    d->reg[reg_index_('J')] -= 1;
+    vm->reg[DCPU16_REG_I] -= 1;
+    vm->reg[DCPU16_REG_J] -= 1;
 
-    d->cycle += 2;
+    vm->cycle += 2;
 
     ACCT_W(ev_b_addr);
 }
@@ -1019,7 +1035,7 @@ OP_IMPL(_reserved_) {
 
     WARN("reserved opcode invoked");
 
-    ACCT_ILL(d->pc - pc_adjust);
+    ACCT_ILL(vm->reg[DCPU16_REG_PC] - pc_adjust);
 }
 
 static const struct opcode_entry opcode_basic_entries[] = {
@@ -1061,11 +1077,11 @@ static const struct opcode_entry opcode_basic_entries[] = {
 static inline
 void dump_operand_value_(DCPU16_WORD value, DCPU16_WORD nextword, unsigned int value_position) {
     if (value <= 0x07) {
-        printf(" %c", regnames_[value]);
+        printf(" %s", dcpu16_reg_names[value]);
     } else if (value <= 0x0f) {
-        printf(" [%c]", regnames_[value & 0x07]);
+        printf(" [%s]", dcpu16_reg_names[value & 0x07]);
     } else if (value <= 0x17) {
-        printf(" [0x%04x + %c]", nextword, regnames_[value & 0x07]);
+        printf(" [0x%04x + %s]", nextword, dcpu16_reg_names[value & 0x07]);
     } else switch (value) {
         case 0x18:
             if (value_position == 0) { /* b */
@@ -1160,13 +1176,13 @@ DCPU16_WORD dcpu16_mnemonify_buf(DCPU16_WORD *buf) {
     print the words of the instruction at addr, followed by its assembly representation
     returns the length of the instruction in words
  */
-DCPU16_WORD dcpu16_disassemble_print(struct dcpu16 *d, DCPU16_WORD addr) {
+DCPU16_WORD dcpu16_disassemble_print(struct dcpu16 *vm, DCPU16_WORD addr) {
     DCPU16_WORD opcode, b, a, instr_len, i, *b_data, *a_data;
-    DCPU16_WORD buf[3] = { d->ram[addr], d->ram[(DCPU16_WORD)(addr + 1)], d->ram[(DCPU16_WORD)(addr + 2)] };
+    DCPU16_WORD buf[3] = { vm->ram[addr], vm->ram[(DCPU16_WORD)(addr + 1)], vm->ram[(DCPU16_WORD)(addr + 2)] };
     unsigned int indent = 0;
     unsigned int partial = 0;
 
-    if (!d) return 0;
+    if (!vm) return 0;
 
 #if 0
     /*
@@ -1175,7 +1191,7 @@ DCPU16_WORD dcpu16_disassemble_print(struct dcpu16 *d, DCPU16_WORD addr) {
         could be data which happen to match instructions..
     */
     for (i = 3; i; i--) {
-        instruction_decode_(d->ram, (DCPU16_WORD)(addr - i), &opcode, &b, &b_data, &a, &a_data, &instr_len);
+        instruction_decode_(vm->ram, (DCPU16_WORD)(addr - i), &opcode, &b, &b_data, &a, &a_data, &instr_len);
         if (instr_len > i)
             partial++;
         if (instr_len == i
@@ -1187,12 +1203,12 @@ DCPU16_WORD dcpu16_disassemble_print(struct dcpu16 *d, DCPU16_WORD addr) {
 #endif
 
     /* just need instr_len */
-    instruction_decode_(d->ram, addr, &opcode, &b, &b_data, &a, &a_data, &instr_len);
+    instruction_decode_(vm->ram, addr, &opcode, &b, &b_data, &a, &a_data, &instr_len);
 
     /* show the raw words */
-    printf("%04x", d->ram[addr]);
+    printf("%04x", vm->ram[addr]);
     for (i = 1; i < instr_len; i++) {
-        printf(" %04x", d->ram[addr + i]);
+        printf(" %04x", vm->ram[addr + i]);
     }
 
     /* align things neatly, show the instruction */
@@ -1207,64 +1223,78 @@ DCPU16_WORD dcpu16_disassemble_print(struct dcpu16 *d, DCPU16_WORD addr) {
     return instr_len;
 }
 
+int dcpu16_interrupt(struct dcpu16 *vm, DCPU16_WORD message) {
+    TRACE("%s>> message:0x%04x", __func__, message);
+    return interrupt_enqueue_(vm, message);
+}
+
 /* execute the next instruction */
-void dcpu16_step(struct dcpu16 *d) {
+void dcpu16_step(struct dcpu16 *vm) {
     DCPU16_WORD opcode, b, a, instr_len, *b_data, *a_data;
+    size_t i;
     const struct opcode_entry *e;
 
-    if (!d) return;
-
-    acct_event_(d, DCPU16_ACCT_EV_CYCLE, d->pc);
+    if (!vm) return;
 
-    /* if we're currently servicing interrupts */
-    if (d->interrupts_deferred_ == 0) {
-        /* and there are interrupts to be serviced */
-        if (d->interrupts_head_ != d->interrupts_tail_) {
-            DCPU16_WORD message;
-            message = interrupt_dequeue_(d);
+    /* signal interested parties that a new cycle has ticked */
+    TRACE("%s: sending global cycle event", __func__);
+    acct_event_(vm, DCPU16_ACCT_EV_CYCLE, vm->reg[DCPU16_REG_PC]);
 
-            if (d->ia) {
-                TRACE("servicing interrupt IA:0x%04x message:0x%04x \n", d->ia, message);
-                /* then service the next interrupt */
-                d->interrupts_deferred_ = 1;
-                d->ram[--d->sp] = d->pc;
-                d->ram[--d->sp] = d->reg[reg_index_('A')];
-                d->pc = d->ia;
-                d->reg[0] = message;
-            } else {
-                TRACE("ignoring interrupt IA:0");
-            }
+    /* signal attached hardware */
+    for (i = 0; i < vm->hw_table_entries_; i++) {
+        if (vm->hw_table_[i].cycle) {
+            TRACE("%s: sending cycle to %s", __func__, vm->hw_table_[i].name_);
+            vm->hw_table_[i].cycle(vm, vm->hw_table_[i].data);
         }
     }
 
-    /* and make sure to execute an instruction after an interrupt */
+    instruction_decode_(vm->ram, vm->reg[DCPU16_REG_PC], &opcode, &b, &b_data, &a, &a_data, &instr_len);
 
-    instruction_decode_(d->ram, d->pc, &opcode, &b, &b_data, &a, &a_data, &instr_len);
+    /* consume what we decoded */
+    /* this happens immediately as PC might be re-set as an operation */
+    vm->reg[DCPU16_REG_PC] += instr_len;
 
+    /* run the operation */
     for (e = opcode_basic_entries; e->impl; e++) {
         if (e->value == opcode) {
             TRACE("%s>> %s 0x%04x, 0x%04x", __func__, e->name, b, a);
-            e->impl(d, b, b_data ? *b_data : 0, a, a_data ? *a_data : 0);
+            e->impl(vm, b, b_data ? *b_data : 0, a, a_data ? *a_data : 0);
             break;
         }
     }
 
-    /* get ready for the next one */
-    d->pc += instr_len;
-
     /* and jump over next instr(s) if needed */
-    if (d->skip_) {
-        instruction_decode_(d->ram, d->pc, &opcode, &b, &b_data, &a, &a_data, &instr_len);
-        d->pc += instr_len;
+    while (vm->skip_) {
+        instruction_decode_(vm->ram, vm->reg[DCPU16_REG_PC], &opcode, &b, &b_data, &a, &a_data, &instr_len);
+        vm->reg[DCPU16_REG_PC] += instr_len;
         TRACE("++ SKIPPED %x words", instr_len);
         if (opcode >= 0x10 && opcode <= 0x17) {
             /* skipping a branch instruction? skip branch's skippable instruction as well */
-            d->cycle += 1;
-            instruction_decode_(d->ram, d->pc, &opcode, &b, &b_data, &a, &a_data, &instr_len);
-            d->pc += instr_len;
-            TRACE("++ SKIPPED %x words", instr_len);
+            vm->cycle += 1;
+        } else {
+            vm->skip_ = 0;
+        }
+    }
+
+    /* if we're currently servicing interrupts */
+    if (vm->interrupts_deferred_ == 0) {
+        /* and there are interrupts to be serviced */
+        if (vm->interrupts_head_ != vm->interrupts_tail_) {
+            DCPU16_WORD message;
+            message = interrupt_dequeue_(vm);
+
+            if (vm->reg[DCPU16_REG_IA]) {
+                TRACE("servicing interrupt IA:0x%04x message:0x%04x \n", vm->reg[DCPU16_REG_IA], message);
+                /* then service the next interrupt */
+                vm->interrupts_deferred_ = 1;
+                vm->ram[--vm->reg[DCPU16_REG_SP]] = vm->reg[DCPU16_REG_PC];
+                vm->ram[--vm->reg[DCPU16_REG_SP]] = vm->reg[DCPU16_REG_A];
+                vm->reg[DCPU16_REG_PC] = vm->reg[DCPU16_REG_IA];
+                vm->reg[DCPU16_REG_A] = message;
+            } else {
+                TRACE("ignoring interrupt IA:0");
+            }
         }
-        d->skip_ = 0;
     }
 }
 
@@ -1272,41 +1302,41 @@ void dcpu16_step(struct dcpu16 *d) {
     print the current state of the machine
     shows current cycle count, registers, and next instruction
 */
-void dcpu16_state_print(struct dcpu16 *d) {
+void dcpu16_state_print(struct dcpu16 *vm) {
     unsigned int i;
 
-    if (!d) return;
+    if (!vm) return;
 
     printf("  ");
     for (i = 0; i < 8; i++)
-        printf("  %c:0x%04x", regnames_[i], d->reg[i]);
+        printf("  %s:0x%04x", dcpu16_reg_names[i], vm->reg[i]);
     printf("\n");
 
     printf("(0x%08llx) %2s:0x%04x %2s:0x%04x %2s:0x%04x %2s:0x%04x [%2s]:",
-           d->cycle,
-           "EX", d->ex,
-           "SP", d->sp,
-           "PC", d->pc,
-           "IA", d->ia,
+           vm->cycle,
+           dcpu16_reg_names[DCPU16_REG_EX], vm->reg[DCPU16_REG_EX],
+           dcpu16_reg_names[DCPU16_REG_SP], vm->reg[DCPU16_REG_SP],
+           dcpu16_reg_names[DCPU16_REG_PC], vm->reg[DCPU16_REG_PC],
+           dcpu16_reg_names[DCPU16_REG_IA], vm->reg[DCPU16_REG_IA],
            "PC");
 
-    dcpu16_disassemble_print(d, d->pc);
+    dcpu16_disassemble_print(vm, vm->reg[DCPU16_REG_PC]);
     printf("\n");
 }
 
 /*  dcpu16_dump_ram
  *  print raw ram contents from start to stop
  */
-void dcpu16_dump_ram(struct dcpu16 *d, DCPU16_WORD start, DCPU16_WORD end) {
+void dcpu16_dump_ram(struct dcpu16 *vm, DCPU16_WORD start, DCPU16_WORD end) {
     unsigned int i, j;
     const unsigned int n = 8; /* words per line */
 
-    if (!d) return;
+    if (!vm) return;
 
     for (i = start, j = 0; i <= end; i++, j++) {
         if (j % n == 0)
             printf("0x%04x:\t", i);
-        printf(" %04x%s", d->ram[i], (j % n) == (n - 1) ? "\n" : "");
+        printf(" %04x%s", vm->ram[i], (j % n) == (n - 1) ? "\n" : "");
     }
     if ((j % n) != (n - 1))
         printf("\n");
@@ -1319,6 +1349,11 @@ int dcpu16_hw_add(struct dcpu16 *vm, struct dcpu16_hw *hw) {
     if (!vm || !hw)
         return -1;
 
+    if (vm->hw_table_entries_ == 0xffff) {
+        WARN("maximum hardware entries reached");
+        return -1;
+    }
+
     if (vm->hw_table_entries_ == vm->hw_table_allocated_) {
         size_t new_entries = vm->hw_table_allocated_ + 32;
         void *tmp_ptr = realloc(vm->hw_table_, new_entries * sizeof * (vm->hw_table_));
@@ -1343,6 +1378,9 @@ int dcpu16_hw_add(struct dcpu16 *vm, struct dcpu16_hw *hw) {
 int dcpu16_acct_add(struct dcpu16 *vm, dcpu16_acct_event mask, dcpu16_ev_cb_t *fn, void *data) {
     struct dcpu16_acct_cb cb;
 
+    if (!vm)
+        return -1;
+
     cb.mask = mask;
     cb.fn = fn;
     cb.data = data;
@@ -1367,19 +1405,30 @@ int dcpu16_acct_add(struct dcpu16 *vm, dcpu16_acct_event mask, dcpu16_ev_cb_t *f
 /*  dcpu16_reset
  *  signals cpu to reset, clearing runstate and ram, then reload any init callbacks
  */
-void dcpu16_reset(struct dcpu16 *d) {
-    if (!d) return;
-
-    d->cycle = 0;
-    memset(d->reg, 0, sizeof d->reg);
-    d->pc = 0;
-    d->sp = 0;
-    d->ex = 0;
-    d->ia = 0;
-    d->skip_ = 0;
-    memset(d->ram, 0, sizeof d->ram);
-
-    acct_event_(d, DCPU16_ACCT_EV_RESET, 0);
+void dcpu16_reset(struct dcpu16 *vm) {
+    size_t i;
+
+    if (!vm)
+        return;
+
+    vm->skip_ = 0;
+    vm->interrupts_deferred_ = 0;
+    vm->on_fire_ = 0;
+    memset(vm->interrupts_, 0, sizeof vm->interrupts_);
+    vm->interrupts_head_ = 0;
+    vm->interrupts_tail_ = 0;
+
+    /* signal attached hardware */
+    for (i = 0; i < vm->hw_table_entries_; i++) {
+        if (vm->hw_table_[i].reset) 
+            vm->hw_table_[i].reset(vm, vm->hw_table_[i].data);
+    }
+
+    memset(vm->reg, 0, sizeof vm->reg);
+    memset(vm->ram, 0, sizeof vm->ram);
+    vm->cycle = 0;
+
+    acct_event_(vm, DCPU16_ACCT_EV_RESET, 0);
 }
 
 /*  dcpu16_new
@@ -1392,6 +1441,9 @@ struct dcpu16 *dcpu16_new(void) {
     if (vm == NULL)
         WARN("%s: %s(%zu): %s", __func__, "calloc", strerror(errno));
 
+    vm->warn_cb_ = warn_cb_;
+    vm->trace_cb_ = trace_cb_;
+
     return vm;
 }
 
@@ -1399,7 +1451,8 @@ struct dcpu16 *dcpu16_new(void) {
  *  release a dcpu16 instance
  */
 void dcpu16_delete(struct dcpu16 **vm) {
-    if (!vm || !*vm) return;
+    if (!vm || !*vm)
+        return;
 
     free(*vm);
     *vm = NULL;
index 35c1fe878877ba8ce9cf00c8f4e55a54caf60633..f2bf787b52a589525961ec69fd75cdf03bc1c565 100644 (file)
--- a/dcpu16.h
+++ b/dcpu16.h
@@ -10,8 +10,28 @@ typedef unsigned short DCPU16_WORD;
 #define DCPU16_RAM 0x10000
 #define DCPU16_INTERRUPT_QUEUE_SIZE 256
 
+extern const char * const dcpu16_reg_names[];
+enum dcpu16_register_indexes {
+    DCPU16_REG_A = 0,
+    DCPU16_REG_B,
+    DCPU16_REG_C,
+    DCPU16_REG_X,
+    DCPU16_REG_Y,
+    DCPU16_REG_Z,
+    DCPU16_REG_I,
+    DCPU16_REG_J,
+    DCPU16_REG_PC,
+    DCPU16_REG_SP,
+    DCPU16_REG_EX,
+    DCPU16_REG_IA,
+    DCPU16_REG__NUM
+};
+
 /* a self-contained dcpu16 core */
 struct dcpu16 {
+    void (*warn_cb_)(char *fmt, ...);
+    void (*trace_cb_)(char *fmt, ...);
+
     struct dcpu16_acct_cb *cb_table_;   /* list of callbacks to invoke for certain events */
     size_t cb_table_entries_;           /* callback list maintenance */
     size_t cb_table_allocated_;         /* callback list maintenance */
@@ -29,11 +49,7 @@ struct dcpu16 {
     size_t interrupts_tail_;            /* interrupt queue maintenance */
 
     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 ex;                     /* overflow */
-    DCPU16_WORD ia;                     /* interrupt address */
+    DCPU16_WORD reg[DCPU16_REG__NUM];   /* system registers, a b c x y z i j */
     DCPU16_WORD ram[DCPU16_RAM];        /* memory */
 };
 
@@ -51,15 +67,21 @@ struct dcpu16_acct_cb {
     dcpu16_acct_event mask;
 };
 
+typedef void (dcpu16_hw_signal_t)(struct dcpu16 *, void *);
 /* these are used to define hardware attached to the system */
 struct dcpu16_hw {
+    struct dcpu16 *vm;  /* which system do I belong to */
     char *name_;
+
     DCPU16_WORD id_l;
     DCPU16_WORD id_h;
     DCPU16_WORD ver;
     DCPU16_WORD mfg_l;
     DCPU16_WORD mfg_h;
-    void (*int_fn)(struct dcpu16 *, void *);
+
+    dcpu16_hw_signal_t *hwi;
+    dcpu16_hw_signal_t *cycle;
+    dcpu16_hw_signal_t *reset;
     void *data;
 };
 
@@ -93,6 +115,8 @@ void dcpu16_step(struct dcpu16 *);
 /* release a core */
 void dcpu16_delete(struct dcpu16 **);
 
+int dcpu16_interrupt(struct dcpu16 *, DCPU16_WORD);
+
 /* register callbacks to handle warning and debug messages, default is writing to stderr, may be set to null */
 void dcpu16_warn_cb_set(void (*)(char *, ...));
 void dcpu16_trace_cb_set(void (*)(char *, ...));
diff --git a/display.c b/display.c
deleted file mode 100644 (file)
index 0fbfbf5..0000000
--- a/display.c
+++ /dev/null
@@ -1,499 +0,0 @@
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <sysexits.h>
-#include <time.h>
-#include <assert.h>
-
-#ifdef HAVE_LIBPNG
-#include <setjmp.h>
-#include <png.h>
-#endif /* HAVE_LIBPNG */
-
-#include "dcpu16.h"
-#include "display.h"
-#include "chargen-4x8.h"
-
-/*
-    preliminary attempt at handling display
-
-    currently keeps raw display output in a pixel buffer, which is updated on every write to cpu's memory in display-space.
-    every [n] cycles, display buffer is rendered into an image file (png or pnm) in memory
-
-    TODO: blinking
-*/
-
-#define DISPLAY_BASE 0x8000
-#define DISPLAY_END  0x8179
-#define DISPLAY_CELL_MAP 0x8180
-#define DISPLAY_MISC 0x8280
-
-#define CELL_X 32
-#define CELL_Y 12
-
-#define CELL_X_SZ 4
-#define CELL_Y_SZ 8
-
-#define PIX_X 160
-#define PIX_Y 128
-#define PIX_BORDER 16
-
-/* total display is 160 pixels by 128 pixels */
-/* active display is 32x12 cells (each 4x8 pixels), surrounded by 16 pixel border */
-/* cells are rendered from cell map, which is bitmap of two words, defining four eight-bit columns */
-
-struct pixel_ {
-    unsigned char r;
-    unsigned char g;
-    unsigned char b;
-};
-
-/* buf will hold image file in memory */
-struct memstream_cookie_ {
-    char *buf;
-    size_t allocated;
-    size_t endpos;
-    off_t offset;
-    size_t chunk_size;
-};
-
-struct dcpu16_display_ {
-    char *outfile;
-    struct pixel_ *pixbuf; /* raw pixels */
-    struct memstream_cookie_ memstream_cookie; /* streambuffer contains rendered image file from pixbuf */
-    unsigned long long cycle_last_write;
-    unsigned int pixbuf_dirty : 1;
-    unsigned int file_dirty : 1;
-};
-
-static inline
-struct pixel_ pcolor_(unsigned int c) {
-    struct pixel_ p = { 0, 0, 0 };
-
-    switch (c) {
-        case 0x1: p.r=0x00, p.g=0x00, p.b=0xaa; break;  /* dark blue */
-        case 0x2: p.r=0x00, p.g=0xaa, p.b=0x00; break;  /* green */
-        case 0x3: p.r=0x00, p.g=0xaa, p.b=0xaa; break;  /* cyan */
-        case 0x4: p.r=0xaa, p.g=0x00, p.b=0x00; break;  /* red */
-        case 0x5: p.r=0xaa, p.g=0x00, p.b=0xaa; break;  /* magenta */
-        case 0x6: p.r=0xaa, p.g=0xaa, p.b=0x55; break;  /* yellow [] */
-        case 0x7: p.r=0xaa, p.g=0xaa, p.b=0xff; break;  /* pale blue */
-        case 0x8: p.r=0x55, p.g=0x55, p.b=0x55; break;  /* grey */
-        case 0x9: p.r=0x55, p.g=0x55, p.b=0xff; break;  /* also blue */
-        case 0xa: p.r=0x55, p.g=0xff, p.b=0x55; break;  /* light green */
-        case 0xb: p.r=0x55, p.g=0xff, p.b=0xff; break;  /* light cyan */
-        case 0xc: p.r=0xff, p.g=0x55, p.b=0x55; break;  /* light red */
-        case 0xd: p.r=0xff, p.g=0x55, p.b=0xff; break;  /* light magenta */
-        case 0xe: p.r=0xff, p.g=0xff, p.b=0x55; break;  /* light yellow */
-        case 0xf: p.r=0xff, p.g=0xff, p.b=0xff; break;  /* white */
-        default:  p.r=0x00, p.g=0x00, p.b=0x00;         /* black */
-    }
-
-    return p;
-}
-
-/* support functions for writing streams into memory buffer */
-static
-void memstream_cookie_init_(struct memstream_cookie_ *c, size_t chunk_size) {
-    assert(c);
-    assert(chunk_size);
-
-    c->buf = NULL;
-    c->allocated = 0;
-    c->endpos = 0;
-    c->offset = 0;
-    c->chunk_size = chunk_size;
-}
-
-static
-void memstream_cookie_fini_(struct memstream_cookie_ *c) {
-    assert(c);
-
-    free(c->buf);
-    c->allocated = c->endpos = c->offset = 0;
-}
-
-static
-/* of course these can't be the same */
-#ifdef HAVE_FOPENCOOKIE
-ssize_t memstream_write_fn_(void *c, const char *buf, size_t size)
-#else /* HAVE_FOPENCOOKIE */
-int memstream_write_fn_(void *c, const char *buf, int size)
-#endif /* HAVE_FOPENCOOKIE */
-{
-    struct memstream_cookie_ *cookie = c;
-
-    while ((size_t)size + cookie->offset > cookie->allocated) {
-        size_t new_allocated = cookie->allocated + cookie->chunk_size;
-        void *tmp_ptr = realloc(cookie->buf, new_allocated);
-        if (tmp_ptr == NULL) {
-            return -1;
-        }
-        cookie->buf = tmp_ptr;
-        cookie->allocated = new_allocated;
-    }
-
-    memcpy(cookie->buf + cookie->offset, buf, size);
-    cookie->offset += size;
-    if (cookie->offset > (off_t)cookie->endpos) {
-        cookie->endpos = cookie->offset;
-    }
-
-    return size;
-}
-
-/* should this just flood-fill entire display? */
-static inline
-void display_draw_border_(struct pixel_ *pixbuf, struct pixel_ color) {
-    size_t x, y;
-    size_t i;
-
-    /* top */
-    for (y = 0; y < PIX_BORDER; y++) {
-        for (x = 0; x < PIX_X; x++) {
-            i = (y * PIX_X) + x;
-            pixbuf[i] = color;
-            i = ((PIX_Y - y) * PIX_X) + x;
-            pixbuf[i] = color;
-        }
-    }
-
-    /* sides */
-    for (y = PIX_BORDER; y < (PIX_Y - PIX_BORDER + 1); y++)
-        for (x = 0; x < PIX_BORDER; x++) {
-            i = (y * PIX_X) + x;
-            pixbuf[i] = color;
-            pixbuf[i + (PIX_X - PIX_BORDER)] = color;
-        }
-
-}
-
-/* render a character cell to the display */
-static inline
-void display_draw_cell_(struct pixel_ *pixbuf, DCPU16_WORD *cell_map, DCPU16_WORD index, int cell_x, int cell_y, struct pixel_ fg, struct pixel_ bg) {
-    struct pixel_ *cellstart = pixbuf; /* start of display */
-    unsigned int pix_x, pix_y;
-    unsigned char *cell_bitmap = (unsigned char *)(cell_map + (index * sizeof index));
-
-    assert(cell_x < CELL_X);
-    assert(cell_y < CELL_Y);
-
-    cellstart += (PIX_X * PIX_BORDER); /* skip top border */
-
-    cellstart += (CELL_Y_SZ * PIX_X) * cell_y; /* skip down to row */
-
-    cellstart += PIX_BORDER; /* skip side border */
-
-    cellstart += (CELL_X_SZ) * cell_x; /* skip to column */
-
-    for (pix_x = 0; pix_x < CELL_X_SZ; pix_x++) {
-        for (pix_y = 0; pix_y < CELL_Y_SZ; pix_y++) {
-            if ((cell_bitmap[pix_x] >> pix_y) & 0x01)
-                cellstart[((CELL_Y_SZ - pix_y - 1) * PIX_X) + pix_x] = fg;
-            else
-                cellstart[((CELL_Y_SZ - pix_y - 1) * PIX_X) + pix_x] = bg;
-        }
-    }
-}
-
-#ifdef HAVE_LIBPNG
-/* write status callback */
-static
-void display_png_write_row_cb_(png_structp png_ptr, png_uint_32 row, int pass) {
-    (void)png_ptr, (void)row, (void)pass;
-    /*
-    fprintf(stderr, "%s:%u:%d\n", __func__, row, pass);
-    */
-}
-
-static
-void display_png_user_error_fn_(png_structp png_ptr, png_const_charp error_msg) {
-    (void)png_ptr;
-    fprintf(stderr, "PNG:ERROR:%s\n", error_msg);
-    exit(EX_SOFTWARE);
-}
-
-static
-void display_png_user_warning_fn_(png_structp png_ptr, png_const_charp warning_msg) {
-    (void)png_ptr;
-    fprintf(stderr, "PNG:WARNING:%s\n", warning_msg);
-    return;
-}
-
-/* write png file */
-static
-void display_png_write_(struct dcpu16_display_ *d) {
-    FILE *f;
-    png_structp png_ptr;
-    png_infop info_ptr;
-    size_t i;
-
-    png_voidp user_error_ptr = NULL;
-
-#ifdef HAVE_FOPENCOOKIE
-    /* linux-style memory stream */
-    cookie_io_functions_t cookie_io_functions = {
-        .read = NULL,
-        .write = memstream_write_fn_,
-        .seek = NULL,
-        .close = NULL
-    };
-    f = fopencookie(&d->memstream_cookie, "wb", cookie_io_functions);
-    if (f == NULL) {
-        fprintf(stderr, "%s():%s\n", "fopencookie", strerror(errno));
-        return;
-    }
-
-#else /* HAVE_FOPENCOOKIE */
-#ifdef HAVE_FUNOPEN
-    /* BSD-style memory stream */
-    f = funopen(&d->memstream_cookie,
-                NULL, /* don't care about read */
-                memstream_write_fn_,
-                NULL, /* don't care about seek */
-                NULL /* don't care about close */
-                );
-    if (f == NULL) {
-        fprintf(stderr, "%s():%s\n", "funopen", strerror(errno));
-        return;
-    }
-
-#else /* HAVE_FUNOPEN */
-    /* default to writing file if we can't buffer in memory */
-    f = fopen(d->outfile, "wb");
-    if (f == NULL) {
-        return;
-    }
-#endif /* HAVE_FUNOPEN */
-#endif /* HAVE_FOPENCOOKIE */
-
-    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, user_error_ptr, display_png_user_error_fn_, display_png_user_warning_fn_);
-    if (png_ptr == NULL) {
-        goto f_done;
-    }
-
-    info_ptr = png_create_info_struct(png_ptr);
-    if (info_ptr == NULL) {
-        png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
-        goto f_done;
-    }
-
-    if (setjmp(png_jmpbuf(png_ptr))) {
-        png_destroy_write_struct(&png_ptr, &info_ptr);
-        goto f_done;
-}
-
-    png_init_io(png_ptr, f);
-    png_set_write_status_fn(png_ptr, display_png_write_row_cb_);
-
-    /* png_set_filter(png_ptr, 0, PNG_FILTER_NONE); */
-    /* png_set_compression_level(png_ptr, Z_BEST_COMPRESSION); */
-
-    png_set_IHDR(png_ptr, info_ptr, PIX_X, PIX_Y, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
-
-    png_write_info(png_ptr, info_ptr);
-
-    for (i = 0; i < PIX_Y; i++) {
-        png_write_row(png_ptr, (unsigned char *)(d->pixbuf + (i * PIX_X)));
-    }
-
-    png_write_end(png_ptr, info_ptr);
-
-    png_destroy_write_struct(&png_ptr, &info_ptr);
-
-f_done:
-    fclose(f);
-}
-#else /* HAVE_LIBPNG */
-
-/* if PNG not available, just write pnm file */
-static
-void display_pnm_write_(struct dcpu16_display_ *d) {
-    size_t i;
-    FILE *f;
-
-    f = fopen(d->outfile, "w");
-    if (f == NULL) {
-        fprintf(stderr, "%s('%s'):%s\n", "fopen", filename, strerror(errno));
-        return;
-    }
-
-    /* write header... */
-    /* PNM binary */
-    /* x y */
-    /* max value */
-    fprintf(f, "P6\n%d %d\n255\n", PIX_X, PIX_Y);
-
-    /* write out image bytes in r g b order */
-    for (i = 0; i < PIX_X * PIX_Y; i++) {
-        fwrite(&d->pixbuf[i].r, 1, 1, f);
-        fwrite(&d->pixbuf[i].g, 1, 1, f);
-        fwrite(&d->pixbuf[i].b, 1, 1, f);
-    }
-
-    fclose(f);
-}
-
-#endif /* HAVE_LIBPNG */
-
-static
-void display_image_render_(struct dcpu16_display_ *d) {
-#ifdef HAVE_LIBPNG
-    display_png_write_(d);
-#else /* HAVE_LIBPNG */
-    display_pnm_write_(d);
-#endif /* HAVE_LIBPNG */
-    d->file_dirty = 1;
-}
-
-
-/* the callback to register to be run on display init/reset */
-/* currently this populates the chargen map 'from rom'.. */
-/* and clears the display buffers */
-void display_reset_fn(struct dcpu16 *vm, dcpu16_acct_event e, DCPU16_WORD addr, void *data) {
-    struct dcpu16_display_ *d = (struct dcpu16_display_ *)data;
-
-    (void)e, (void)addr;
-
-    memcpy(vm->ram + DISPLAY_CELL_MAP, chargen_4x8_glyphs, sizeof chargen_4x8_glyphs);
-
-    memset(vm->ram + DISPLAY_BASE, 0, (DISPLAY_END - DISPLAY_BASE) * sizeof *(vm->ram));
-
-    memset(d->pixbuf, 0, PIX_X * PIX_Y * sizeof *(d->pixbuf));
-
-    d->pixbuf_dirty = 1;
-}
-
-/* the callback to register with the cpu for occasionally doing something with the display buffer */
-void display_cycle_fn(struct dcpu16 *vm, dcpu16_acct_event e, DCPU16_WORD addr, void *data) {
-    struct dcpu16_display_ *d = (struct dcpu16_display_ *)data;
-
-    const unsigned long long min_cycles_between_writes = 1536;
-    const time_t min_seconds_between_file_write = 5;
-
-    static time_t last_file_write_ = 0;
-    time_t now;
-
-    (void)e, (void)addr;
-
-    if (d->pixbuf_dirty) {
-        if (vm->cycle > d->cycle_last_write + min_cycles_between_writes) {
-            display_image_render_(d);
-            d->pixbuf_dirty = 0;
-            d->cycle_last_write = vm->cycle;
-        }
-    }
-
-    if (d->file_dirty) {
-        now = time(NULL);
-        if (now > last_file_write_ + min_seconds_between_file_write) {
-            FILE *of;
-
-            of = fopen(d->outfile, "wb");
-            if (of == NULL) {
-                fprintf(stderr, "%s('%s'):%s\n", "fopen", d->outfile, strerror(errno));
-                return;
-            }
-
-            fwrite(d->memstream_cookie.buf, d->memstream_cookie.endpos, 1, of);
-            fclose(of);
-
-            d->file_dirty = 0;
-            last_file_write_ = now;
-        }
-    }
-}
-
-/* the callback to register with the cpu for watching memory updates */
-/* 'data' is an allocated display buffer */
-void display_fn(struct dcpu16 *vm, dcpu16_acct_event e, DCPU16_WORD addr, void *data) {
-    DCPU16_DISPLAY *d = (DCPU16_DISPLAY *)data;
-    unsigned char index, blink, bg, fg;
-    unsigned int cell_x, cell_y;
-
-    (void)e;
-
-    if (addr < DISPLAY_BASE || addr > DISPLAY_MISC)
-        return;
-
-    if (addr > DISPLAY_END && addr < DISPLAY_MISC) {
-        unsigned char updated_index = addr - DISPLAY_CELL_MAP;
-        /* a cell map was updated, refresh entire screen */
-        for (cell_x = 0; cell_x < CELL_X; cell_x++) {
-            for (cell_y = 0; cell_y < CELL_Y; cell_y++) {
-                addr = DISPLAY_BASE + cell_x + (cell_y * CELL_X);
-                index = vm->ram[addr] & 0x7f;
-                if (index != updated_index)
-                    continue;
-                blink = (vm->ram[addr] >> 7) & 0x01;
-                bg = (vm->ram[addr] >> 8) & 0x0f;
-                fg = (vm->ram[addr] >> 12) & 0x0f;
-                display_draw_cell_(d->pixbuf, vm->ram + DISPLAY_CELL_MAP, index, cell_x, cell_y, pcolor_(fg), pcolor_(bg));
-            }
-        }
-
-        d->pixbuf_dirty = 1;
-        return;
-    }
-
-    if (addr == DISPLAY_MISC) {
-        /* new border color */
-        char border = vm->ram[addr] & 0x0f;
-        display_draw_border_(d->pixbuf, pcolor_(border));
-
-        d->pixbuf_dirty = 1;
-        return;
-    }
-
-    cell_x = (addr - DISPLAY_BASE) % CELL_X;
-    cell_y = (addr - DISPLAY_BASE) / CELL_X;
-
-    index = vm->ram[addr] & 0x7f;
-    blink = (vm->ram[addr] >> 7) & 0x01;
-    bg = (vm->ram[addr] >> 8) & 0x0f;
-    fg = (vm->ram[addr] >> 12) & 0x0f;
-
-    display_draw_cell_(d->pixbuf, vm->ram + DISPLAY_CELL_MAP, index, cell_x, cell_y, pcolor_(fg), pcolor_(bg));
-
-    d->pixbuf_dirty = 1;
-}
-
-/* init the pixel buffer */
-DCPU16_DISPLAY *display_new(const char *filename) {
-    DCPU16_DISPLAY *d = calloc(1, sizeof *d);
-    if (d == NULL)
-        return NULL;
-
-    d->pixbuf = calloc(PIX_X * PIX_Y, sizeof *(d->pixbuf));
-    if (d->pixbuf == NULL) {
-        free(d);
-        return NULL;
-    }
-
-    d->outfile = strdup(filename);
-    if (d->outfile == NULL) {
-        free(d->pixbuf);
-        free(d);
-        return NULL;
-    }
-
-    memstream_cookie_init_(&d->memstream_cookie, 1024 * 8);
-
-    d->cycle_last_write = 0;
-    d->pixbuf_dirty = 0;
-    d->file_dirty = 0;
-
-    return d;
-}
-
-void display_free(DCPU16_DISPLAY *d) {
-    if (d) {
-        free(d->pixbuf);
-        d->pixbuf = NULL;
-        free(d->outfile);
-        d->outfile = NULL;
-        memstream_cookie_fini_(&d->memstream_cookie);
-    }
-    free(d);
-}
diff --git a/display.h b/display.h
deleted file mode 100644 (file)
index fe88cc3..0000000
--- a/display.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef DISPLAY_H_MJYI1IAV
-#define DISPLAY_H_MJYI1IAV
-
-#include "dcpu16.h"
-
-typedef struct dcpu16_display_ DCPU16_DISPLAY;
-
-void display_reset_fn(struct dcpu16 *, dcpu16_acct_event, DCPU16_WORD, void *);
-void display_cycle_fn(struct dcpu16 *, dcpu16_acct_event, DCPU16_WORD, void *);
-void display_fn(struct dcpu16 *, dcpu16_acct_event, DCPU16_WORD, void *);
-DCPU16_DISPLAY *display_new(const char *);
-void display_free(DCPU16_DISPLAY *);
-
-#endif /* DISPLAY_H_MJYI1IAV */
diff --git a/hw_clock.c b/hw_clock.c
new file mode 100644 (file)
index 0000000..89d5912
--- /dev/null
@@ -0,0 +1,112 @@
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "dcpu16.h"
+#include "hw_clock.h"
+
+static dcpu16_hw_signal_t clock_reset_;
+static dcpu16_hw_signal_t clock_cycle_;
+static dcpu16_hw_signal_t clock_hwi_;
+static struct dcpu16_hw hw_ = {
+    .name_  = "Generic Clock (compatible)",
+    .id_l   = 0xb402,
+    .id_h   = 0x12d0,
+    .ver    = 0x0001,
+    .mfg_l  = 0x0000,
+    .mfg_h  = 0x0000,
+    .hwi    = clock_hwi_,
+    .cycle  = clock_cycle_,
+    .reset  = clock_reset_,
+    .data   = (struct clock_ *)NULL
+};
+
+struct clock_ {
+    DCPU16_WORD cycle_;
+    DCPU16_WORD rate;
+    DCPU16_WORD tick;
+    DCPU16_WORD interrupt_message;
+};
+
+static
+void clock_reset_(struct dcpu16 *vm, void *data) {
+    struct clock_ *clock = (struct clock_ *)data;
+
+    (void)vm;
+
+    memset(clock, 0, sizeof *clock);
+}
+
+static
+void clock_cycle_(struct dcpu16 *vm, void *data) {
+    struct clock_ *clock = (struct clock_ *)data;
+
+    /* cycle is only called 100000 times per second */
+    /* maximum rate is 60hz / word_max = 3932160 */
+
+    if (clock->rate == 0)
+        return;
+
+    clock->cycle_++;
+    if (clock->cycle_ >= clock->rate) {
+        /* THIS CHECK IS WRONG, JUST A PLACEHOLDER */
+        clock->cycle_ = 0;
+        clock->tick += 1;
+
+        if (clock->interrupt_message) {
+            if (dcpu16_interrupt(vm, clock->interrupt_message))
+                vm->warn_cb_("%s: could not send interrupt", hw_.name_);
+        }
+    }
+}
+
+static
+void clock_hwi_(struct dcpu16 *vm, void *data) {
+    struct clock_ *clock = (struct clock_ *)data;
+    DCPU16_WORD reg_a = vm->reg[DCPU16_REG_A];
+    DCPU16_WORD reg_b = vm->reg[DCPU16_REG_B];
+
+    switch (reg_a) {
+        case 0: /* set tick gather rate, 60hz/B */
+            clock->rate = reg_b;
+            break;
+
+        case 1: /* fetch elapsed count since rate was set */
+            vm->reg[DCPU16_REG_C] = clock->tick;
+            break;
+
+        case 2:
+            clock->interrupt_message = reg_b;
+            break;
+    }
+}
+
+/* instantitate a new clock */
+struct dcpu16_hw *clock_new(struct dcpu16 *vm) {
+    struct dcpu16_hw *hw;
+
+    hw = calloc(1, sizeof *hw);
+    if (hw == NULL) {
+        vm->warn_cb_("%s():%s", "calloc", strerror(errno));
+        return NULL;
+    }
+    memcpy(hw, &hw_, sizeof *hw);
+    hw->data = calloc(1, sizeof hw->data);
+    if (hw->data == NULL) {
+        vm->warn_cb_("%s():%s", "calloc", strerror(errno));
+        free(hw);
+        return NULL;
+    }
+
+    return hw;
+}
+
+void clock_del(struct dcpu16_hw **hw) {
+    if (hw) {
+        free((*hw)->data);
+        (*hw)->data = NULL;
+
+        free((*hw));
+        *hw = NULL;
+    }
+}
diff --git a/hw_clock.h b/hw_clock.h
new file mode 100644 (file)
index 0000000..263c03b
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef CLOCK_H_P5G2G9FA
+#define CLOCK_H_P5G2G9FA
+
+struct dcpu16_hw *clock_new(struct dcpu16 *);
+void clock_del(struct dcpu16_hw **);
+
+#endif /* CLOCK_H_P5G2G9FA */
diff --git a/hw_keyboard.c b/hw_keyboard.c
new file mode 100644 (file)
index 0000000..71e016f
--- /dev/null
@@ -0,0 +1,123 @@
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "dcpu16.h"
+#include "hw_keyboard.h"
+
+#define BUF_SZ 32
+
+static dcpu16_hw_signal_t keyboard_reset_;
+static dcpu16_hw_signal_t keyboard_cycle_;
+static dcpu16_hw_signal_t keyboard_hwi_;
+static struct dcpu16_hw hw_ = {
+    .name_  = "Generic Keyboard (compatible)",
+    .id_l   = 0x7406,
+    .id_h   = 0x30cf,
+    .ver    = 0x0001,
+    .mfg_l  = 0x0000,
+    .mfg_h  = 0x0000,
+    .hwi    = keyboard_hwi_,
+    .cycle  = keyboard_cycle_,
+    .reset  = keyboard_reset_,
+    .data   = (struct keyboard_ *)NULL
+};
+
+struct keyboard_ {
+    char *buf;
+    size_t buf_sz;
+    DCPU16_WORD interrupt_message;
+};
+
+static
+void keyboard_reset_(struct dcpu16 *vm, void *data) {
+    struct keyboard_ *keyboard = (struct keyboard_ *)data;
+
+    (void)vm;
+
+    keyboard->interrupt_message = 0;
+    memset(keyboard->buf, 0, keyboard->buf_sz);
+}
+
+static
+void keyboard_cycle_(struct dcpu16 *vm, void *data) {
+    struct keyboard_ *keyboard = (struct keyboard_ *)data;
+
+    (void)vm, (void)keyboard;
+}
+
+static
+void keyboard_hwi_(struct dcpu16 *vm, void *data) {
+    struct keyboard_ *keyboard = (struct keyboard_ *)data;
+    DCPU16_WORD reg_a = vm->reg[DCPU16_REG_A];
+    DCPU16_WORD reg_b = vm->reg[DCPU16_REG_B];
+    size_t i;
+
+    switch (reg_a) {
+        case 0: /* clear keyboard buffer */
+            memset(keyboard->buf, 0, keyboard->buf_sz);
+            break;
+
+        case 1: /* get next key from buffer as C */
+            vm->reg[DCPU16_REG_C] = keyboard->buf[0];
+            for (i = 1; i < keyboard->buf_sz; i++) {
+                keyboard->buf[i-1] = keyboard->buf[i];
+            }
+            keyboard->buf[i] = '\0';
+            break;
+
+        case 2: /* get currently-pressed-state of key in B as C */
+            vm->warn_cb_("IMPLEMENT");
+            (void)reg_b;
+            vm->reg[DCPU16_REG_C] = 0;
+            break;
+
+        case 3: /* set interrupt state */
+            keyboard->interrupt_message = reg_b;
+            break;
+
+    }
+}
+
+struct dcpu16_hw *keyboard_new(struct dcpu16 *vm) {
+    struct dcpu16_hw *hw;
+    char *b;
+
+    hw = calloc(1, sizeof *hw);
+    if (hw == NULL) {
+        vm->warn_cb_("%s():%s", "calloc", strerror(errno));
+        return NULL;
+    }
+    memcpy(hw, &hw_, sizeof *hw);
+    hw->data = calloc(1, sizeof hw->data);
+    if (hw->data == NULL) {
+        vm->warn_cb_("%s():%s", "calloc", strerror(errno));
+        free(hw);
+        return NULL;
+    }
+
+    b = calloc(BUF_SZ, sizeof *b);
+    if (b == NULL) {
+        vm->warn_cb_("%s():%s", "calloc", strerror(errno));
+        free(hw->data);
+        free(hw);
+        return NULL;
+    }
+    ((struct keyboard_ *)(hw->data))->buf = b;
+    ((struct keyboard_ *)(hw->data))->buf_sz = BUF_SZ;
+
+    return hw;
+}
+
+void keyboard_del(struct dcpu16_hw **hw) {
+    if (hw) {
+        free(((struct keyboard_ *)((*hw)->data))->buf);
+        ((struct keyboard_ *)((*hw)->data))->buf = NULL;
+
+        free((*hw)->data);
+        (*hw)->data = NULL;
+
+        free(*hw);
+        *hw = NULL;
+    }
+}
diff --git a/hw_keyboard.h b/hw_keyboard.h
new file mode 100644 (file)
index 0000000..cf7eb4c
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef KEYBOARD_H_Y2G5EOAS
+#define KEYBOARD_H_Y2G5EOAS
+
+struct dcpu16_hw *keyboard_new(struct dcpu16 *);
+void keyboard_del(struct dcpu16_hw **);
+
+#endif /* KEYBOARD_H_Y2G5EOAS */
diff --git a/hw_lem1802.c b/hw_lem1802.c
new file mode 100644 (file)
index 0000000..72af16a
--- /dev/null
@@ -0,0 +1,509 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBPNG
+#include <setjmp.h>
+#include <png.h>
+#endif /* HAVE_LIBPNG */
+
+#ifdef HAVE_LIBVNCSERVER
+#include <rfb/rfb.h>
+#include <rfb/keysym.h>
+#endif /* HAVE_LIBVNCSERVER */
+
+#include "dcpu16.h"
+#include "chargen-4x8.h"
+#include "hw_lem1802.h"
+
+static dcpu16_hw_signal_t lem1802_reset_;
+static dcpu16_hw_signal_t lem1802_cycle_;
+static dcpu16_hw_signal_t lem1802_hwi_;
+static struct dcpu16_hw hw_ = {
+    .name_  = "LEM1802 - Low Energy Monitor",
+    .id_l   = 0xf615,
+    .id_h   = 0x7349,
+    .ver    = 0x1802,
+    .mfg_l  = 0x8b36,
+    .mfg_h  = 0x1c6c,
+    .hwi    = lem1802_hwi_,
+    .cycle  = lem1802_cycle_,
+    .reset  = lem1802_reset_,
+    .data   = (struct lem1802_ *)NULL
+};
+
+#define PIX_X 160
+#define PIX_Y 128
+#define PIX_BORDER 16
+#define CELL_X_SZ 4
+#define CELL_Y_SZ 8
+
+#define PALETTE_ENTRIES 16
+static const DCPU16_WORD palette_default_[PALETTE_ENTRIES] = {
+    0x0000, /* black */
+    0x000a, /* blue */
+    0x00a0, /* green */
+    0x00aa, /* cyan */
+    0x0a05, /* red */
+    0x0a0f, /* magenta */
+    0x0aa5, /* yellow */
+    0x0aaf, /* pale blue */
+    0x0555, /* grey */
+    0x055f, /* light blue */
+    0x05f5, /* light green*/
+    0x05ff, /* light cyan */
+    0x0f55, /* light red */
+    0x0f5f, /* light magenta */
+    0x0ff5, /* light yellow */
+    0x0fff, /* white */
+};
+
+struct pixel_ {
+    unsigned char r;
+    unsigned char g;
+    unsigned char b;
+    unsigned char a;
+};
+
+struct lem1802_ {
+    long long cycle_activated; /* for tracking 'turn on delay' */
+
+    DCPU16_WORD video_base;
+    DCPU16_WORD font_base;
+    DCPU16_WORD palette_base;
+    DCPU16_WORD border_color;
+    struct pixel_ *pixbuf;
+
+    unsigned int refresh_rate; /* redraw every n cycles */
+    unsigned int refresh_tally_; /* tick */
+    
+    unsigned int blink_rate; /* toggle every n cycles? still figuring this out.. */
+    unsigned int blink_tally_; /* tick */
+    unsigned int blink_state;
+
+    int (*render)(void *, struct pixel_ *, size_t, size_t);
+    void *renderer_data;
+};
+
+static inline
+void pixel_color_(struct pixel_ *pix, DCPU16_WORD color) {
+    pix->r = (color & 0x000f) | ((color & 0x000f) << 8);
+    pix->g = ((color & 0x00f0) >> 8) | (color & 0x00f0);
+    pix->b = ((color & 0x0f00) >> 8) | ((color & 0x0f00) >> 16);
+    pix->a = ((color & 0xf000) >> 16) | ((color & 0xf000) >> 24);
+}
+
+static
+void pixbuf_border_paint_(struct pixel_ *pixbuf, struct pixel_ *border) {
+    size_t x, y, i;
+
+    /* top */
+    for (y = 0; y < PIX_BORDER; y++) {
+        for (x = 0; x < PIX_X; x++) {
+            i = (y * PIX_X) + x;
+            pixbuf[i] = *border;
+            i = ((PIX_Y - y) * PIX_X) + x;
+            pixbuf[i] = *border;
+        }
+    }
+
+    /* sides */
+    for (y = PIX_BORDER; y < (PIX_Y - PIX_BORDER + 1); y++)
+        for (x = 0; x < PIX_BORDER; x++) {
+            i = (y * PIX_X) + x;
+            pixbuf[i] = *border;
+            pixbuf[i + (PIX_X - PIX_BORDER)] = *border;
+        }
+}
+
+static
+void font_tile_paint_(struct pixel_ *p, struct pixel_ *fg, struct pixel_ *bg, DCPU16_WORD *tile) {
+    size_t pix_x, pix_y;
+    unsigned char *font_bitmap = (unsigned char *)tile;
+
+    for (pix_x = 0; pix_x < CELL_X_SZ; pix_x++) {
+        for (pix_y = 0; pix_y < CELL_Y_SZ; pix_y++) {
+            if ((font_bitmap[pix_x] >> pix_y) & 0x01)
+                p[((CELL_Y_SZ - pix_y - 1) * PIX_X) + pix_x] = *fg;
+            else
+                p[((CELL_Y_SZ - pix_y - 1) * PIX_X) + pix_x] = *bg;
+        }
+    }
+}
+
+static
+void pixbuf_addr_paint_(struct pixel_ *pixbuf, DCPU16_WORD *mem, DCPU16_WORD addr, DCPU16_WORD *palette, DCPU16_WORD *tiles, unsigned int blink_state) {
+    struct pixel_ *tilestart = pixbuf; /* start of display */
+    unsigned int cell_x = addr % (PIX_X / CELL_X_SZ),
+                 cell_y = addr / (PIX_X / CELL_X_SZ);
+    struct pixel_ fg, bg;
+    DCPU16_WORD *font_bitmap;
+    int blink;
+
+    cell_x = addr % (PIX_X / CELL_X_SZ);
+    cell_y = addr / (PIX_X / CELL_X_SZ);
+
+    blink = mem[addr] & 0x0080;
+
+    /* tiles take two words each */
+    font_bitmap = tiles + (2 * (mem[addr] & 0x7f));
+
+    pixel_color_(&bg, palette[(mem[addr] >> 8) & 0x0f]);
+    if (blink && blink_state)
+        pixel_color_(&fg, palette[(mem[addr] >> 8) & 0x0f]);
+    else
+        pixel_color_(&fg, palette[(mem[addr] >> 12) & 0x0f]);
+
+    tilestart += (PIX_X * PIX_BORDER); /* skip top border */
+    tilestart += (CELL_Y_SZ * PIX_X) * cell_y; /* skip down to row */
+
+    tilestart += PIX_BORDER; /* skip side border */
+    tilestart += (CELL_X_SZ) * cell_x; /* skip to column */
+
+    font_tile_paint_(tilestart, &fg, &bg, font_bitmap);
+}
+
+static
+void lem1802_pixbuf_refresh_full_(struct lem1802_ *display, DCPU16_WORD *mem) {
+    struct pixel_ border;
+    size_t tile;
+
+    if (display->video_base == 0) {
+        /* disconnected, blank display.  static might be fun, too */
+        memset(display->pixbuf, 0, PIX_X * PIX_Y * sizeof *display->pixbuf);
+        return;
+    }
+
+    pixel_color_(&border, display->border_color);
+    pixbuf_border_paint_(display->pixbuf, &border);
+
+    for (tile = 0; tile < (PIX_X / CELL_X_SZ) * (PIX_Y / CELL_Y_SZ); tile++) {
+        pixbuf_addr_paint_(display->pixbuf,
+                           mem,
+                           display->video_base + tile,
+                           display->palette_base ? mem + display->palette_base : palette_default_,
+                           display->font_base ? mem + display->font_base : (unsigned short *)chargen_4x8_glyphs,
+                           display->blink_state);
+    }
+}
+
+static
+int pixbuf_render_pnm_(void *data, struct pixel_ *pixbuf, size_t x, size_t y) {
+    FILE *f = (FILE *)data;
+    size_t i;
+    fprintf(f, "P6\n"
+               "%lu %lu\n"
+               "255\n",
+               x, y);
+    for (i = 0; i < x * y; i++) {
+        fwrite(&pixbuf[i].r, 1, 1, f);
+        fwrite(&pixbuf[i].g, 1, 1, f);
+        fwrite(&pixbuf[i].b, 1, 1, f);
+    }
+    fclose(f);
+
+    return 0;
+}
+
+#ifdef HAVE_LIBPNG
+static
+void pixbuf_render_png_user_warning_(png_structp png, png_const_charp msg) {
+    (void)png, (void)msg;
+}
+static
+void pixbuf_render_png_user_error_(png_structp png, png_const_charp msg) {
+    (void)png, (void)msg;
+}
+static
+int pixbuf_render_png_(void *data, struct pixel_ *pixbuf, size_t x, size_t y) {
+    FILE *f = (FILE *)data;
+    int retval = 0;
+    png_structp png;
+    png_infop info;
+    size_t i;
+
+    png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, pixbuf_render_png_user_error_, pixbuf_render_png_user_warning_);
+    if (png == NULL) {
+        goto f_done;
+    }
+
+    info = png_create_info_struct(png);
+    if (info == NULL) {
+        png_destroy_write_struct(&png, (png_infopp)NULL);
+        goto f_done;
+    }
+
+    if (setjmp(png_jmpbuf(png))) {
+        png_destroy_write_struct(&png, &info);
+        goto f_done;
+    }
+
+    png_init_io(png, f);
+    png_set_IHDR(png, info, x, y, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+    png_set_invert_alpha(png);
+    png_write_info(png, info);
+    for (i = 0; i < y; i++) {
+        png_write_row(png, (unsigned char *)(pixbuf + (i * x)));
+    }
+    png_write_end(png, info);
+
+    png_destroy_write_struct(&png, &info);
+
+f_done:
+    fclose(f);
+
+    return retval;
+}
+#endif /* HAVE_LIBPNG */
+
+#ifdef HAVE_LIBVNCSERVER
+struct vnc_server_ {
+    rfbScreenInfoPtr rfbScreen;
+};
+
+/* create and return a new struct vnc_server_, for use as a vnc renderer's generic data */
+void *lem1802_vnc_init_data(int argc, char *argv[], struct dcpu16_hw *hw) {
+    struct vnc_server_ *s;
+    struct pixel_ *pixbuf = ((struct lem1802_ *)(hw->data))->pixbuf;
+    int paddedWidth = PIX_X + ( (PIX_X & 3) ? (4 - (PIX_X & 3)) : 0 );
+    int height = PIX_Y;
+    int bitsPerSample = 8;
+    int samplesPerPixel = 3;
+    int bytesPerPixel = 4;
+
+    s = calloc(1, sizeof *s);
+    if (s == NULL)
+        return NULL;
+
+    s->rfbScreen = rfbGetScreen(&argc, argv, paddedWidth, height, bitsPerSample, samplesPerPixel, bytesPerPixel);
+    if (s->rfbScreen == NULL) {
+        free(s);
+        return NULL;
+    }
+
+    s->rfbScreen->desktopName = "lem1802";
+    s->rfbScreen->alwaysShared = TRUE;
+
+#if 0
+    s->rfbScreen->kbdAddEvent = HandleKey; */
+    s->rfbScreen->httpDir = "../classes";
+#endif
+
+    s->rfbScreen->frameBuffer = (char *)pixbuf;
+
+    rfbInitServer(s->rfbScreen);
+
+    rfbRunEventLoop(s->rfbScreen,-1,TRUE);
+
+    return s;
+}
+
+static
+int pixbuf_render_vnc_(void *data, struct pixel_ *pixbuf, size_t x, size_t y) {
+    int retval = 0;
+    struct vnc_server_ *s = (struct vnc_server_ *)data;
+
+    (void)pixbuf;
+
+    /* derp */
+    rfbMarkRectAsModified(s->rfbScreen,0,0,x,y);
+
+    return retval;
+}
+#endif /* HAVE_LIBVNCSERVER */
+
+
+static
+void lem1802_reset_(struct dcpu16 *vm, void *data) {
+    struct lem1802_ *display = (struct lem1802_ *)data;
+
+    display->cycle_activated = vm->cycle;
+
+    display->video_base = 0;
+    display->font_base = 0;
+    display->palette_base = 0;
+    display->border_color = 0;
+
+    memset(display->pixbuf, 0, PIX_X * PIX_Y * sizeof *display->pixbuf);
+
+    display->refresh_tally_ = 0;
+    display->blink_tally_ = 0;
+    display->blink_state = 0;
+}
+
+static
+void lem1802_cycle_(struct dcpu16 *vm, void *data) {
+    struct lem1802_ *display = (struct lem1802_ *)data;
+
+    (void)vm;
+    /*
+        maybe just step through video memory (if set)
+        one word per clock..?  could just cheat and
+        use accounting callbacks..
+
+        for now just count cycles and issue a full refresh/render
+        every so often
+     */
+
+    display->blink_tally_++;
+    if (display->blink_tally_ >= display->blink_rate)
+        display->blink_state ^= 1;
+
+    display->refresh_tally_++;
+    if (display->refresh_tally_ >= display->refresh_rate) {
+        display->refresh_tally_ = 0;
+        if (display->render)
+            display->render(display->renderer_data, display->pixbuf, PIX_X, PIX_Y);
+    }
+
+    if (display->render) {
+        lem1802_pixbuf_refresh_full_(display, vm->ram);
+        display->render(display->renderer_data, display->pixbuf, PIX_X, PIX_Y);
+    }
+}
+
+static
+void lem1802_hwi_(struct dcpu16 *vm, void *data) {
+    struct lem1802_ *display = (struct lem1802_ *)data;
+    DCPU16_WORD reg_a = vm->reg[DCPU16_REG_A];
+    DCPU16_WORD reg_b = vm->reg[DCPU16_REG_B];
+    size_t i;
+
+    switch (reg_a) {
+        case 0: /* MEM_MAP_SCREEN */
+            if (display->cycle_activated == 0 && reg_b) {
+                display->cycle_activated = vm->cycle;
+            }
+            display->video_base = reg_b;
+            break;
+
+        case 1: /* MEM_MAP_FONT */
+            display->font_base = reg_b;
+            break;
+
+        case 2: /* MEM_MAP_PALETTE */
+            display->palette_base = reg_b;
+            break;
+
+        case 3: /* SET_BORDER_COLOR */
+            display->border_color = reg_b & 0x000f;
+            break;
+
+        case 4: /* MEM_DUMP_FONT */
+            for (i = 0; i < 128 ; i++) {
+                vm->ram[reg_b] = chargen_4x8_glyphs[reg_b][0] << 8;
+                vm->ram[reg_b] |= chargen_4x8_glyphs[reg_b][1];
+                reg_b += 1;
+                vm->ram[reg_b] = chargen_4x8_glyphs[reg_b][2] << 8;
+                vm->ram[reg_b] |= chargen_4x8_glyphs[reg_b][3];
+                reg_b += 1;
+            }
+            vm->cycle += 256;
+            break;
+
+        case 5: /* MEM_DUMP_PALETTE */
+            for (i = 0; i < 16; i++) {
+                vm->ram[reg_b] = palette_default_[i];
+                reg_b += 1;
+            }
+            vm->cycle += 16;
+            break;
+    }
+}
+
+static struct renderer_ {
+    char *name;
+    char *args;
+    int (*renderer)(void *, struct pixel_ *, size_t, size_t);
+} lem1802_renderers_[] = {
+    { "pnm", "filename", pixbuf_render_pnm_ },
+#ifdef HAVE_LIBPNG
+    { "png", "filename", pixbuf_render_png_ },
+#endif /* HAVE_LIBPNG */
+#ifdef HAVE_LIBVNCSERVER
+    { "vnc", "", pixbuf_render_vnc_ },
+#endif /* HAVE_LIBVNCSERVER */
+    { "none", "", NULL },
+    { NULL, NULL, NULL }
+};
+
+int lem1802_renderer_set(struct dcpu16_hw *hw, const char *renderer, void *data) {
+    struct renderer_ *r;
+
+    for (r = lem1802_renderers_; r->renderer; r++) {
+        if (strcmp(renderer, r->name) == 0) {
+            ((struct lem1802_ *)(hw->data))->render = r->renderer;
+            ((struct lem1802_ *)(hw->data))->renderer_data = data;
+            return 0;
+        }
+    }
+
+    return -1;
+}
+
+char *lem1802_renderers_iter(void **iterp, char **name, char **args) {
+    struct renderer_ **r = (struct renderer_ **)iterp;
+
+    if (*r == NULL)
+        *r = lem1802_renderers_;
+    else
+        (*r)++;
+
+    if ((*r)->name == NULL) {
+        *r = NULL;
+        return NULL;
+    }
+
+    *name = (*r)->name;
+    *args = (*r)->args;
+
+    return (*r)->name;
+}
+
+/* instantitate a new display */
+struct dcpu16_hw *lem1802_new(struct dcpu16 *vm) {
+    struct dcpu16_hw *hw;
+
+    hw = calloc(1, sizeof *hw);
+    if (hw == NULL) {
+        vm->warn_cb_("%s():%s", "calloc", strerror(errno));
+        return NULL;
+    }
+
+    memcpy(hw, &hw_, sizeof *hw);
+
+    hw->data = calloc(1, sizeof(struct dcpu16_hw));
+    if (hw->data == NULL) {
+        vm->warn_cb_("%s():%s", "calloc", strerror(errno));
+        free(hw);
+        return NULL;
+    }
+
+    ((struct lem1802_ *)(hw->data))->pixbuf = calloc(1, PIX_X * PIX_Y * sizeof *((struct lem1802_ *)(hw->data))->pixbuf);
+    if (((struct lem1802_ *)(hw->data))->pixbuf == NULL) {
+        vm->warn_cb_("%s():%s", "calloc", strerror(errno));
+        free(hw->data);
+        free(hw);
+        return NULL;
+    }
+
+    return hw;
+}
+
+void lem1802_del(struct dcpu16_hw **hw) {
+    if (hw) {
+        if (*hw) {
+            if ((*hw)->data) {
+                if (((struct lem1802_ *)(*hw)->data)->pixbuf) {
+                    free(((struct lem1802_ *)(*hw)->data)->pixbuf);
+                    ((struct lem1802_ *)(*hw)->data)->pixbuf = NULL;
+                }
+                free((*hw)->data);
+                (*hw)->data = NULL;
+            }
+            free(*hw);
+        }
+        *hw = NULL;
+    }
+}
diff --git a/hw_lem1802.h b/hw_lem1802.h
new file mode 100644 (file)
index 0000000..fb739f4
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef LEM1802_H_WH5E5NOE
+#define LEM1802_H_WH5E5NOE
+
+#include "dcpu16.h"
+
+struct dcpu16_hw *lem1802_new(struct dcpu16 *);
+void lem1802_del(struct dcpu16_hw **);
+int lem1802_renderer_set(struct dcpu16_hw *, const char *, void *);
+char *lem1802_renderers_iter(void **, char **, char **);
+void *lem1802_vnc_init_data(int argc, char *argv[], struct dcpu16_hw *hw);
+
+#endif /* LEM1802_H_WH5E5NOE */
index 5924074aa468ce5c5370538fd819864ef69d53c2..6324717f0b28f9e23d1f879abe6b568fa353cd8f 100644 (file)
@@ -12,7 +12,8 @@
 
 #include "dcpu16.h"
 #include "common.h"
-#include "display.h"
+
+#include "hw_lem1802.h"
 
 /*
  *  shell-like driver for dcpu16 core
@@ -21,6 +22,7 @@
  *  Justin Wind <justin.wind@gmail.com>
  *    2012 04 10 - implementation started
  *    2012 04 12 - cleanup, better shell loop
+ *    2012 05 12 - support v1.7 style devices
  *
  *  TODO
  *    handle quotes in shell command parsing
@@ -65,7 +67,7 @@ void usage_(char *prog, unsigned int full) {
     if (full) {
         fprintf(f, "\nOptions:\n"
                 "\t [file] -- ram image to load initially\n"
-                "\t -v -- displays slightly more information\n"
+                "\t -v -- prints slightly more information while operating\n"
                 "\t -h -- this screen\n");
 
         fprintf(f, "\n%78s\n", src_id_);
@@ -140,6 +142,7 @@ int file_load_(struct dcpu16 *vm, char *filename, DCPU16_WORD addr) {
     return 0;
 }
 
+
 /*
     Here follows the various commands the shell can execute.
 
@@ -235,7 +238,7 @@ COMMAND_IMPL(dump) {
             return 0;
         }
     }
-    if (arg_count < 2) addr[0] = vm->pc;
+    if (arg_count < 2) addr[0] = vm->reg[DCPU16_REG_PC];
     if (arg_count < 3) addr[1] = addr[0];
 
     if (addr[1] < addr[0]) {
@@ -266,7 +269,7 @@ COMMAND_IMPL(disassemble) {
             return 0;
         }
     }
-    if (arg_count < 2) addr[0] = vm->pc;
+    if (arg_count < 2) addr[0] = vm->reg[DCPU16_REG_PC];
     if (arg_count < 3) addr[1] = addr[0];
 
     if (addr[1] < addr[0]) {
@@ -298,7 +301,7 @@ COMMAND_IMPL(step) {
         errno = 0;
         count = strtoul(arg_vector[1], &ep, 0);
         if (errno
-        ||  !(*arg_vector[0] && *ep == '\0') ) {
+        ||  !(*arg_vector[1] && *ep == '\0') ) {
             fprintf(stderr, "count '%s' is not a valid number: %s\n", arg_vector[1], strerror(errno));
             return 0;
         }
@@ -310,7 +313,7 @@ COMMAND_IMPL(step) {
     }
 
     while (count--) {
-        dcpu16_disassemble_print(vm, vm->pc);
+        dcpu16_disassemble_print(vm, vm->reg[DCPU16_REG_PC]);
         printf("\n");
         dcpu16_step(vm);
 
@@ -328,6 +331,46 @@ COMMAND_HELP(step) {
 }
 
 
+COMMAND_IMPL(set) {
+    int addr, value;
+    DCPU16_WORD *v;
+
+    (void)arg_count;
+
+    /* check if addr is a register */
+    for (addr = 0; dcpu16_reg_names[addr]; addr++) {
+        if (strcasecmp(arg_vector[1], dcpu16_reg_names[addr]) == 0)
+            break;
+    }
+    if (addr < DCPU16_REG__NUM) {
+        v = vm->reg + addr;
+    } else {
+        addr = str_to_word(arg_vector[1]);
+        if (addr < 0) {
+            fprintf(stderr, "address '%s' is not a valid word: %s\n", arg_vector[1], strerror(errno));
+            return 0;
+        }
+        v = vm->ram + addr;
+    }
+
+    value = str_to_word(arg_vector[2]);
+    if (value < 0) {
+        fprintf(stderr, "address '%s' is not a valid word: %s\n", arg_vector[2], strerror(errno));
+        return 0;
+    }
+
+    *v = value;
+
+    return 0;
+}
+
+COMMAND_HELP(set) {
+    fprintf(f, "\tset addr value\n");
+    if (summary) return;
+
+    fprintf(f, "Sets addr to value.");
+}
+
 COMMAND_IMPL(run) {
     struct sigaction act;
     (void)arg_count, (void)arg_vector;
@@ -348,7 +391,7 @@ COMMAND_IMPL(run) {
         if (opt_.verbose > 1)
             dcpu16_state_print(vm);
         else if (opt_.verbose) {
-            dcpu16_disassemble_print(vm, vm->pc);
+            dcpu16_disassemble_print(vm, vm->reg[DCPU16_REG_PC]);
             printf("\n");
         }
     }
@@ -373,53 +416,82 @@ static const char * const display_filename_default_ =
 #endif /* HAVE_LIBPNG */
 ;
 COMMAND_IMPL(display) {
-    static DCPU16_DISPLAY *display = NULL;
-    const char *filename = display_filename_default_;
+    struct dcpu16_hw *hw = lem1802_new(vm);
+    const char *renderer = arg_vector[1];
+    const char *renderer_arg = NULL;
+    void *renderer_data = NULL;
 
-    if (arg_count == 2) {
-        filename = arg_vector[1];
-    }
+    if (arg_count == 3)
+        renderer_arg = arg_vector[2];
 
-    if (display) {
-        printf("display already enabled..\n");
+    if (hw == NULL) {
+        fprintf(stderr, "failed to initialize new display\n");
         return 0;
     }
 
-    display = display_new(filename);
+    /* handle per-renderer setup of data.. */
+    /* FIXME: these are awkward */
+    if (strcmp(renderer, "pnm") == 0) {
+        if (renderer_arg == NULL)
+            renderer_arg = display_filename_default_;
+        renderer_data = (void *)renderer_arg;
+    }
 
-    if (display == NULL) {
-        fprintf(stderr, "failed to initialize display buffer\n");
-        return 0;
+#ifdef HAVE_LIBPNG
+    if (strcmp(renderer, "png") == 0) {
+        if (renderer_arg == NULL)
+            renderer_arg = display_filename_default_;
+        renderer_data = (void *)renderer_arg;
     }
+#endif /* HAVE_LIBPNG */
 
-    if (dcpu16_acct_add(vm, DCPU16_ACCT_EV_WRITE, display_fn, display)) {
-        fprintf(stderr, "failed to register display update callback\n");
-        return 0;
+#ifdef HAVE_LIBVNCSERVER
+    if (strcmp(renderer, "vnc") == 0) {
+        int argc = 1;
+        char *argv[] = { "vm-dcpu16", NULL };
+
+        renderer_data = lem1802_vnc_init_data(argc, argv, hw);
+
+        /* FIXME: keep refs to vnc displays around somewhere, in global list maybe.. */
+        /* keyboards will want to attach to them as well.. */
+
+        if (renderer_data == NULL) {
+            fprintf(stderr, "failed to initialize vnc\n");
+            lem1802_del(&hw);
+            return 0;
+        }
     }
+#endif /* HAVE_LIBVNCSERVER */
 
-    if (dcpu16_acct_add(vm, DCPU16_ACCT_EV_RESET, display_reset_fn, display)) {
-        fprintf(stderr, "failed to register display reset callback\n");
+    if (lem1802_renderer_set(hw, renderer, renderer_data)) {
+        fprintf(stderr, "failed to set back-end renderer for display\n");
+        lem1802_del(&hw);
         return 0;
     }
 
-    if (dcpu16_acct_add(vm, DCPU16_ACCT_EV_CYCLE, display_cycle_fn, display)) {
-        fprintf(stderr, "failed to register display cycle callback\n");
+    if (dcpu16_hw_add(vm, hw)) {
+        fprintf(stderr, "failed to attach new display\n");
+        lem1802_del(&hw);
         return 0;
     }
 
-    /* init display as if reset occurred */
-    display_reset_fn(vm, DCPU16_ACCT_EV_RESET, 0, display);
-
     return 0;
 }
 COMMAND_HELP(display) {
-    fprintf(f, "\tdisplay [file]\n");
+    char *name, *args;
+    void *iter;
+
+    fprintf(f, "\tdisplay renderer [renderer data]\n");
     if (summary) return;
 
-    fprintf(f, "Attaches display interface, begins updating an image file of display contents...\n"
-               "Image filename may be specified, defaults to '%s'\n",
-               display_filename_default_
+    fprintf(f, "Attaches new display unit, using 'renderer' as back-end output.\n"
             );
+
+    fprintf(f, "Supported renderers:\n");
+    iter = NULL;
+    while ( (lem1802_renderers_iter(&iter, &name, &args)) ) {
+        fprintf(f, "\t%s %s\n", name, args);
+    }
 }
 
 /* gather all these together into a searchable table */
@@ -436,8 +508,9 @@ static struct command_ command_table_[] = {
     COMMAND_ENTRY(disassemble, 0, 2),
     COMMAND_ENTRY(step, 0, 1),
     COMMAND_ENTRY(run, 0, 0),
+    COMMAND_ENTRY(set, 2, 2),
     COMMAND_ENTRY(reset, 0, 0),
-    COMMAND_ENTRY(display, 0, 1),
+    COMMAND_ENTRY(display, 1, 2),
     { NULL, 0, 0, NULL, NULL }
 };
 
@@ -519,13 +592,13 @@ int main(int argc, char **argv) {
     for (line = line_prev = NULL,
          tok_v = tok_v_prev = NULL,
          tok_c = tok_c_prev= 0,
-         snprintf(prompt, sizeof prompt, prompt_fmt, vm->pc),
+         snprintf(prompt, sizeof prompt, prompt_fmt, vm->reg[DCPU16_REG_PC]),
          dcpu16_state_print(vm);
 
          (line = readline(prompt));
 
          printf("\n"),
-         snprintf(prompt, sizeof prompt, prompt_fmt, vm->pc),
+         snprintf(prompt, sizeof prompt, prompt_fmt, vm->reg[DCPU16_REG_PC]),
          dcpu16_state_print(vm)) {
         const char whitespace[] = " \t";
         char *line_start;