diff --git a/py/runtime.c b/py/runtime.c
index b133b31e06fbc3628d410b42ffc3825e9d73d266..a93d05d9b3c036c6c05473c577ac3fc5cddfdc86 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -71,7 +71,7 @@ typedef struct _py_obj_base_t {
         const char *id;
         qstr u_str;
 #ifdef PY_FLOAT
-        float_t flt;
+        float_t u_flt;
 #endif
         struct { // for O_FUN_[012N]
             int n_args;
@@ -258,7 +258,7 @@ py_obj_t py_obj_new_str(qstr qstr) {
 py_obj_t py_obj_new_float(float_t val) {
     py_obj_base_t *o = m_new(py_obj_base_t, 1);
     o->kind = O_FLOAT;
-    o->flt = val;
+    o->u_flt = val;
     return (py_obj_t)o;
 }
 #endif
@@ -298,7 +298,7 @@ typedef struct _py_code_t {
             py_fun_t fun;
         } u_native;
         struct {
-            py_fun_t fun;
+            void *fun;
         } u_inline_asm;
     };
 } py_code_t;
@@ -497,7 +497,7 @@ void py_obj_print(py_obj_t o_in) {
                 break;
 #ifdef PY_FLOAT
             case O_FLOAT:
-                printf("%f", o->flt);
+                printf("%f", o->u_flt);
                 break;
 #endif
             case O_LIST:
@@ -745,16 +745,63 @@ py_obj_t rt_make_function(int n_args, py_fun_t code) {
     return o;
 }
 
+// convert a Python object to a sensible value for inline asm
+machine_uint_t rt_convert_obj_for_inline_asm(py_obj_t obj) {
+    // TODO for byte_array, pass pointer to the array
+    if (IS_SMALL_INT(obj)) {
+        return FROM_SMALL_INT(obj);
+    } else if (obj == py_const_none) {
+        return 0;
+    } else if (obj == py_const_false) {
+        return 0;
+    } else if (obj == py_const_true) {
+        return 1;
+    } else {
+        py_obj_base_t *o = obj;
+        switch (o->kind) {
+            case O_STR:
+                // pointer to the string (it's probably constant though!)
+                return (machine_uint_t)qstr_str(o->u_str);
+
+            case O_FLOAT:
+                // convert float to int (could also pass in float registers)
+                return (machine_int_t)o->u_flt;
+
+            case O_LIST:
+                // pointer to start of list (could pass length, but then could use len(x) for that)
+                return (machine_uint_t)o->u_list.items;
+
+            default:
+                // just pass along a pointer to the object
+                return (machine_uint_t)obj;
+        }
+    }
+}
+
+// convert a return value from inline asm to a sensible Python object
+py_obj_t rt_convert_val_from_inline_asm(machine_uint_t val) {
+    return TO_SMALL_INT(val);
+}
+
+typedef machine_uint_t (*inline_asm_fun_0_t)();
+typedef machine_uint_t (*inline_asm_fun_1_t)(machine_uint_t);
+typedef machine_uint_t (*inline_asm_fun_2_t)(machine_uint_t, machine_uint_t);
+
 py_obj_t rt_call_function_0(py_obj_t fun) {
     if (IS_O(fun, O_FUN_0)) {
         py_obj_base_t *o = fun;
-        DEBUG_OP_printf("calling native %p...\n", o->u_fun.fun);
+        DEBUG_OP_printf("calling native %p with no args\n", o->u_fun.fun);
         return ((py_fun_0_t)o->u_fun.fun)();
     } else if (IS_O(fun, O_FUN_BC)) {
         py_obj_base_t *o = fun;
         assert(o->u_fun_bc.n_args == 0);
-        DEBUG_OP_printf("calling byte code %p...\n", o->u_fun_bc.code);
+        DEBUG_OP_printf("calling byte code %p with no args\n", o->u_fun_bc.code);
         return py_execute_byte_code(o->u_fun_bc.code, o->u_fun_bc.len, NULL, 0);
+    } else if (IS_O(fun, O_FUN_ASM)) {
+        py_obj_base_t *o = fun;
+        assert(o->u_fun_asm.n_args == 0);
+        DEBUG_OP_printf("calling inline asm %p with no args\n", o->u_fun_asm.fun);
+        return rt_convert_val_from_inline_asm(((inline_asm_fun_0_t)o->u_fun_asm.fun)());
     } else {
         printf("fun0:%p\n", fun);
         assert(0);
@@ -776,13 +823,7 @@ py_obj_t rt_call_function_1(py_obj_t fun, py_obj_t arg) {
         py_obj_base_t *o = fun;
         assert(o->u_fun_asm.n_args == 1);
         DEBUG_OP_printf("calling inline asm %p with 1 arg\n", o->u_fun_asm.fun);
-        machine_int_t arg_val;
-        if (IS_SMALL_INT(arg)) {
-            arg_val = FROM_SMALL_INT(arg);
-        } else {
-            arg_val = arg;
-        }
-        return ((py_fun_1_t)o->u_fun_asm.fun)(arg_val);
+        return rt_convert_val_from_inline_asm(((inline_asm_fun_1_t)o->u_fun_asm.fun)(rt_convert_obj_for_inline_asm(arg)));
     } else if (IS_O(fun, O_BOUND_METH)) {
         py_obj_base_t *o = fun;
         return rt_call_function_2(o->u_bound_meth.meth, o->u_bound_meth.self, arg);
@@ -796,16 +837,21 @@ py_obj_t rt_call_function_1(py_obj_t fun, py_obj_t arg) {
 py_obj_t rt_call_function_2(py_obj_t fun, py_obj_t arg1, py_obj_t arg2) {
     if (IS_O(fun, O_FUN_2)) {
         py_obj_base_t *o = fun;
-        DEBUG_OP_printf("calling native %p...\n", o->u_fun.fun);
+        DEBUG_OP_printf("calling native %p with 2 args\n", o->u_fun.fun);
         return ((py_fun_2_t)o->u_fun.fun)(arg1, arg2);
     } else if (IS_O(fun, O_FUN_BC)) {
         py_obj_base_t *o = fun;
         assert(o->u_fun_bc.n_args == 2);
-        DEBUG_OP_printf("calling byte code %p...\n", o->u_fun_bc.code);
+        DEBUG_OP_printf("calling byte code %p with 2 args\n", o->u_fun_bc.code);
         py_obj_t args[2];
         args[0] = arg1;
         args[1] = arg2;
         return py_execute_byte_code(o->u_fun_bc.code, o->u_fun_bc.len, &args[0], 2);
+    } else if (IS_O(fun, O_FUN_ASM)) {
+        py_obj_base_t *o = fun;
+        assert(o->u_fun_asm.n_args == 2);
+        DEBUG_OP_printf("calling inline asm %p with 2 args\n", o->u_fun_asm.fun);
+        return rt_convert_val_from_inline_asm(((inline_asm_fun_2_t)o->u_fun_asm.fun)(rt_convert_obj_for_inline_asm(arg1), rt_convert_obj_for_inline_asm(arg2)));
     } else {
         assert(0);
         return py_const_none;