diff --git a/py/emitglue.c b/py/emitglue.c
index 0ab9090924ac8a616aba58bd27e6cb0c1ff6aa2d..ed816a51619f300e6d60fddc3137be1b1aba7764 100644
--- a/py/emitglue.c
+++ b/py/emitglue.c
@@ -1,6 +1,6 @@
 // This code glues the code emitters to the runtime.
 
-#include <stdlib.h>
+#include <stdio.h>
 #include <assert.h>
 
 #include "misc.h"
@@ -10,6 +10,7 @@
 #include "runtime0.h"
 #include "runtime.h"
 #include "emitglue.h"
+#include "bc.h"
 
 #if 0 // print debugging info
 #define DEBUG_PRINT (1)
@@ -51,13 +52,27 @@ STATIC machine_uint_t unique_codes_alloc = 0;
 STATIC mp_code_t *unique_codes = NULL;
 STATIC uint next_unique_code_id;
 
+#ifdef WRITE_CODE
+FILE *fp_write_code = NULL;
+#endif
+
 void mp_emit_glue_init(void) {
     next_unique_code_id = 0;
     unique_codes_alloc = 0;
     unique_codes = NULL;
+
+#ifdef WRITE_CODE
+    fp_write_code = fopen("out-code", "wb");
+#endif
 }
 
 void mp_emit_glue_deinit(void) {
+#ifdef WRITE_CODE
+    if (fp_write_code != NULL) {
+        fclose(fp_write_code);
+    }
+#endif
+
     m_del(mp_code_t, unique_codes, unique_codes_alloc);
 }
 
diff --git a/py/obj.h b/py/obj.h
index af38253c5677b254b7873228b8ef6f589a22b884..500ffbdc0426182c86585088807c1c922264762a 100644
--- a/py/obj.h
+++ b/py/obj.h
@@ -231,6 +231,7 @@ extern const mp_obj_t mp_const_false;
 extern const mp_obj_t mp_const_true;
 extern const mp_obj_t mp_const_empty_tuple;
 extern const mp_obj_t mp_const_ellipsis;
+extern const mp_obj_t mp_const_GeneratorExit;
 
 // General API for objects
 
diff --git a/py/objexcept.c b/py/objexcept.c
index 71874751b2db1b3da739c8368c8b957fe0e2129d..d4c4b124920cf009d8f6620db821d6ce191aa3a2 100644
--- a/py/objexcept.c
+++ b/py/objexcept.c
@@ -21,6 +21,12 @@ typedef struct mp_obj_exception_t {
     mp_obj_tuple_t args;
 } mp_obj_exception_t;
 
+// Instance of GeneratorExit exception - needed by generator.close()
+// This would belong to objgenerator.c, but to keep mp_obj_exception_t
+// definition module-private so far, have it here.
+STATIC mp_obj_exception_t GeneratorExit_obj = {{&mp_type_GeneratorExit}, MP_OBJ_NULL, NULL, {{&tuple_type}, 0}};
+const mp_obj_t mp_const_GeneratorExit = (mp_obj_t)&GeneratorExit_obj;
+
 STATIC void mp_obj_exception_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t o_in, mp_print_kind_t kind) {
     mp_obj_exception_t *o = o_in;
     if (o->msg != NULL) {
diff --git a/py/objgenerator.c b/py/objgenerator.c
index aeb5f6219a03b8b4d6d0f814cb864015512813db..f6c7007a02d8ca26ddc7949699820ba0a52e23eb 100644
--- a/py/objgenerator.c
+++ b/py/objgenerator.c
@@ -171,7 +171,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(gen_instance_throw_obj, 2, 4, gen_ins
 
 STATIC mp_obj_t gen_instance_close(mp_obj_t self_in) {
     mp_obj_t ret;
-    switch (mp_obj_gen_resume(self_in, mp_const_none, (mp_obj_t)&mp_type_GeneratorExit, &ret)) {
+    switch (mp_obj_gen_resume(self_in, mp_const_none, mp_const_GeneratorExit, &ret)) {
         case MP_VM_RETURN_YIELD:
             nlr_jump(mp_obj_new_exception_msg(&mp_type_RuntimeError, "generator ignored GeneratorExit"));
 
diff --git a/py/runtime.c b/py/runtime.c
index 762924c20a1ae021afdc8c8da295916dfe4541ed..3f637a16f9c65f3755fe60d139f5a1b3869620f1 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -20,7 +20,6 @@
 
 #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
@@ -33,10 +32,6 @@ STATIC mp_map_t *map_locals;
 STATIC mp_map_t *map_globals;
 STATIC mp_map_t map_builtins;
 
-#ifdef WRITE_CODE
-FILE *fp_write_code = NULL;
-#endif
-
 // a good optimising compiler will inline this if necessary
 STATIC void mp_map_add_qstr(mp_map_t *map, qstr qstr, mp_obj_t value) {
     mp_map_lookup(map, MP_OBJ_NEW_QSTR(qstr), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value;
@@ -68,18 +63,9 @@ void rt_init(void) {
     // for efficiency, left to platform-specific startup code
     //sys_path = mp_obj_new_list(0, NULL);
     //rt_store_attr(m_sys, MP_QSTR_path, sys_path);
-
-#ifdef WRITE_CODE
-    fp_write_code = fopen("out-code", "wb");
-#endif
 }
 
 void rt_deinit(void) {
-#ifdef WRITE_CODE
-    if (fp_write_code != NULL) {
-        fclose(fp_write_code);
-    }
-#endif
     mp_map_free(map_globals);
     mp_map_deinit(&map_builtins);
     mp_module_deinit();
diff --git a/py/showbc.c b/py/showbc.c
index eb743bd29e583627a704d6a101514c1774c9bda3..12bd901185204793fef03c51cd5e0d36e664c2f3 100644
--- a/py/showbc.c
+++ b/py/showbc.c
@@ -30,7 +30,16 @@ void mp_byte_code_print(const byte *ip, int len) {
     machine_uint_t code_info_size = ip[0] | (ip[1] << 8) | (ip[2] << 16) | (ip[3] << 24);
     ip += code_info_size;
 
-    // decode prelude
+    // bytecode prelude: state size and exception stack size; 16 bit uints
+    {
+        uint n_state = ip[0] | (ip[1] << 8);
+        uint n_exc_stack = ip[2] | (ip[3] << 8);
+        ip += 4;
+        printf("(N_STATE %u)\n", n_state);
+        printf("(N_EXC_STACK %u)\n", n_exc_stack);
+    }
+
+    // bytecode prelude: initialise closed over variables
     {
         uint n_local = *ip++;
         printf("(NUM_LOCAL %u)\n", n_local);
@@ -244,6 +253,15 @@ void mp_byte_code_print(const byte *ip, int len) {
                 printf("SETUP_LOOP " UINT_FMT, ip + unum - ip_start);
                 break;
 
+            case MP_BC_SETUP_WITH:
+                DECODE_ULABEL; // loop-like labels are always forward
+                printf("SETUP_WITH " UINT_FMT, ip + unum - ip_start);
+                break;
+
+            case MP_BC_WITH_CLEANUP:
+                printf("WITH_CLEANUP");
+                break;
+
             case MP_BC_UNWIND_JUMP:
                 DECODE_SLABEL;
                 printf("UNWIND_JUMP " UINT_FMT " %d", ip + unum - ip_start, *ip);