diff --git a/externals/cxc/hex2dec-help.pd b/externals/cxc/hex2dec-help.pd
index 0e907bdd6a0734b490c2ebb405fb5391bdcc4eac..ffa1a8b0c8dabd0343d86afe6ab6e81af9ba0754 100644
--- a/externals/cxc/hex2dec-help.pd
+++ b/externals/cxc/hex2dec-help.pd
@@ -1,22 +1,45 @@
-#N canvas 0 31 450 300 10;
-#X obj 138 153 hex2dec;
-#X floatatom 137 189 5 0 0 0 - - -;
-#X msg 139 105 symbol fff;
-#X msg 237 123 123;
-#X obj 257 194 print;
-#X text 136 51 doesn't seem to work;
-#N canvas 397 200 494 344 META 0;
+#N canvas 3 60 560 479 10;
+#X obj 29 413 hex2dec;
+#N canvas 334 170 494 344 META 0;
 #X text 12 135 HELP_PATCH_AUTHORS Jonathan Wilkes revised the patch
 to conform to the PDDP template for Pd version 0.42.;
 #X text 12 55 DESCIPTION hexadecimal to decimal conversion;
 #X text 12 75 INLET_0 symbol float;
 #X text 12 95 OUTLET_0 float symbol;
-#X text 12 5 KEYWORDS control conversion symbol_op needs_work (object
-doesn't work);
 #X text 12 35 LICENSE GPL v2;
 #X text 12 115 AUTHOR jdl@xdv.org;
-#X restore 394 274 pd META;
-#X connect 0 0 1 0;
-#X connect 0 0 4 0;
-#X connect 2 0 0 0;
+#X text 12 5 KEYWORDS control conversion symbol_op;
+#X restore 504 454 pd META;
+#X text 22 11 treat input as a hex string and convert to float;
+#X msg 39 181 symbol ff;
+#X msg 65 237 symbol 255;
+#X msg 29 145 symbol 0xff \, symbol 0x02;
+#X text 28 104 To minimize errors due to Pd's type limitations \, prepend
+a "0x" to each hex string like so:;
+#X obj 29 448 print;
+#X text 187 145 <- "0x" makes everything a symbol atom;
+#X msg 57 209 3 \, 3.14;
+#X msg 73 279 symbol 0xffffff;
+#X floatatom 89 448 14 0 0 0 - - -, f 14;
+#X text 107 181 <- Unprefixed hex strings will work...;
+#X text 117 209 ... as will floats (truncated to ints)...;
+#X text 178 279 <- Also \, try to limit your input to byte-sized hex
+strings. Even 32-bit ints won't fit losslessly into single-precision
+floating point numbers. (Although they will fit into double-precision
+floats.);
+#X text 59 39 An incoming symbol message with a valid hex string is
+output in decimal notation as a float.;
+#X text 137 237 ... but without the "0x" prefix it's too easy to introduce
+subtle bugs like this one. So always use the "0x" prefix.;
+#X msg 73 361 list ff aa cc;
+#X text 165 360 lists may be sent \, too. But be careful-- more than
+10 elements will cause memory allocation that is not realtime safe.
+;
+#X connect 0 0 7 0;
+#X connect 0 0 11 0;
 #X connect 3 0 0 0;
+#X connect 4 0 0 0;
+#X connect 5 0 0 0;
+#X connect 9 0 0 0;
+#X connect 10 0 0 0;
+#X connect 17 0 0 0;
diff --git a/externals/cxc/hex2dec.c b/externals/cxc/hex2dec.c
index a812f4fc41dd5c4ef7ad668130fa523f3344074c..d85a4bb72240db48cc58804d4c2fe2c03febf46a 100644
--- a/externals/cxc/hex2dec.c
+++ b/externals/cxc/hex2dec.c
@@ -1,47 +1,136 @@
-#include "m_pd.h"
-#include <math.h>
-
+#include <stdlib.h>
+#include <limits.h>
+#include <errno.h>
 #include <string.h>
-#include <stdio.h>
 
-/* ----------------------- hex2dec --------------------- */
+#include "m_pd.h"
 
 static t_class *hex2dec_class;
 
-typedef struct _hex2dec
-{
-    t_object x_obj;
-    t_symbol *x_format;
+typedef struct _hex2dec {
+    t_object  x_obj;
 } t_hex2dec;
 
-static void *hex2dec_new(t_symbol *s)
+static int hex2dec_strtoll(t_hex2dec *x, char *buf, long long int *val)
 {
-    t_hex2dec *x = (t_hex2dec *)pd_new(hex2dec_class);
-    if (!s->s_name) s = gensym("file.%d");
-    outlet_new(&x->x_obj, &s_symbol);
-    x->x_format = s;
-    return (x);
+    char *endptr;
+    errno = 0;
+
+    /* No empty strings, please. This guards against things like
+       [symbol 42( silently failing */
+    if (!buf || *buf == '\0')
+    {
+        pd_error(x, "hex2dec: empty hex string detected");
+        return 0;
+    }
+
+    *val = strtoll(buf, &endptr, 16);
+    if (errno == ERANGE) {
+        if (*val == LLONG_MIN)
+            pd_error(x, "hex2dec: underflow detected");
+        else if (*val == LLONG_MAX)
+            pd_error(x, "hex2dec: overflow detected");
+        else
+            pd_error(x, "hex2dec: unknown range error");
+        return 0;
+    }
+    else if (errno != 0)
+    {
+        pd_error(x, "hex2dec: unknown error");
+        return 0;
+    }
+    else if (*endptr != '\0')
+    {
+        pd_error(x, "hex2dec: invalid input '%s'", buf);
+        return 0;
+    }
+    return 1;
 }
 
-static void hex2dec_float(t_hex2dec *x, t_floatarg f)
+    /* Let's see how we do with static allocation for size 10 lists. This
+       can be expanded later if people want to avoid heap allocations for
+       larger messages. */
+#define STATIC_SIZE 10
+int warned_about_allocation;
+
+static void hex2dec_list(t_hex2dec *x, t_symbol *s, int argc, t_atom *argv)
 {
+    t_atom at[STATIC_SIZE], *outvec;
     char buf[MAXPDSTRING];
-    sprintf(buf, x->x_format->s_name, (int)f);
-    outlet_symbol(x->x_obj.ob_outlet, gensym(buf));
+    if (argc > STATIC_SIZE)
+    {
+        if (!warned_about_allocation)
+        {
+            post("warning: hex2dec: realtime unsafe memory allocation for "
+                 "lists with more than %d elements", STATIC_SIZE);
+            warned_about_allocation++;
+        }
+        outvec = (t_atom *)t_getbytes(argc * sizeof(t_atom));
+    }
+    else
+        outvec = at;
+
+    int i;
+    for (i = 0; i < argc; i++)
+    {
+        if (argv[i].a_type == A_SYMBOL)
+        {
+            long long int val;
+            if (hex2dec_strtoll(x, argv[i].a_w.w_symbol->s_name, &val))
+                SETFLOAT(outvec + i, (t_float)val);
+            else
+                return; /* no output if we hit any errors */
+        }
+        else if (argv[i].a_type == A_FLOAT)
+        {
+            long long int val;
+            sprintf(buf, "%lld", (long long int)argv[i].a_w.w_float);
+            if (hex2dec_strtoll(x, buf, &val))
+                SETFLOAT(outvec + i, (t_float)val);
+            else
+                return;
+        }
+        else
+        {
+            pd_error(x, "hex2dec: only symbol and float accepted");
+            /* cleanup for large lists */
+            if (argc > STATIC_SIZE)
+                t_freebytes(outvec, argc * sizeof(t_atom));
+            return;
+        }
+    }
+    outlet_list(x->x_obj.ob_outlet, &s_list, argc, outvec);
+
+    /* cleanup for large lists */
+    if (argc > STATIC_SIZE)
+        t_freebytes(outvec, argc * sizeof(t_atom));
 }
 
-static void hex2dec_symbol(t_hex2dec *x, t_symbol *s)
+/* We could accept anything. But then we'd have to allocate for larger
+   messages, copy the selector to the first slot, then copy argv in the
+   remaining slots. There are some macros to achieve this in x_list.c
+   but they're specific to the list classes and therefore not public. */
+static void hex2dec_anything(t_hex2dec *x, t_symbol *s, int argc, t_atom *argv)
 {
-    char buf[MAXPDSTRING];
-    sprintf(buf, x->x_format->s_name, s->s_name);
-    outlet_symbol(x->x_obj.ob_outlet, gensym(buf));
+    if (s && s != &s_)
+        pd_error(x, "hex2dec: no method for '%s' (did you mean "
+                    "'%s %s%s'?)",
+        s->s_name, argc ? "list" : "symbol", s->s_name, argc ? " ..." : "");
+    else if (s && s == &s_)
+        pd_error(x, "hex2dec: no method for empty symbol");
+    else
+        pd_error(x, "hex2dec: only float, symbol, or list accepted");
 }
 
-void hex2dec_setup(void)
-{
-    hex2dec_class = class_new(gensym("hex2dec"),
-    (t_newmethod)hex2dec_new, 0,
-    	sizeof(t_hex2dec), 0, A_DEFSYM, 0);
-    class_addfloat(hex2dec_class, hex2dec_float);
-    class_addsymbol(hex2dec_class, hex2dec_symbol);
+static void *hex2dec_new(void) {
+    t_hex2dec *x = (t_hex2dec *)pd_new(hex2dec_class);
+    outlet_new(&x->x_obj, &s_float);
+    return (void *)x;
+}
+
+void hex2dec_setup(void) {
+    hex2dec_class = class_new(gensym("hex2dec"), (t_newmethod)hex2dec_new,
+        0, sizeof(t_hex2dec), CLASS_DEFAULT, A_DEFFLOAT, 0);
+    class_addlist(hex2dec_class, hex2dec_list);
+    class_addanything(hex2dec_class, hex2dec_anything);
 }
diff --git a/scripts/regression_tests/cxc_hex2dec_big_list.pd b/scripts/regression_tests/cxc_hex2dec_big_list.pd
new file mode 100644
index 0000000000000000000000000000000000000000..d07ec6706aa78e43cd2e04740d89c1465dc05ad1
--- /dev/null
+++ b/scripts/regression_tests/cxc_hex2dec_big_list.pd
@@ -0,0 +1,47 @@
+#N canvas 3 60 593 558 12;
+#X obj 41 8 inlet;
+#X obj 41 474 outlet;
+#X obj 41 336 cxc/hex2dec;
+#X obj 41 375 list length;
+#X obj 41 37 bang;
+#X obj 41 442 list append small list should generate output of same
+size;
+#X msg 111 33 bang;
+#X obj 115 177 f;
+#X obj 116 140 until;
+#X obj 115 206 makefilename 0x%.2d;
+#X obj 115 270 trigger anything;
+#X obj 41 301 list;
+#X obj 41 72 trigger bang bang bang;
+#X obj 151 177 + 1;
+#X msg 162 140 0;
+#X obj 115 241 list prepend;
+#X obj 41 404 == 100;
+#X obj 116 111 f 100;
+#X floatatom 387 58 5 0 0 0 - - -, f 5;
+#X obj 387 86 t a;
+#X connect 0 0 4 0;
+#X connect 2 0 3 0;
+#X connect 3 0 16 0;
+#X connect 4 0 12 0;
+#X connect 5 0 1 0;
+#X connect 6 0 12 0;
+#X connect 7 0 9 0;
+#X connect 7 0 13 0;
+#X connect 8 0 7 0;
+#X connect 9 0 15 0;
+#X connect 10 0 11 1;
+#X connect 10 0 15 1;
+#X connect 11 0 2 0;
+#X connect 12 0 11 0;
+#X connect 12 1 17 0;
+#X connect 12 2 14 0;
+#X connect 12 2 15 1;
+#X connect 13 0 7 1;
+#X connect 14 0 7 1;
+#X connect 15 0 10 0;
+#X connect 16 0 5 0;
+#X connect 17 0 8 0;
+#X connect 18 0 19 0;
+#X connect 19 0 17 1;
+#X connect 19 0 16 1;
diff --git a/scripts/regression_tests/cxc_hex2dec_empty_symbol.pd b/scripts/regression_tests/cxc_hex2dec_empty_symbol.pd
new file mode 100644
index 0000000000000000000000000000000000000000..cd8eb1846add3871f76024434e745a4c04323833
--- /dev/null
+++ b/scripts/regression_tests/cxc_hex2dec_empty_symbol.pd
@@ -0,0 +1,27 @@
+#N canvas 65 89 593 521 12;
+#X obj 41 8 inlet;
+#X obj 41 424 outlet;
+#X obj 155 136 cxc/hex2dec;
+#X obj 116 78 unpost;
+#X obj 41 37 trigger bang bang bang;
+#X obj 41 191 list;
+#X obj 41 220 route bang;
+#X obj 108 249 b;
+#X msg 41 249 0;
+#X msg 108 278 1;
+#X obj 41 322 list append empty symbol payload is not allowed;
+#X msg 155 106 symbol;
+#X connect 0 0 4 0;
+#X connect 3 0 5 1;
+#X connect 3 1 11 0;
+#X connect 4 0 5 0;
+#X connect 4 1 3 0;
+#X connect 4 2 5 1;
+#X connect 5 0 6 0;
+#X connect 6 0 8 0;
+#X connect 6 1 7 0;
+#X connect 7 0 9 0;
+#X connect 8 0 10 0;
+#X connect 9 0 10 0;
+#X connect 10 0 1 0;
+#X connect 11 0 2 0;
diff --git a/scripts/regression_tests/cxc_hex2dec_invalid.pd b/scripts/regression_tests/cxc_hex2dec_invalid.pd
new file mode 100644
index 0000000000000000000000000000000000000000..96d8166878ffa5a3d3d6cad824ac0c54449c79b7
--- /dev/null
+++ b/scripts/regression_tests/cxc_hex2dec_invalid.pd
@@ -0,0 +1,28 @@
+#N canvas 3 60 593 521 12;
+#X obj 41 8 inlet;
+#X obj 41 424 outlet;
+#X obj 155 136 cxc/hex2dec;
+#X obj 116 78 unpost;
+#X obj 41 37 trigger bang bang bang;
+#X obj 41 191 list;
+#X obj 41 220 route bang;
+#X obj 108 249 b;
+#X msg 41 249 0;
+#X msg 108 278 1;
+#X msg 155 106 symbol 0xinvalid;
+#X obj 41 322 list append invalid input string should output an error
+;
+#X connect 0 0 4 0;
+#X connect 3 0 5 1;
+#X connect 3 1 10 0;
+#X connect 4 0 5 0;
+#X connect 4 1 3 0;
+#X connect 4 2 5 1;
+#X connect 5 0 6 0;
+#X connect 6 0 8 0;
+#X connect 6 1 7 0;
+#X connect 7 0 9 0;
+#X connect 8 0 11 0;
+#X connect 9 0 11 0;
+#X connect 10 0 2 0;
+#X connect 11 0 1 0;
diff --git a/scripts/regression_tests/cxc_hex2dec_overflow.pd b/scripts/regression_tests/cxc_hex2dec_overflow.pd
new file mode 100644
index 0000000000000000000000000000000000000000..5af1f5ffc16afb31472db8df84958961d273faeb
--- /dev/null
+++ b/scripts/regression_tests/cxc_hex2dec_overflow.pd
@@ -0,0 +1,27 @@
+#N canvas 3 60 593 521 12;
+#X obj 41 8 inlet;
+#X obj 41 424 outlet;
+#X obj 155 136 cxc/hex2dec;
+#X obj 116 78 unpost;
+#X obj 41 37 trigger bang bang bang;
+#X obj 41 191 list;
+#X obj 41 220 route bang;
+#X obj 108 249 b;
+#X msg 41 249 0;
+#X msg 108 278 1;
+#X msg 155 106 symbol 0xffffffffffffffff;
+#X obj 41 322 list append overflow should output an error;
+#X connect 0 0 4 0;
+#X connect 3 0 5 1;
+#X connect 3 1 10 0;
+#X connect 4 0 5 0;
+#X connect 4 1 3 0;
+#X connect 4 2 5 1;
+#X connect 5 0 6 0;
+#X connect 6 0 8 0;
+#X connect 6 1 7 0;
+#X connect 7 0 9 0;
+#X connect 8 0 11 0;
+#X connect 9 0 11 0;
+#X connect 10 0 2 0;
+#X connect 11 0 1 0;
diff --git a/scripts/regression_tests/cxc_hex2dec_small_list.pd b/scripts/regression_tests/cxc_hex2dec_small_list.pd
new file mode 100644
index 0000000000000000000000000000000000000000..04c8c16c75f5afe35efd151190ac6ce987b74ae1
--- /dev/null
+++ b/scripts/regression_tests/cxc_hex2dec_small_list.pd
@@ -0,0 +1,17 @@
+#N canvas 3 60 593 521 12;
+#X obj 41 8 inlet;
+#X obj 41 424 outlet;
+#X obj 41 136 cxc/hex2dec;
+#X msg 41 106 list 0x00 0x01 0x02 0x03;
+#X obj 41 165 list length;
+#X obj 41 194 == 4;
+#X obj 41 37 bang;
+#X obj 41 322 list append small list should generate output of same
+size;
+#X connect 0 0 6 0;
+#X connect 2 0 4 0;
+#X connect 3 0 2 0;
+#X connect 4 0 5 0;
+#X connect 5 0 7 0;
+#X connect 6 0 3 0;
+#X connect 7 0 1 0;
diff --git a/scripts/regression_tests/cxc_hex2dec_underflow.pd b/scripts/regression_tests/cxc_hex2dec_underflow.pd
new file mode 100644
index 0000000000000000000000000000000000000000..09f208923970def81583af34b5501b4c754d22db
--- /dev/null
+++ b/scripts/regression_tests/cxc_hex2dec_underflow.pd
@@ -0,0 +1,27 @@
+#N canvas 3 60 593 521 12;
+#X obj 41 8 inlet;
+#X obj 41 424 outlet;
+#X obj 155 136 cxc/hex2dec;
+#X obj 116 78 unpost;
+#X obj 41 37 trigger bang bang bang;
+#X obj 41 191 list;
+#X obj 41 220 route bang;
+#X obj 108 249 b;
+#X msg 41 249 0;
+#X msg 108 278 1;
+#X msg 155 106 symbol -0xffffffffffffffff;
+#X obj 41 322 list append underflow should output an error;
+#X connect 0 0 4 0;
+#X connect 3 0 5 1;
+#X connect 3 1 10 0;
+#X connect 4 0 5 0;
+#X connect 4 1 3 0;
+#X connect 4 2 5 1;
+#X connect 5 0 6 0;
+#X connect 6 0 8 0;
+#X connect 6 1 7 0;
+#X connect 7 0 9 0;
+#X connect 8 0 11 0;
+#X connect 9 0 11 0;
+#X connect 10 0 2 0;
+#X connect 11 0 1 0;