diff --git a/py/compile.c b/py/compile.c
index 8a30518b33a2b1a76e52572534a1bb14878b980a..7f9689102784642f7a6946fa5f9761f317b8fda1 100644
--- a/py/compile.c
+++ b/py/compile.c
@@ -2936,7 +2936,24 @@ STATIC void compile_scope_inline_asm(compiler_t *comp, scope_t *scope, pass_kind
         }
     }
 
-    assert(MP_PARSE_NODE_IS_NULL(pns->nodes[2])); // type
+    // pns->nodes[2] is function return annotation
+    mp_uint_t type_sig = MP_NATIVE_TYPE_INT;
+    mp_parse_node_t pn_annotation = pns->nodes[2];
+    if (!MP_PARSE_NODE_IS_NULL(pn_annotation)) {
+        // nodes[2] can be null or a test-expr
+        if (MP_PARSE_NODE_IS_ID(pn_annotation)) {
+            qstr ret_type = MP_PARSE_NODE_LEAF_ARG(pn_annotation);
+            switch (ret_type) {
+                case MP_QSTR_object: type_sig = MP_NATIVE_TYPE_OBJ; break;
+                case MP_QSTR_bool: type_sig = MP_NATIVE_TYPE_BOOL; break;
+                case MP_QSTR_int: type_sig = MP_NATIVE_TYPE_INT; break;
+                case MP_QSTR_uint: type_sig = MP_NATIVE_TYPE_UINT; break;
+                default: compile_syntax_error(comp, pn_annotation, "unknown type"); return;
+            }
+        } else {
+            compile_syntax_error(comp, pn_annotation, "return annotation must be an identifier");
+        }
+    }
 
     mp_parse_node_t pn_body = pns->nodes[3]; // body
     mp_parse_node_t *nodes;
@@ -3028,7 +3045,7 @@ STATIC void compile_scope_inline_asm(compiler_t *comp, scope_t *scope, pass_kind
     }
 
     if (comp->pass > MP_PASS_SCOPE) {
-        EMIT_INLINE_ASM(end_pass);
+        EMIT_INLINE_ASM_ARG(end_pass, type_sig);
     }
 
     if (comp->compile_error != MP_OBJ_NULL) {
diff --git a/py/emit.h b/py/emit.h
index 9d09ee2eface24573c476fe05b310d3122441dcd..7e8e03393dc8178ff6f30a520c8a0289ce741290 100644
--- a/py/emit.h
+++ b/py/emit.h
@@ -268,7 +268,7 @@ typedef struct _emit_inline_asm_t emit_inline_asm_t;
 
 typedef struct _emit_inline_asm_method_table_t {
     void (*start_pass)(emit_inline_asm_t *emit, pass_kind_t pass, scope_t *scope, mp_obj_t *error_slot);
-    void (*end_pass)(emit_inline_asm_t *emit);
+    void (*end_pass)(emit_inline_asm_t *emit, mp_uint_t type_sig);
     mp_uint_t (*count_params)(emit_inline_asm_t *emit, mp_uint_t n_params, mp_parse_node_t *pn_params);
     bool (*label)(emit_inline_asm_t *emit, mp_uint_t label_num, qstr label_id);
     void (*align)(emit_inline_asm_t *emit, mp_uint_t align);
diff --git a/py/emitglue.c b/py/emitglue.c
index 949a66ba9f5f1cbd7a6028e5e563c9999d65a953..4157593ba316b2119d1a03450e43516ff5a555ce 100644
--- a/py/emitglue.c
+++ b/py/emitglue.c
@@ -162,7 +162,7 @@ mp_obj_t mp_make_function_from_raw_code(mp_raw_code_t *rc, mp_obj_t def_args, mp
         #endif
         #if MICROPY_EMIT_INLINE_THUMB
         case MP_CODE_NATIVE_ASM:
-            fun = mp_obj_new_fun_asm(rc->n_pos_args, rc->data.u_native.fun_data);
+            fun = mp_obj_new_fun_asm(rc->n_pos_args, rc->data.u_native.fun_data, rc->data.u_native.type_sig);
             break;
         #endif
         default:
diff --git a/py/emitinlinethumb.c b/py/emitinlinethumb.c
index 317febe92333af68487c2288e4ec82d9da7d9cf1..c31a931c259e3fa23a898939e53606bf690945da 100644
--- a/py/emitinlinethumb.c
+++ b/py/emitinlinethumb.c
@@ -84,13 +84,14 @@ STATIC void emit_inline_thumb_start_pass(emit_inline_asm_t *emit, pass_kind_t pa
     asm_thumb_entry(emit->as, 0);
 }
 
-STATIC void emit_inline_thumb_end_pass(emit_inline_asm_t *emit) {
+STATIC void emit_inline_thumb_end_pass(emit_inline_asm_t *emit, mp_uint_t type_sig) {
     asm_thumb_exit(emit->as);
     asm_thumb_end_pass(emit->as);
 
     if (emit->pass == MP_PASS_EMIT) {
         void *f = asm_thumb_get_code(emit->as);
-        mp_emit_glue_assign_native(emit->scope->raw_code, MP_CODE_NATIVE_ASM, f, asm_thumb_get_code_size(emit->as), NULL, emit->scope->num_pos_args, 0, 0);
+        mp_emit_glue_assign_native(emit->scope->raw_code, MP_CODE_NATIVE_ASM, f,
+            asm_thumb_get_code_size(emit->as), NULL, emit->scope->num_pos_args, 0, type_sig);
     }
 }
 
diff --git a/py/nativeglue.c b/py/nativeglue.c
index 3220996f9557bcd8ce03730ee79858d92ed29684..cc0d61ce96cb1cb530a3b43ea20a1301d281b57a 100644
--- a/py/nativeglue.c
+++ b/py/nativeglue.c
@@ -34,14 +34,14 @@
 #include "py/emitglue.h"
 #include "py/bc.h"
 
-#if MICROPY_EMIT_NATIVE
-
 #if 0 // print debugging info
 #define DEBUG_printf DEBUG_printf
 #else // don't print debugging info
 #define DEBUG_printf(...) (void)0
 #endif
 
+#if MICROPY_EMIT_NATIVE
+
 // convert a Micro Python object to a valid native value based on type
 mp_uint_t mp_convert_obj_to_native(mp_obj_t obj, mp_uint_t type) {
     DEBUG_printf("mp_convert_obj_to_native(%p, " UINT_FMT ")\n", obj, type);
@@ -61,6 +61,10 @@ mp_uint_t mp_convert_obj_to_native(mp_obj_t obj, mp_uint_t type) {
     }
 }
 
+#endif
+
+#if MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_THUMB
+
 // convert a native value to a Micro Python object based on type
 mp_obj_t mp_convert_native_to_obj(mp_uint_t val, mp_uint_t type) {
     DEBUG_printf("mp_convert_native_to_obj(" UINT_FMT ", " UINT_FMT ")\n", val, type);
@@ -73,6 +77,10 @@ mp_obj_t mp_convert_native_to_obj(mp_uint_t val, mp_uint_t type) {
     }
 }
 
+#endif
+
+#if MICROPY_EMIT_NATIVE
+
 // wrapper that accepts n_args and n_kw in one argument
 // (native emitter can only pass at most 3 arguments to a function)
 mp_obj_t mp_native_call_function_n_kw(mp_obj_t fun_in, mp_uint_t n_args_kw, const mp_obj_t *args) {
diff --git a/py/obj.h b/py/obj.h
index 14c3a8120015ed7d5d6dd4bcb1adf67afeda292a..bfdceeb4e89bfd4abb4209c0e68aebf7d62f45ce 100644
--- a/py/obj.h
+++ b/py/obj.h
@@ -625,7 +625,7 @@ mp_obj_t mp_obj_new_exception_msg_varg(const mp_obj_type_t *exc_type, const char
 mp_obj_t mp_obj_new_fun_bc(mp_obj_t def_args, mp_obj_t def_kw_args, const byte *code, const mp_uint_t *const_table);
 mp_obj_t mp_obj_new_fun_native(mp_obj_t def_args_in, mp_obj_t def_kw_args, const void *fun_data, const mp_uint_t *const_table);
 mp_obj_t mp_obj_new_fun_viper(mp_uint_t n_args, void *fun_data, mp_uint_t type_sig);
-mp_obj_t mp_obj_new_fun_asm(mp_uint_t n_args, void *fun_data);
+mp_obj_t mp_obj_new_fun_asm(mp_uint_t n_args, void *fun_data, mp_uint_t type_sig);
 mp_obj_t mp_obj_new_gen_wrap(mp_obj_t fun);
 mp_obj_t mp_obj_new_closure(mp_obj_t fun, mp_uint_t n_closed, const mp_obj_t *closed);
 mp_obj_t mp_obj_new_tuple(mp_uint_t n, const mp_obj_t *items);
diff --git a/py/objfun.c b/py/objfun.c
index f2cc34fad686876b1f854073c0c0a568d3f0bddd..df7d162133361d75f027f795b124ff87618a45a5 100644
--- a/py/objfun.c
+++ b/py/objfun.c
@@ -452,6 +452,7 @@ typedef struct _mp_obj_fun_asm_t {
     mp_obj_base_t base;
     mp_uint_t n_args;
     void *fun_data; // GC must be able to trace this pointer
+    mp_uint_t type_sig;
 } mp_obj_fun_asm_t;
 
 typedef mp_uint_t (*inline_asm_fun_0_t)(void);
@@ -509,11 +510,6 @@ STATIC mp_uint_t convert_obj_for_inline_asm(mp_obj_t obj) {
     }
 }
 
-// convert a return value from inline asm to a sensible Micro Python object
-STATIC mp_obj_t convert_val_from_inline_asm(mp_uint_t val) {
-    return MP_OBJ_NEW_SMALL_INT(val);
-}
-
 STATIC mp_obj_t fun_asm_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
     mp_obj_fun_asm_t *self = self_in;
 
@@ -535,7 +531,7 @@ STATIC mp_obj_t fun_asm_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const
         ret = 0;
     }
 
-    return convert_val_from_inline_asm(ret);
+    return mp_convert_native_to_obj(ret, self->type_sig);
 }
 
 STATIC const mp_obj_type_t mp_type_fun_asm = {
@@ -545,11 +541,12 @@ STATIC const mp_obj_type_t mp_type_fun_asm = {
     .unary_op = mp_generic_unary_op,
 };
 
-mp_obj_t mp_obj_new_fun_asm(mp_uint_t n_args, void *fun_data) {
+mp_obj_t mp_obj_new_fun_asm(mp_uint_t n_args, void *fun_data, mp_uint_t type_sig) {
     mp_obj_fun_asm_t *o = m_new_obj(mp_obj_fun_asm_t);
     o->base.type = &mp_type_fun_asm;
     o->n_args = n_args;
     o->fun_data = fun_data;
+    o->type_sig = type_sig;
     return o;
 }
 
diff --git a/py/qstrdefs.h b/py/qstrdefs.h
index 122fca128bbc0e861a9ebe8c1dc479ae48444a51..bf774e036b6bd189d70a0069af5caff42fd1d011 100644
--- a/py/qstrdefs.h
+++ b/py/qstrdefs.h
@@ -113,6 +113,7 @@ Q(asm_thumb)
 Q(label)
 Q(align)
 Q(data)
+Q(uint)
 #endif
 
 Q(builtins)
diff --git a/tests/inlineasm/asmrettype.py b/tests/inlineasm/asmrettype.py
new file mode 100644
index 0000000000000000000000000000000000000000..f1918696eeaeb88c9db0274d049a586ee23f01cb
--- /dev/null
+++ b/tests/inlineasm/asmrettype.py
@@ -0,0 +1,21 @@
+# test return type of inline asm
+
+@micropython.asm_thumb
+def ret_obj(r0) -> object:
+    pass
+ret_obj(print)(1)
+
+@micropython.asm_thumb
+def ret_bool(r0) -> bool:
+    pass
+print(ret_bool(0), ret_bool(1))
+
+@micropython.asm_thumb
+def ret_int(r0) -> int:
+    lsl(r0, r0, 29)
+print(ret_int(0), hex(ret_int(1)), hex(ret_int(2)), hex(ret_int(4)))
+
+@micropython.asm_thumb
+def ret_uint(r0) -> uint:
+    lsl(r0, r0, 29)
+print(ret_uint(0), hex(ret_uint(1)), hex(ret_uint(2)), hex(ret_uint(4)))
diff --git a/tests/inlineasm/asmrettype.py.exp b/tests/inlineasm/asmrettype.py.exp
new file mode 100644
index 0000000000000000000000000000000000000000..cbb49d24724ecdbff0797998eb8e9d3f5efd7c96
--- /dev/null
+++ b/tests/inlineasm/asmrettype.py.exp
@@ -0,0 +1,4 @@
+1
+False True
+0 0x20000000 0x40000000 -0x80000000
+0 0x20000000 0x40000000 0x80000000