diff --git a/py/compile.c b/py/compile.c
index 4d07f2e74b2f66b349e27703cf025cd8874155b6..b3a83715e059d0df1b350f9a0ecd72d125fb4b63 100644
--- a/py/compile.c
+++ b/py/compile.c
@@ -13,6 +13,7 @@
 #include "scope.h"
 #include "runtime0.h"
 #include "emit.h"
+#include "emitglue.h"
 #include "obj.h"
 #include "compile.h"
 #include "runtime.h"
@@ -213,7 +214,7 @@ STATIC void compile_decrease_except_level(compiler_t *comp) {
 }
 
 STATIC scope_t *scope_new_and_link(compiler_t *comp, scope_kind_t kind, mp_parse_node_t pn, uint emit_options) {
-    scope_t *scope = scope_new(kind, pn, comp->source_file, rt_get_unique_code_id(), emit_options);
+    scope_t *scope = scope_new(kind, pn, comp->source_file, mp_emit_glue_get_unique_code_id(), emit_options);
     scope->parent = comp->scope_cur;
     scope->next = NULL;
     if (comp->scope_head == NULL) {
@@ -1149,14 +1150,14 @@ void compile_del_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
 
 void compile_break_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
     if (comp->break_label == 0) {
-        printf("ERROR: cannot break from here\n");
+        compile_syntax_error(comp, "'break' outside loop");
     }
     EMIT_ARG(break_loop, comp->break_label, comp->cur_except_level - comp->break_continue_except_level);
 }
 
 void compile_continue_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
     if (comp->continue_label == 0) {
-        printf("ERROR: cannot continue from here\n");
+        compile_syntax_error(comp, "'continue' outside loop");
     }
     EMIT_ARG(continue_loop, comp->continue_label, comp->cur_except_level - comp->break_continue_except_level);
 }
diff --git a/py/emit.h b/py/emit.h
index ce0c98ba78cc9901199838d16bff7680d87d854c..26149983b089416c296a1c6a3a8c0fdafb076ec4 100644
--- a/py/emit.h
+++ b/py/emit.h
@@ -79,7 +79,7 @@ typedef struct _emit_method_table_t {
     void (*setup_except)(emit_t *emit, int label);
     void (*setup_finally)(emit_t *emit, int label);
     void (*end_finally)(emit_t *emit);
-    void (*get_iter)(emit_t *emit); // tos = getiter(tos)
+    void (*get_iter)(emit_t *emit);
     void (*for_iter)(emit_t *emit, int label);
     void (*for_iter_end)(emit_t *emit);
     void (*pop_block)(emit_t *emit);
diff --git a/py/emitbc.c b/py/emitbc.c
index 1516b41e5bea7579e304754e04bad223cccd4e28..ac3fc800958de14787c72badbf1af712f39b46b3 100644
--- a/py/emitbc.c
+++ b/py/emitbc.c
@@ -12,6 +12,7 @@
 #include "scope.h"
 #include "runtime0.h"
 #include "emit.h"
+#include "emitglue.h"
 #include "bc0.h"
 
 struct _emit_t {
@@ -271,9 +272,9 @@ STATIC void emit_bc_end_pass(emit_t *emit) {
         for (int i = 0; i < emit->scope->num_params; i++) {
             arg_names[i] = emit->scope->id_info[i].qstr;
         }
-        rt_assign_byte_code(emit->scope->unique_code_id, emit->code_base,
+        mp_emit_glue_assign_byte_code(emit->scope->unique_code_id, emit->code_base,
             emit->code_info_size + emit->byte_code_size,
-            emit->scope->num_params, emit->scope->num_locals, emit->scope->stack_size,
+            emit->scope->num_params, emit->scope->num_locals,
             emit->scope->scope_flags, arg_names);
     }
 }
diff --git a/py/emitglue.c b/py/emitglue.c
new file mode 100644
index 0000000000000000000000000000000000000000..0ab9090924ac8a616aba58bd27e6cb0c1ff6aa2d
--- /dev/null
+++ b/py/emitglue.c
@@ -0,0 +1,206 @@
+// This code glues the code emitters to the runtime.
+
+#include <stdlib.h>
+#include <assert.h>
+
+#include "misc.h"
+#include "mpconfig.h"
+#include "qstr.h"
+#include "obj.h"
+#include "runtime0.h"
+#include "runtime.h"
+#include "emitglue.h"
+
+#if 0 // print debugging info
+#define DEBUG_PRINT (1)
+#define WRITE_CODE (1)
+#define DEBUG_printf DEBUG_printf
+#define DEBUG_OP_printf(...) DEBUG_printf(__VA_ARGS__)
+#else // don't print debugging info
+#define DEBUG_printf(...) (void)0
+#define DEBUG_OP_printf(...) (void)0
+#endif
+
+typedef enum {
+    MP_CODE_NONE,
+    MP_CODE_BYTE,
+    MP_CODE_NATIVE,
+    MP_CODE_INLINE_ASM,
+} mp_code_kind_t;
+
+typedef struct _mp_code_t {
+    mp_code_kind_t kind : 8;
+    uint scope_flags : 8;
+    uint n_args : 16;
+    union {
+        struct {
+            byte *code;
+            uint len;
+        } u_byte;
+        struct {
+            mp_fun_t fun;
+        } u_native;
+        struct {
+            void *fun;
+        } u_inline_asm;
+    };
+    qstr *arg_names;
+} mp_code_t;
+
+STATIC machine_uint_t unique_codes_alloc = 0;
+STATIC mp_code_t *unique_codes = NULL;
+STATIC uint next_unique_code_id;
+
+void mp_emit_glue_init(void) {
+    next_unique_code_id = 0;
+    unique_codes_alloc = 0;
+    unique_codes = NULL;
+}
+
+void mp_emit_glue_deinit(void) {
+    m_del(mp_code_t, unique_codes, unique_codes_alloc);
+}
+
+uint mp_emit_glue_get_unique_code_id(void) {
+    return next_unique_code_id++;
+}
+
+STATIC void mp_emit_glue_alloc_unique_codes(void) {
+    if (next_unique_code_id > unique_codes_alloc) {
+        DEBUG_printf("allocate more unique codes: " UINT_FMT " -> %u\n", unique_codes_alloc, next_unique_code_id);
+        // increase size of unique_codes table
+        unique_codes = m_renew(mp_code_t, unique_codes, unique_codes_alloc, next_unique_code_id);
+        for (uint i = unique_codes_alloc; i < next_unique_code_id; i++) {
+            unique_codes[i].kind = MP_CODE_NONE;
+        }
+        unique_codes_alloc = next_unique_code_id;
+    }
+}
+
+void mp_emit_glue_assign_byte_code(uint unique_code_id, byte *code, uint len, int n_args, int n_locals, uint scope_flags, qstr *arg_names) {
+    mp_emit_glue_alloc_unique_codes();
+
+    assert(unique_code_id < next_unique_code_id && unique_codes[unique_code_id].kind == MP_CODE_NONE);
+    unique_codes[unique_code_id].kind = MP_CODE_BYTE;
+    unique_codes[unique_code_id].scope_flags = scope_flags;
+    unique_codes[unique_code_id].n_args = n_args;
+    unique_codes[unique_code_id].u_byte.code = code;
+    unique_codes[unique_code_id].u_byte.len = len;
+    unique_codes[unique_code_id].arg_names = arg_names;
+
+    //printf("byte code: %d bytes\n", len);
+
+#ifdef DEBUG_PRINT
+    DEBUG_printf("assign byte code: id=%d code=%p len=%u n_args=%d n_locals=%d\n", unique_code_id, code, len, n_args, n_locals);
+    for (int i = 0; i < 128 && i < len; i++) {
+        if (i > 0 && i % 16 == 0) {
+            DEBUG_printf("\n");
+        }
+        DEBUG_printf(" %02x", code[i]);
+    }
+    DEBUG_printf("\n");
+#if MICROPY_DEBUG_PRINTERS
+    mp_byte_code_print(code, len);
+#endif
+#endif
+}
+
+void mp_emit_glue_assign_native_code(uint unique_code_id, void *fun, uint len, int n_args) {
+    mp_emit_glue_alloc_unique_codes();
+
+    assert(unique_code_id < next_unique_code_id && unique_codes[unique_code_id].kind == MP_CODE_NONE);
+    unique_codes[unique_code_id].kind = MP_CODE_NATIVE;
+    unique_codes[unique_code_id].scope_flags = 0;
+    unique_codes[unique_code_id].n_args = n_args;
+    unique_codes[unique_code_id].u_native.fun = fun;
+
+    //printf("native code: %d bytes\n", len);
+
+#ifdef DEBUG_PRINT
+    DEBUG_printf("assign native code: id=%d fun=%p len=%u n_args=%d\n", unique_code_id, fun, len, n_args);
+    byte *fun_data = (byte*)(((machine_uint_t)fun) & (~1)); // need to clear lower bit in case it's thumb code
+    for (int i = 0; i < 128 && i < len; i++) {
+        if (i > 0 && i % 16 == 0) {
+            DEBUG_printf("\n");
+        }
+        DEBUG_printf(" %02x", fun_data[i]);
+    }
+    DEBUG_printf("\n");
+
+#ifdef WRITE_CODE
+    if (fp_write_code != NULL) {
+        fwrite(fun_data, len, 1, fp_write_code);
+        fflush(fp_write_code);
+    }
+#endif
+#endif
+}
+
+void mp_emit_glue_assign_inline_asm_code(uint unique_code_id, void *fun, uint len, int n_args) {
+    mp_emit_glue_alloc_unique_codes();
+
+    assert(unique_code_id < next_unique_code_id && unique_codes[unique_code_id].kind == MP_CODE_NONE);
+    unique_codes[unique_code_id].kind = MP_CODE_INLINE_ASM;
+    unique_codes[unique_code_id].scope_flags = 0;
+    unique_codes[unique_code_id].n_args = n_args;
+    unique_codes[unique_code_id].u_inline_asm.fun = fun;
+
+#ifdef DEBUG_PRINT
+    DEBUG_printf("assign inline asm code: id=%d fun=%p len=%u n_args=%d\n", unique_code_id, fun, len, n_args);
+    byte *fun_data = (byte*)(((machine_uint_t)fun) & (~1)); // need to clear lower bit in case it's thumb code
+    for (int i = 0; i < 128 && i < len; i++) {
+        if (i > 0 && i % 16 == 0) {
+            DEBUG_printf("\n");
+        }
+        DEBUG_printf(" %02x", fun_data[i]);
+    }
+    DEBUG_printf("\n");
+
+#ifdef WRITE_CODE
+    if (fp_write_code != NULL) {
+        fwrite(fun_data, len, 1, fp_write_code);
+    }
+#endif
+#endif
+}
+
+mp_obj_t rt_make_function_from_id(int unique_code_id, mp_obj_t def_args) {
+    DEBUG_OP_printf("make_function_from_id %d\n", unique_code_id);
+    if (unique_code_id >= next_unique_code_id) {
+        // illegal code id
+        return mp_const_none;
+    }
+
+    // make the function, depending on the code kind
+    mp_code_t *c = &unique_codes[unique_code_id];
+    mp_obj_t fun;
+    switch (c->kind) {
+        case MP_CODE_BYTE:
+            fun = mp_obj_new_fun_bc(c->scope_flags, c->arg_names, c->n_args, def_args, c->u_byte.code);
+            break;
+        case MP_CODE_NATIVE:
+            fun = rt_make_function_n(c->n_args, c->u_native.fun);
+            break;
+        case MP_CODE_INLINE_ASM:
+            fun = mp_obj_new_fun_asm(c->n_args, c->u_inline_asm.fun);
+            break;
+        default:
+            assert(0);
+            fun = mp_const_none;
+    }
+
+    // check for generator functions and if so wrap in generator object
+    if ((c->scope_flags & MP_SCOPE_FLAG_GENERATOR) != 0) {
+        fun = mp_obj_new_gen_wrap(fun);
+    }
+
+    return fun;
+}
+
+mp_obj_t rt_make_closure_from_id(int unique_code_id, mp_obj_t closure_tuple, mp_obj_t def_args) {
+    DEBUG_OP_printf("make_closure_from_id %d\n", unique_code_id);
+    // make function object
+    mp_obj_t ffun = rt_make_function_from_id(unique_code_id, def_args);
+    // wrap function in closure object
+    return mp_obj_new_closure(ffun, closure_tuple);
+}
diff --git a/py/emitglue.h b/py/emitglue.h
new file mode 100644
index 0000000000000000000000000000000000000000..e06578e2205ea063e055ee6a11bf05636e450d93
--- /dev/null
+++ b/py/emitglue.h
@@ -0,0 +1,9 @@
+// These variables and functions glue the code emitters to the runtime.
+
+void mp_emit_glue_init(void);
+void mp_emit_glue_deinit(void);
+uint mp_emit_glue_get_unique_code_id(void);
+uint mp_emit_glue_get_unique_code(uint unique_code_id);
+void mp_emit_glue_assign_byte_code(uint unique_code_id, byte *code, uint len, int n_args, int n_locals, uint scope_flags, qstr *arg_names);
+void mp_emit_glue_assign_native_code(uint unique_code_id, void *f, uint len, int n_args);
+void mp_emit_glue_assign_inline_asm_code(uint unique_code_id, void *f, uint len, int n_args);
diff --git a/py/emitinlinethumb.c b/py/emitinlinethumb.c
index 55af97f83520c623bddd55f1baec48a1d3696027..9ea2cc24bc1b46038086b763877be666b00553cd 100644
--- a/py/emitinlinethumb.c
+++ b/py/emitinlinethumb.c
@@ -11,6 +11,7 @@
 #include "scope.h"
 #include "runtime0.h"
 #include "emit.h"
+#include "emitglue.h"
 #include "asmthumb.h"
 
 #if MICROPY_EMIT_INLINE_THUMB
@@ -51,7 +52,7 @@ STATIC void emit_inline_thumb_end_pass(emit_inline_asm_t *emit) {
 
     if (emit->pass == PASS_3) {
         void *f = asm_thumb_get_code(emit->as);
-        rt_assign_inline_asm_code(emit->scope->unique_code_id, f, asm_thumb_get_code_size(emit->as), emit->scope->num_params);
+        mp_emit_glue_assign_inline_asm_code(emit->scope->unique_code_id, f, asm_thumb_get_code_size(emit->as), emit->scope->num_params);
     }
 }
 
diff --git a/py/emitnative.c b/py/emitnative.c
index 75086de155fcbaed1403f87bce97391303d79184..743c3211191bc7dbe9a840c7780bda909ee1123a 100644
--- a/py/emitnative.c
+++ b/py/emitnative.c
@@ -30,6 +30,7 @@
 #include "scope.h"
 #include "runtime0.h"
 #include "emit.h"
+#include "emitglue.h"
 #include "obj.h"
 #include "runtime.h"
 
@@ -275,10 +276,10 @@ STATIC void emit_native_end_pass(emit_t *emit) {
     if (emit->pass == PASS_3) {
 #if N_X64
         void *f = asm_x64_get_code(emit->as);
-        rt_assign_native_code(emit->scope->unique_code_id, f, asm_x64_get_code_size(emit->as), emit->scope->num_params);
+        mp_emit_glue_assign_native_code(emit->scope->unique_code_id, f, asm_x64_get_code_size(emit->as), emit->scope->num_params);
 #elif N_THUMB
         void *f = asm_thumb_get_code(emit->as);
-        rt_assign_native_code(emit->scope->unique_code_id, f, asm_thumb_get_code_size(emit->as), emit->scope->num_params);
+        mp_emit_glue_assign_native_code(emit->scope->unique_code_id, f, asm_thumb_get_code_size(emit->as), emit->scope->num_params);
 #endif
     }
 }
diff --git a/py/py.mk b/py/py.mk
index 2247b6babbd6cb50d277fae00e2863392ae71400..3905aa77df7a464e4346fed3fcdf7fbffcdf1c32 100644
--- a/py/py.mk
+++ b/py/py.mk
@@ -35,6 +35,7 @@ PY_O_BASENAME = \
 	formatfloat.o \
 	parsenumbase.o \
 	parsenum.o \
+	emitglue.o \
 	runtime.o \
 	map.o \
 	obj.o \
diff --git a/py/runtime.c b/py/runtime.c
index 60e975687dc5dcb0ebb8902b6e605e6a170beb78..762924c20a1ae021afdc8c8da295916dfe4541ed 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -1,11 +1,6 @@
-// in principle, rt_xxx functions are called only by vm/native/viper and make assumptions about args
-// mp_xxx functions are safer and can be called by anyone
-// note that rt_assign_xxx are called only from emit*, and maybe we can rename them to reflect this
-
 #include <stdio.h>
 #include <string.h>
 #include <assert.h>
-#include <math.h>
 
 #include "nlr.h"
 #include "misc.h"
@@ -16,6 +11,7 @@
 #include "parsenum.h"
 #include "runtime0.h"
 #include "runtime.h"
+#include "emitglue.h"
 #include "map.h"
 #include "builtin.h"
 #include "builtintables.h"
@@ -37,36 +33,6 @@ STATIC mp_map_t *map_locals;
 STATIC mp_map_t *map_globals;
 STATIC mp_map_t map_builtins;
 
-typedef enum {
-    MP_CODE_NONE,
-    MP_CODE_BYTE,
-    MP_CODE_NATIVE,
-    MP_CODE_INLINE_ASM,
-} mp_code_kind_t;
-
-typedef struct _mp_code_t {
-    mp_code_kind_t kind : 8;
-    uint scope_flags : 8;
-    uint n_args : 16;
-    union {
-        struct {
-            byte *code;
-            uint len;
-        } u_byte;
-        struct {
-            mp_fun_t fun;
-        } u_native;
-        struct {
-            void *fun;
-        } u_inline_asm;
-    };
-    qstr *arg_names;
-} mp_code_t;
-
-STATIC uint next_unique_code_id;
-STATIC machine_uint_t unique_codes_alloc = 0;
-STATIC mp_code_t *unique_codes = NULL;
-
 #ifdef WRITE_CODE
 FILE *fp_write_code = NULL;
 #endif
@@ -77,6 +43,8 @@ STATIC void mp_map_add_qstr(mp_map_t *map, qstr qstr, mp_obj_t value) {
 }
 
 void rt_init(void) {
+    mp_emit_glue_init();
+
     // locals = globals for outer module (see Objects/frameobject.c/PyFrame_New())
     map_locals = map_globals = mp_map_new(1);
 
@@ -101,129 +69,21 @@ void rt_init(void) {
     //sys_path = mp_obj_new_list(0, NULL);
     //rt_store_attr(m_sys, MP_QSTR_path, sys_path);
 
-    // TODO: wastes one mp_code_t structure in mem
-    next_unique_code_id = 1; // 0 indicates "no code"
-    unique_codes_alloc = 0;
-    unique_codes = NULL;
-
 #ifdef WRITE_CODE
     fp_write_code = fopen("out-code", "wb");
 #endif
 }
 
 void rt_deinit(void) {
-    m_del(mp_code_t, unique_codes, unique_codes_alloc);
-    mp_map_free(map_globals);
-    mp_map_deinit(&map_builtins);
-    mp_module_deinit();
 #ifdef WRITE_CODE
     if (fp_write_code != NULL) {
         fclose(fp_write_code);
     }
 #endif
-}
-
-uint rt_get_unique_code_id(void) {
-    return next_unique_code_id++;
-}
-
-STATIC void alloc_unique_codes(void) {
-    if (next_unique_code_id > unique_codes_alloc) {
-        DEBUG_printf("allocate more unique codes: " UINT_FMT " -> %u\n", unique_codes_alloc, next_unique_code_id);
-        // increase size of unique_codes table
-        unique_codes = m_renew(mp_code_t, unique_codes, unique_codes_alloc, next_unique_code_id);
-        for (uint i = unique_codes_alloc; i < next_unique_code_id; i++) {
-            unique_codes[i].kind = MP_CODE_NONE;
-        }
-        unique_codes_alloc = next_unique_code_id;
-    }
-}
-
-void rt_assign_byte_code(uint unique_code_id, byte *code, uint len, int n_args, int n_locals, int n_stack, uint scope_flags, qstr *arg_names) {
-    alloc_unique_codes();
-
-    assert(1 <= unique_code_id && unique_code_id < next_unique_code_id && unique_codes[unique_code_id].kind == MP_CODE_NONE);
-    unique_codes[unique_code_id].kind = MP_CODE_BYTE;
-    unique_codes[unique_code_id].scope_flags = scope_flags;
-    unique_codes[unique_code_id].n_args = n_args;
-    unique_codes[unique_code_id].u_byte.code = code;
-    unique_codes[unique_code_id].u_byte.len = len;
-    unique_codes[unique_code_id].arg_names = arg_names;
-
-    //printf("byte code: %d bytes\n", len);
-
-#ifdef DEBUG_PRINT
-    DEBUG_printf("assign byte code: id=%d code=%p len=%u n_args=%d n_locals=%d n_stack=%d\n", unique_code_id, code, len, n_args, n_locals, n_stack);
-    for (int i = 0; i < 128 && i < len; i++) {
-        if (i > 0 && i % 16 == 0) {
-            DEBUG_printf("\n");
-        }
-        DEBUG_printf(" %02x", code[i]);
-    }
-    DEBUG_printf("\n");
-#if MICROPY_DEBUG_PRINTERS
-    mp_byte_code_print(code, len);
-#endif
-#endif
-}
-
-void rt_assign_native_code(uint unique_code_id, void *fun, uint len, int n_args) {
-    alloc_unique_codes();
-
-    assert(1 <= unique_code_id && unique_code_id < next_unique_code_id && unique_codes[unique_code_id].kind == MP_CODE_NONE);
-    unique_codes[unique_code_id].kind = MP_CODE_NATIVE;
-    unique_codes[unique_code_id].scope_flags = 0;
-    unique_codes[unique_code_id].n_args = n_args;
-    unique_codes[unique_code_id].u_native.fun = fun;
-
-    //printf("native code: %d bytes\n", len);
-
-#ifdef DEBUG_PRINT
-    DEBUG_printf("assign native code: id=%d fun=%p len=%u n_args=%d\n", unique_code_id, fun, len, n_args);
-    byte *fun_data = (byte*)(((machine_uint_t)fun) & (~1)); // need to clear lower bit in case it's thumb code
-    for (int i = 0; i < 128 && i < len; i++) {
-        if (i > 0 && i % 16 == 0) {
-            DEBUG_printf("\n");
-        }
-        DEBUG_printf(" %02x", fun_data[i]);
-    }
-    DEBUG_printf("\n");
-
-#ifdef WRITE_CODE
-    if (fp_write_code != NULL) {
-        fwrite(fun_data, len, 1, fp_write_code);
-        fflush(fp_write_code);
-    }
-#endif
-#endif
-}
-
-void rt_assign_inline_asm_code(uint unique_code_id, void *fun, uint len, int n_args) {
-    alloc_unique_codes();
-
-    assert(1 <= unique_code_id && unique_code_id < next_unique_code_id && unique_codes[unique_code_id].kind == MP_CODE_NONE);
-    unique_codes[unique_code_id].kind = MP_CODE_INLINE_ASM;
-    unique_codes[unique_code_id].scope_flags = 0;
-    unique_codes[unique_code_id].n_args = n_args;
-    unique_codes[unique_code_id].u_inline_asm.fun = fun;
-
-#ifdef DEBUG_PRINT
-    DEBUG_printf("assign inline asm code: id=%d fun=%p len=%u n_args=%d\n", unique_code_id, fun, len, n_args);
-    byte *fun_data = (byte*)(((machine_uint_t)fun) & (~1)); // need to clear lower bit in case it's thumb code
-    for (int i = 0; i < 128 && i < len; i++) {
-        if (i > 0 && i % 16 == 0) {
-            DEBUG_printf("\n");
-        }
-        DEBUG_printf(" %02x", fun_data[i]);
-    }
-    DEBUG_printf("\n");
-
-#ifdef WRITE_CODE
-    if (fp_write_code != NULL) {
-        fwrite(fun_data, len, 1, fp_write_code);
-    }
-#endif
-#endif
+    mp_map_free(map_globals);
+    mp_map_deinit(&map_builtins);
+    mp_module_deinit();
+    mp_emit_glue_deinit();
 }
 
 int rt_is_true(mp_obj_t arg) {
@@ -646,47 +506,6 @@ generic_binary_op:
     return mp_const_none;
 }
 
-mp_obj_t rt_make_function_from_id(int unique_code_id, mp_obj_t def_args) {
-    DEBUG_OP_printf("make_function_from_id %d\n", unique_code_id);
-    if (unique_code_id < 1 || unique_code_id >= next_unique_code_id) {
-        // illegal code id
-        return mp_const_none;
-    }
-
-    // make the function, depending on the code kind
-    mp_code_t *c = &unique_codes[unique_code_id];
-    mp_obj_t fun;
-    switch (c->kind) {
-        case MP_CODE_BYTE:
-            fun = mp_obj_new_fun_bc(c->scope_flags, c->arg_names, c->n_args, def_args, c->u_byte.code);
-            break;
-        case MP_CODE_NATIVE:
-            fun = rt_make_function_n(c->n_args, c->u_native.fun);
-            break;
-        case MP_CODE_INLINE_ASM:
-            fun = mp_obj_new_fun_asm(c->n_args, c->u_inline_asm.fun);
-            break;
-        default:
-            assert(0);
-            fun = mp_const_none;
-    }
-
-    // check for generator functions and if so wrap in generator object
-    if ((c->scope_flags & MP_SCOPE_FLAG_GENERATOR) != 0) {
-        fun = mp_obj_new_gen_wrap(fun);
-    }
-
-    return fun;
-}
-
-mp_obj_t rt_make_closure_from_id(int unique_code_id, mp_obj_t closure_tuple, mp_obj_t def_args) {
-    DEBUG_OP_printf("make_closure_from_id %d\n", unique_code_id);
-    // make function object
-    mp_obj_t ffun = rt_make_function_from_id(unique_code_id, def_args);
-    // wrap function in closure object
-    return mp_obj_new_closure(ffun, closure_tuple);
-}
-
 mp_obj_t rt_call_function_0(mp_obj_t fun) {
     return rt_call_function_n_kw(fun, 0, 0, NULL);
 }
diff --git a/py/runtime.h b/py/runtime.h
index 553a83468944baecceaf0b066bb28f5f2e7d47ba..b27d12ff1dd0c819f1bbcf217eb79216545c9290 100644
--- a/py/runtime.h
+++ b/py/runtime.h
@@ -1,3 +1,6 @@
+void rt_init(void);
+void rt_deinit(void);
+
 void rt_check_nargs(int n_args, machine_uint_t n_args_min, machine_uint_t n_args_max, int n_kw, bool is_kw);
 
 int rt_is_true(mp_obj_t arg);
diff --git a/py/runtime0.h b/py/runtime0.h
index 07fcf0705e3e408222cb53330b2a83f12706bd6d..a650e704ae1feb8cfd7b01241517fdbb7fc4494f 100644
--- a/py/runtime0.h
+++ b/py/runtime0.h
@@ -93,10 +93,3 @@ typedef enum {
 } rt_fun_kind_t;
 
 extern void *const rt_fun_table[RT_F_NUMBER_OF];
-
-void rt_init(void);
-void rt_deinit(void);
-uint rt_get_unique_code_id(void);
-void rt_assign_byte_code(uint unique_code_id, byte *code, uint len, int n_args, int n_locals, int n_stack, uint scope_flags, qstr *arg_names);
-void rt_assign_native_code(uint unique_code_id, void *f, uint len, int n_args);
-void rt_assign_inline_asm_code(uint unique_code_id, void *f, uint len, int n_args);
diff --git a/unix-cpy/main.c b/unix-cpy/main.c
index 52d63273cee5942355d9f8231ea0e9e7dded91b5..7e2703139e74bbc17fbe5c7841e3decade0eea60 100644
--- a/unix-cpy/main.c
+++ b/unix-cpy/main.c
@@ -12,7 +12,7 @@
 #include "obj.h"
 #include "parsehelper.h"
 #include "compile.h"
-#include "runtime0.h"
+#include "runtime.h"
 
 void do_file(const char *file) {
     mp_lexer_t *lex = mp_lexer_new_from_file(file);