further reorg of module abstraction and control interface
authorJustin Wind <justin.wind@gmail.com>
Sun, 20 May 2012 20:52:09 +0000 (13:52 -0700)
committerJustin Wind <justin.wind@gmail.com>
Sun, 20 May 2012 20:52:09 +0000 (13:52 -0700)
dcpu16.c
dcpu16.h
hw_clock.c
hw_keyboard.c
hw_keyboard.h
hw_lem1802.c
hw_lem1802.h
hw_spc2000.c
vm-dcpu16.c

index a0e1ad0ca3864ee2be54535b1f5030c79713a9a2..2b39bafd35507092237027462fdf3d1b419502f0 100644 (file)
--- a/dcpu16.c
+++ b/dcpu16.c
@@ -1369,13 +1369,13 @@ struct dcpu16_hw *dcpu16_hw_new(struct dcpu16 *vm, struct dcpu16_hw_module *mod,
     }
     memcpy(hw, mod->template, sizeof *hw);
     hw->vm = vm;
     }
     memcpy(hw, mod->template, sizeof *hw);
     hw->vm = vm;
+    hw->mod = mod;
 
     if (mod->data_init(hw, data)) {
         vm->warn_cb_("failed to init hw module data");
         free(hw);
         return NULL;
     }
 
     if (mod->data_init(hw, data)) {
         vm->warn_cb_("failed to init hw module data");
         free(hw);
         return NULL;
     }
-    hw->data_free = mod->data_free;
 
     return hw;
 }
 
     return hw;
 }
@@ -1384,8 +1384,8 @@ struct dcpu16_hw *dcpu16_hw_new(struct dcpu16 *vm, struct dcpu16_hw_module *mod,
 void dcpu16_hw_del(struct dcpu16_hw **hw) {
     if (hw) {
         if (*hw) {
 void dcpu16_hw_del(struct dcpu16_hw **hw) {
     if (hw) {
         if (*hw) {
-            if ((*hw)->data_free) {
-                (*hw)->data_free(*hw);
+            if ((*hw)->mod->data_free) {
+                (*hw)->mod->data_free(*hw);
             }
             free(*hw);
             *hw = NULL;
             }
             free(*hw);
             *hw = NULL;
@@ -1393,7 +1393,24 @@ void dcpu16_hw_del(struct dcpu16_hw **hw) {
     }
 }
 
     }
 }
 
-/*  dcpu16_hw_add
+/*  dcpu16_hw_ctl
+ *  invokes per-module controls for hw device
+ */
+int dcpu16_hw_ctl(struct dcpu16_hw *hw, const char *cmd, void *data_in, void *data_out) {
+    if (hw) {
+        if (hw->mod) {
+            if (hw->mod->ctl) {
+                if (cmd) {
+                    return hw->mod->ctl(hw, cmd, data_in, data_out);
+                }
+            }
+        }
+    }
+    return 0;
+}
+
+
+/*  dcpu16_hw_attach
  *  registers new 'hardware' device with system
  */
 int dcpu16_hw_attach(struct dcpu16 *vm, struct dcpu16_hw *hw) {
  *  registers new 'hardware' device with system
  */
 int dcpu16_hw_attach(struct dcpu16 *vm, struct dcpu16_hw *hw) {
index 9ca39124e402cfa8b5030a5e65513f54d1ecb7dc..f70372a0d11655034ff279e038aeb0930c54b9d4 100644 (file)
--- a/dcpu16.h
+++ b/dcpu16.h
@@ -68,10 +68,10 @@ struct dcpu16_acct_cb {
 };
 
 typedef void (dcpu16_hw_signal_t)(struct dcpu16 *, struct dcpu16_hw *);
 };
 
 typedef void (dcpu16_hw_signal_t)(struct dcpu16 *, struct dcpu16_hw *);
-typedef void (dcpu16_hw_data_free_t)(struct dcpu16_hw *);
 /* these are used to define hardware attached to the system */
 struct dcpu16_hw {
     struct dcpu16 *vm;  /* which system do I belong to */
 /* these are used to define hardware attached to the system */
 struct dcpu16_hw {
     struct dcpu16 *vm;  /* which system do I belong to */
+    struct dcpu16_hw_module *mod;   /* whence I came */
     char *name_;
 
     DCPU16_WORD id_l;
     char *name_;
 
     DCPU16_WORD id_l;
@@ -84,15 +84,25 @@ struct dcpu16_hw {
     dcpu16_hw_signal_t *cycle;
     dcpu16_hw_signal_t *reset;
 
     dcpu16_hw_signal_t *cycle;
     dcpu16_hw_signal_t *reset;
 
-    dcpu16_hw_data_free_t *data_free;
     void *data;
 };
 
     void *data;
 };
 
+/* human-readable text describing hw module control operations, for convenience's sake */
+struct dcpu16_hw_ctl_cmd {
+    char *command;
+    char *data_in_type;
+    char *data_out_type;
+    char *description;
+};
 typedef int (dcpu16_hw_data_init_t)(struct dcpu16_hw *, void *);
 typedef int (dcpu16_hw_data_init_t)(struct dcpu16_hw *, void *);
+typedef void (dcpu16_hw_data_free_t)(struct dcpu16_hw *);
+typedef int (dcpu16_hw_ctl_t)(struct dcpu16_hw *, const char *, void *, void *);
 struct dcpu16_hw_module {
     struct dcpu16_hw *template;
     dcpu16_hw_data_init_t *data_init;
     dcpu16_hw_data_free_t *data_free;
 struct dcpu16_hw_module {
     struct dcpu16_hw *template;
     dcpu16_hw_data_init_t *data_init;
     dcpu16_hw_data_free_t *data_free;
+    dcpu16_hw_ctl_t *ctl;
+    struct dcpu16_hw_ctl_cmd *ctl_cmd;
 };
 
 /* instantiate a new core */
 };
 
 /* instantiate a new core */
@@ -117,6 +127,9 @@ DCPU16_WORD dcpu16_disassemble_print(struct dcpu16 *, DCPU16_WORD);
 struct dcpu16_hw *dcpu16_hw_new(struct dcpu16 *, struct dcpu16_hw_module *, void *);
 void dcpu16_hw_del(struct dcpu16_hw **);
 
 struct dcpu16_hw *dcpu16_hw_new(struct dcpu16 *, struct dcpu16_hw_module *, void *);
 void dcpu16_hw_del(struct dcpu16_hw **);
 
+/* set options on hardware objects */
+int dcpu16_hw_ctl(struct dcpu16_hw *, const char *, void *, void *);
+
 /* register new 'hardware' device with system */
 int dcpu16_hw_attach(struct dcpu16 *, struct dcpu16_hw *);
 
 /* register new 'hardware' device with system */
 int dcpu16_hw_attach(struct dcpu16 *, struct dcpu16_hw *);
 
index f70dcf4c3d368f634f12435d62ab5cd4ea2c1f37..83eaa8090598866838ed90df00dc88da81e1d0c1 100644 (file)
@@ -104,4 +104,6 @@ struct dcpu16_hw_module dcpu16_hw_module_clock = {
     .template = &hw_,
     .data_init = clock_data_init_,
     .data_free = clock_data_free_,
     .template = &hw_,
     .data_init = clock_data_init_,
     .data_free = clock_data_free_,
+    .ctl = NULL,
+    .ctl_cmd = NULL,
 };
 };
index 428971d5a6a6ecfcb51c0e19e88d3ed024c23ab6..592b6735736a23135220f5ceb4952e3fb39b8fed 100644 (file)
@@ -70,19 +70,12 @@ void keyboard_rfbevent_(rfbBool down, rfbKeySym key, rfbClientPtr cl) {
     struct dcpu16_hw *hw = (struct dcpu16_hw *)cl->screen->screenData;
     struct keyboard_ *keyboard = (struct keyboard_ *)hw->data;
 
     struct dcpu16_hw *hw = (struct dcpu16_hw *)cl->screen->screenData;
     struct keyboard_ *keyboard = (struct keyboard_ *)hw->data;
 
-    keysym_rfbtodcpu(key, &dcpu_key);
+    hw->vm->trace_cb_("%s>> down:%u rfb_key:0x%04x", down, key);
 
 
-    fprintf(stderr, "%s: down:%u key:0x%04x dcpu_key:0x%04x\n",
-            __func__,
-            down,
-            key,
-            dcpu_key);
-
-    fprintf(stderr, "%s: hw:%p name:%s vm:%p\n",
-            __func__,
-            VOIDP(hw),
-            hw->name_,
-            VOIDP(hw->vm));
+    if (keysym_rfbtodcpu(key, &dcpu_key)) {
+        /* unhandled key event */
+        return;
+    }
 
     keyboard->keys_pressed[dcpu_key] = (down ? 1 : 0);
     if (down) {
 
     keyboard->keys_pressed[dcpu_key] = (down ? 1 : 0);
     if (down) {
@@ -112,7 +105,9 @@ void keyboard_reset_(struct dcpu16 *vm, struct dcpu16_hw *hw) {
     (void)vm;
 
     keyboard->interrupt_message = 0;
     (void)vm;
 
     keyboard->interrupt_message = 0;
-    memset(keyboard->buf, 0, keyboard->buf_sz);
+    keyboard->buf_head = 0;
+    keyboard->buf_tail = 0;
+    memset(keyboard->keys_pressed, 0, sizeof keyboard->keys_pressed);
 }
 
 static
 }
 
 static
@@ -191,6 +186,57 @@ void keyboard_data_free_(struct dcpu16_hw *hw) {
     }
 }
 
     }
 }
 
+static struct dcpu16_hw_ctl_cmd ctl_[] = {
+    { "buffer_size", "const size_t *", "size_t *", "get or set current buffer size" },
+    { "associate_rfbScreen", "rfbScreenInfoPtr", "NULL", "associates this keyboard instance with an rfb display" },
+    { NULL, NULL, NULL, NULL }
+};
+static
+int keyboard_data_ctl_(struct dcpu16_hw *hw, const char *cmd, void *data_in, void *data_out) {
+    if (strcmp(cmd, "buffer_size") == 0) {
+        struct keyboard_ *keyboard = (struct keyboard_ *)hw->data;
+        void *tmp_ptr;
+        const size_t *buf_sz_in = (const size_t *)data_in;
+        size_t *buf_sz_out = (size_t *)data_out;
+
+        if (buf_sz_out) {
+            *buf_sz_out = keyboard->buf_sz;
+        }
+
+        if (buf_sz_in) {
+            hw->vm->trace_cb_("%s>> resizing buffer from %zu to %zu", __func__, keyboard->buf_sz, *buf_sz_in);
+
+            tmp_ptr = realloc(keyboard->buf, *buf_sz_in);
+            if (tmp_ptr == NULL) {
+                hw->vm->warn_cb_("%s():%s", "realloc", strerror(errno));
+                return -1;
+            }
+            keyboard->buf = tmp_ptr;
+            keyboard->buf_sz = *buf_sz_in;
+            keyboard->buf_head = keyboard->buf_tail = 0;
+        }
+
+        return 0;
+    }
+
+#ifdef HAVE_LIBVNCSERVER
+    if (strcmp(cmd, "associate_rfbScreen") == 0) {
+        rfbScreenInfoPtr rfbScreen = (rfbScreenInfoPtr)data_in;
+        (void)data_out;
+
+        if (rfbScreen == NULL)
+            return -EFAULT;
+
+        rfbScreen->screenData = hw;
+        rfbScreen->kbdAddEvent = keyboard_rfbevent_;
+
+        return 0;
+    }
+#endif /* HAVE_LIBVNCSERVER */
+
+    return -EINVAL;
+}
+
 static struct dcpu16_hw hw_ = {
     .vm     = NULL,
     .name_  = "Generic Keyboard (compatible)",
 static struct dcpu16_hw hw_ = {
     .vm     = NULL,
     .name_  = "Generic Keyboard (compatible)",
@@ -209,4 +255,6 @@ struct dcpu16_hw_module dcpu16_hw_module_keyboard = {
     .template = &hw_,
     .data_init = keyboard_data_init_,
     .data_free = keyboard_data_free_,
     .template = &hw_,
     .data_init = keyboard_data_init_,
     .data_free = keyboard_data_free_,
+    .ctl = keyboard_data_ctl_,
+    .ctl_cmd = ctl_,
 };
 };
index bb44a93346e7026e615ff66fbf1e9b2a4cbed797..b8ea7772e4e26629b24f7923aca6597aab4c4673 100644 (file)
@@ -1,16 +1,8 @@
 #ifndef KEYBOARD_H_Y2G5EOAS
 #define KEYBOARD_H_Y2G5EOAS
 
 #ifndef KEYBOARD_H_Y2G5EOAS
 #define KEYBOARD_H_Y2G5EOAS
 
-#ifdef HAVE_LIBVNCSERVER
-#include "rfb/rfb.h"
-#endif /* HAVE_LIBVNCSERVER */
-
 #include "dcpu16.h"
 
 extern struct dcpu16_hw_module dcpu16_hw_module_keyboard;
 
 #include "dcpu16.h"
 
 extern struct dcpu16_hw_module dcpu16_hw_module_keyboard;
 
-#ifdef HAVE_LIBVNCSERVER
-void keyboard_vnc_associate(struct dcpu16_hw *, rfbScreenInfoPtr);
-#endif /* HAVE_LIBVNCSERVER */
-
 #endif /* KEYBOARD_H_Y2G5EOAS */
 #endif /* KEYBOARD_H_Y2G5EOAS */
index f17ebc73287a2eff7eaa083f37fb400e6bf3548c..06b0d5ac2c5d9392fb58d0b76c19995fb38d67f8 100644 (file)
 #include "chargen-4x8.h"
 #include "hw_lem1802.h"
 
 #include "chargen-4x8.h"
 #include "hw_lem1802.h"
 
+/* lem1802
+ *
+ * TODO:
+ * multiple vnc displays
+ */
+
 #ifdef DEBUG
 #define TRACE(...) do { printf("[debug] "); printf(__VA_ARGS__); printf("\n"); } while (0)
 #else /* DEBUG  */
 #ifdef DEBUG
 #define TRACE(...) do { printf("[debug] "); printf(__VA_ARGS__); printf("\n"); } while (0)
 #else /* DEBUG  */
@@ -337,15 +343,6 @@ rfbScreenInfoPtr lem1802_rfb_new(int argc, char *argv[]) {
     return s;
 }
 
     return s;
 }
 
-/* set up a new screen to see our pixels */
-void lem1802_vnc_associate(struct dcpu16_hw *hw, rfbScreenInfoPtr s) {
-    struct lem1802_ *display = (struct lem1802_ *)hw->data;
-
-    s->desktopName = "NYA ELEKTRISKA LEM1802";
-    s->frameBuffer = (char *)display->pixbuf;
-
-    TRACE("%s>> s:%p\n", __func__, VOIDP(s));
-}
 
 /* notify rfb server that pixels may have changed */
 static
 
 /* notify rfb server that pixels may have changed */
 static
@@ -512,20 +509,6 @@ static struct renderer_ {
     { NULL, NULL, 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;
-            TRACE("%s>> renderer set to %s", __func__, renderer);
-            return 0;
-        }
-    }
-
-    return -1;
-}
 
 char *lem1802_renderers_iter(void **iterp, char **name, char **args) {
     struct renderer_ **r = (struct renderer_ **)iterp;
 
 char *lem1802_renderers_iter(void **iterp, char **name, char **args) {
     struct renderer_ **r = (struct renderer_ **)iterp;
@@ -572,20 +555,106 @@ int lem1802_data_init_(struct dcpu16_hw *hw, void *data) {
 void lem1802_data_free_(struct dcpu16_hw *hw) {
     if (hw) {
         if (hw->data) {
 void lem1802_data_free_(struct dcpu16_hw *hw) {
     if (hw) {
         if (hw->data) {
-            /* FIXME: free renderer data */
-            hw->vm->warn_cb_("FIXME");
-
             if (((struct lem1802_ *)(hw->data))->pixbuf) {
                 free(((struct lem1802_ *)(hw->data))->pixbuf);
                 ((struct lem1802_ *)(hw->data))->pixbuf = NULL;
             }
             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->data);
             hw->data = NULL;
         }
     }
 }
 
+static struct dcpu16_hw_ctl_cmd ctl_[] = {
+    { "blink_rate", "const unsigned int *rate", "unsigned int *rate", "sets or gets cycles per blink toggle" },
+    { "refresh_rate", "const unsigned int *rate", "unsigned int *rate", "sets or gets cycles per screen refresh" },
+#ifdef HAVE_LIBVNCSERVER
+    { "associate_rfbScreen", "rfbScreenInfoPtr", "NULL", "associates this lem1802 instance with an rfb display" },
+#endif /* HAVE_LIBVNCSERVER */
+    { "renderer", "const char *", "NULL", "sets this lem1802 instance to use renderer" },
+    { "renderer_data", "void *", "NULL", "sets renderer-specific data" },
+    { NULL, NULL, NULL, NULL }
+};
+int lem1802_data_ctl_(struct dcpu16_hw *hw, const char *cmd, void *data_in, void *data_out) {
+    if (strcmp(cmd, "blink_rate") == 0) {
+        struct lem1802_ *display = (struct lem1802_ *)hw->data;
+        const unsigned int *rate_in = (const unsigned int *)data_in;
+        unsigned int *rate_out = (unsigned int *)data_out;
+
+        if (rate_out) {
+            *rate_out = display->blink_rate;
+        }
+
+        if (rate_in) {
+            display->blink_rate = *rate_in;
+        }
+
+        return 0;
+    }
+
+    if (strcmp(cmd, "refresh_rate") == 0) {
+        struct lem1802_ *display = (struct lem1802_ *)hw->data;
+        const unsigned int *rate_in = (const unsigned int *)data_in;
+        unsigned int *rate_out = (unsigned int *)data_out;
+
+        if (rate_out) {
+            *rate_out = display->refresh_rate;
+        }
+
+        if (rate_in) {
+            display->refresh_rate = *rate_in;
+        }
+
+        return 0;
+    }
+
+#ifdef HAVE_LIBVNCSERVER
+    if (strcmp(cmd, "associate_rfbScreen") == 0) {
+        struct lem1802_ *display = (struct lem1802_ *)hw->data;
+        rfbScreenInfoPtr rfbScreen = (rfbScreenInfoPtr)data_out;
+        (void)data_in;
+
+        if (rfbScreen == NULL)
+            return -EFAULT;
+
+        rfbScreen->desktopName = "NYA ELEKTRISKA LEM1802";
+        rfbScreen->frameBuffer = (char *)display->pixbuf;
+
+        return 0;
+    }
+#endif /* HAVE_LIBVNCSERVER */
+
+    if (strcmp(cmd, "renderer") == 0) {
+        struct lem1802_ *display = (struct lem1802_ *)hw->data;
+        char *renderer = (char *)data_in;
+        (void)data_out;
+        struct renderer_ *r;
+
+        for (r = lem1802_renderers_; r->renderer; r++) {
+            if (strcmp(renderer, r->name) == 0) {
+                display->render = r->renderer;
+                TRACE("%s>> renderer set to %s", __func__, renderer);
+                return 0;
+            }
+        }
+
+        hw->vm->warn_cb_("unknown renderer '%s'", renderer);
+
+        return -ENOENT;
+    }
+
+    if (strcmp(cmd, "renderer_data") == 0) {
+        struct lem1802_ *display = (struct lem1802_ *)hw->data;
+        (void)data_out;
+
+        display->renderer_data = data_in;
+
+        return 0;
+    }
+
+    return -EINVAL;
+}
+
 
 static struct dcpu16_hw hw_ = {
     .name_  = "LEM1802 - Low Energy Monitor",
 
 static struct dcpu16_hw hw_ = {
     .name_  = "LEM1802 - Low Energy Monitor",
@@ -604,5 +673,7 @@ struct dcpu16_hw_module dcpu16_hw_module_lem1802 = {
     .template = &hw_,
     .data_init = lem1802_data_init_,
     .data_free = lem1802_data_free_,
     .template = &hw_,
     .data_init = lem1802_data_init_,
     .data_free = lem1802_data_free_,
+    .ctl = lem1802_data_ctl_,
+    .ctl_cmd = ctl_,
 };
 
 };
 
index df333e9f8da87b298f8682f74beb23b71c1e9fe9..38ffbb01e61e3dc3038f345cb5f322fbf5192b4d 100644 (file)
@@ -9,12 +9,10 @@
 
 extern struct dcpu16_hw_module dcpu16_hw_module_lem1802;
 
 
 extern struct dcpu16_hw_module dcpu16_hw_module_lem1802;
 
-int lem1802_renderer_set(struct dcpu16_hw *, const char *, void *);
 char *lem1802_renderers_iter(void **, char **, char **);
 
 #ifdef HAVE_LIBVNCSERVER
 rfbScreenInfoPtr lem1802_rfb_new(int argc, char *argv[]);
 char *lem1802_renderers_iter(void **, char **, char **);
 
 #ifdef HAVE_LIBVNCSERVER
 rfbScreenInfoPtr lem1802_rfb_new(int argc, char *argv[]);
-void lem1802_vnc_associate(struct dcpu16_hw *, rfbScreenInfoPtr);
 #endif /* HAVE_LIBVNCSERVER */
 
 #endif /* LEM1802_H_WH5E5NOE */
 #endif /* HAVE_LIBVNCSERVER */
 
 #endif /* LEM1802_H_WH5E5NOE */
index f07489578b1157e80842a8ac0d7027761395ed99..c967822279768111ec770cb7dbb42a3fcd39c325 100644 (file)
@@ -103,4 +103,6 @@ struct dcpu16_hw_module dcpu16_hw_module_spc2000 = {
     .template = &hw_,
     .data_init = spc2000_data_init_,
     .data_free = spc2000_data_free_,
     .template = &hw_,
     .data_init = spc2000_data_init_,
     .data_free = spc2000_data_free_,
+    .ctl = NULL,
+    .ctl_cmd = NULL,
 };
 };
index b8a35327b0721bc75961fa86a3746ac227b7a3e5..a6dc67dac2ca2f2212eac09ffe2836e46e913fc1 100644 (file)
@@ -564,18 +564,19 @@ COMMAND_IMPL(display) {
             return 0;
         }
 
             return 0;
         }
 
-        lem1802_vnc_associate(hw, s->screen);
+        if (dcpu16_hw_ctl(hw, "associate_rfbScreen", s->screen, NULL)) {
+            fprintf(stderr, "failed to configure display/vnc");
+            dcpu16_hw_del(&hw);
+            return 0;
+        }
         s->attached_display = hw;
         rfbScreen_start(s->screen);
         renderer_data = s->screen;
     }
 #endif /* HAVE_LIBVNCSERVER */
 
         s->attached_display = hw;
         rfbScreen_start(s->screen);
         renderer_data = s->screen;
     }
 #endif /* HAVE_LIBVNCSERVER */
 
-    if (lem1802_renderer_set(hw, renderer, renderer_data)) {
-        fprintf(stderr, "failed to set back-end renderer for display\n");
-        dcpu16_hw_del(&hw);
-        return 0;
-    }
+    dcpu16_hw_ctl(hw, "renderer", (char *)renderer, NULL);
+    dcpu16_hw_ctl(hw, "renderer_data", renderer_data, NULL);
 
     if (dcpu16_hw_attach(vm, hw)) {
         fprintf(stderr, "failed to attach new display\n");
 
     if (dcpu16_hw_attach(vm, hw)) {
         fprintf(stderr, "failed to attach new display\n");
@@ -586,6 +587,11 @@ COMMAND_IMPL(display) {
     return 0;
 }
 COMMAND_HELP(display) {
     return 0;
 }
 COMMAND_HELP(display) {
+    struct renderer_ {
+        char *name;
+        char *args;
+        int (*renderer)(void *, void *, size_t, size_t);
+    } *r;
     char *name, *args;
     void *iter;
 
     char *name, *args;
     void *iter;
 
@@ -596,6 +602,16 @@ COMMAND_HELP(display) {
             );
 
     fprintf(f, "Supported renderers:\n");
             );
 
     fprintf(f, "Supported renderers:\n");
+
+    if (dcpu16_hw_module_lem1802.ctl(NULL, "get_renderers", NULL, &r)) {
+        fprintf(stderr, "error fetching list of renderers\n");
+        return;
+    }
+
+    while (r->name) {
+        fprintf(f, "name:%s args:%s\n", r->name, r->args);
+    }
+
     iter = NULL;
     while ( (lem1802_renderers_iter(&iter, &name, &args)) ) {
         fprintf(f, "\t%s %s\n", name, args);
     iter = NULL;
     while ( (lem1802_renderers_iter(&iter, &name, &args)) ) {
         fprintf(f, "\t%s %s\n", name, args);
@@ -624,7 +640,11 @@ COMMAND_IMPL(keyboard) {
         dcpu16_hw_del(&hw);
         return 0;
     }
         dcpu16_hw_del(&hw);
         return 0;
     }
-    keyboard_vnc_associate(hw, s->screen);
+    if (dcpu16_hw_ctl(hw, "associate_rfbScreen", s->screen, NULL)) {
+        fprintf(stderr, "failed to configure keyboard/vnc\n");
+        dcpu16_hw_del(&hw);
+        return 0;
+    }
     s->attached_keyboard = hw;
 
     if (dcpu16_hw_attach(vm, hw)) {
     s->attached_keyboard = hw;
 
     if (dcpu16_hw_attach(vm, hw)) {