py: work towards working closures.
diff --git a/py/runtime.c b/py/runtime.c
index df69ddf..189d29f 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -43,6 +43,7 @@
     O_FLOAT,
     O_COMPLEX,
 #endif
+    O_CELL,
     O_EXCEPTION_0,
     O_EXCEPTION_N,
     O_RANGE,
@@ -57,6 +58,7 @@
     O_GEN_WRAP,
     O_GEN_INSTANCE,
     O_BOUND_METH,
+    O_CLOSURE,
     O_TUPLE,
     O_LIST,
     O_TUPLE_IT,
@@ -101,6 +103,7 @@
             py_float_t imag;
         } u_complex;
 #endif
+        py_obj_t u_cell; // for O_CELL
         struct { // for O_EXCEPTION_0
             qstr id;
         } u_exc0;
@@ -148,6 +151,10 @@
             py_obj_t meth;
             py_obj_t self;
         } u_bound_meth;
+        struct { // for O_CLOSURE
+            py_obj_t fun;
+            py_obj_t vars;
+        } u_closure;
         struct { // for O_TUPLE, O_LIST
             machine_uint_t alloc;
             machine_uint_t len;
@@ -382,6 +389,13 @@
 }
 #endif
 
+py_obj_t py_obj_new_cell(py_obj_t val) {
+    py_obj_base_t *o = m_new(py_obj_base_t, 1);
+    o->kind = O_CELL;
+    o->u_cell = val;
+    return (py_obj_t)o;
+}
+
 py_obj_t py_obj_new_exception_0(qstr id) {
     py_obj_base_t *o = m_new(py_obj_base_t, 1);
     o->kind = O_EXCEPTION_0;
@@ -585,6 +599,23 @@
     }
 }
 
+py_obj_t py_obj_get_cell(py_obj_t cell) {
+    if (IS_O(cell, O_CELL)) {
+        return ((py_obj_base_t*)cell)->u_cell;
+    } else {
+        assert(0);
+        return py_const_none;
+    }
+}
+
+void py_obj_set_cell(py_obj_t cell, py_obj_t val) {
+    if (IS_O(cell, O_CELL)) {
+        ((py_obj_base_t*)cell)->u_cell = val;
+    } else {
+        assert(0);
+    }
+}
+
 py_obj_t *py_obj_get_array_fixed_n(py_obj_t o_in, machine_int_t n) {
     if (IS_O(o_in, O_TUPLE) || IS_O(o_in, O_LIST)) {
         py_obj_base_t *o = o_in;
@@ -879,6 +910,7 @@
     py_code_kind_t kind;
     int n_args;
     int n_locals;
+    int n_cells;
     int n_stack;
     bool is_generator;
     union {
@@ -1078,19 +1110,20 @@
     }
 }
 
-void rt_assign_byte_code(int unique_code_id, byte *code, uint len, int n_args, int n_locals, int n_stack, bool is_generator) {
+void rt_assign_byte_code(int unique_code_id, byte *code, uint len, int n_args, int n_locals, int n_cells, int n_stack, bool is_generator) {
     alloc_unique_codes();
 
     assert(unique_code_id < next_unique_code_id);
     unique_codes[unique_code_id].kind = PY_CODE_BYTE;
     unique_codes[unique_code_id].n_args = n_args;
     unique_codes[unique_code_id].n_locals = n_locals;
+    unique_codes[unique_code_id].n_cells = n_cells;
     unique_codes[unique_code_id].n_stack = n_stack;
     unique_codes[unique_code_id].is_generator = is_generator;
     unique_codes[unique_code_id].u_byte.code = code;
     unique_codes[unique_code_id].u_byte.len = len;
 
-    printf("byte code: %d bytes\n", len);
+    //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", unique_code_id, code, len, n_args);
@@ -1120,6 +1153,7 @@
     unique_codes[unique_code_id].kind = PY_CODE_NATIVE;
     unique_codes[unique_code_id].n_args = n_args;
     unique_codes[unique_code_id].n_locals = 0;
+    unique_codes[unique_code_id].n_cells = 0;
     unique_codes[unique_code_id].n_stack = 0;
     unique_codes[unique_code_id].is_generator = false;
     unique_codes[unique_code_id].u_native.fun = fun;
@@ -1153,6 +1187,7 @@
     unique_codes[unique_code_id].kind = PY_CODE_INLINE_ASM;
     unique_codes[unique_code_id].n_args = n_args;
     unique_codes[unique_code_id].n_locals = 0;
+    unique_codes[unique_code_id].n_cells = 0;
     unique_codes[unique_code_id].n_stack = 0;
     unique_codes[unique_code_id].is_generator = false;
     unique_codes[unique_code_id].u_inline_asm.fun = fun;
@@ -1562,7 +1597,7 @@
         case PY_CODE_BYTE:
             o->kind = O_FUN_BC;
             o->u_fun_bc.n_args = c->n_args;
-            o->u_fun_bc.n_state = c->n_locals + c->n_stack;
+            o->u_fun_bc.n_state = c->n_locals + c->n_cells + c->n_stack;
             o->u_fun_bc.code = c->u_byte.code;
             break;
         case PY_CODE_NATIVE:
@@ -1588,7 +1623,7 @@
         py_obj_base_t *o2 = m_new(py_obj_base_t, 1);
         o2->kind = O_GEN_WRAP;
         // we have at least 3 locals so the bc can write back fast[0,1,2] safely; should improve how this is done
-        o2->u_gen_wrap.n_state = (c->n_locals < 3 ? 3 : c->n_locals) + c->n_stack;
+        o2->u_gen_wrap.n_state = ((c->n_locals + c->n_cells) < 3 ? 3 : (c->n_locals + c->n_cells)) + c->n_stack;
         o2->u_gen_wrap.fun = o;
         o = o2;
     }
@@ -1634,6 +1669,16 @@
     return o;
 }
 
+py_obj_t rt_make_closure_from_id(int unique_code_id, py_obj_t closure_tuple) {
+    py_obj_t f = rt_make_function_from_id(unique_code_id);
+    // wrap function in closure object
+    py_obj_base_t *f2 = m_new(py_obj_base_t, 1);
+    f2->kind = O_CLOSURE;
+    f2->u_closure.fun = f;
+    f2->u_closure.vars = closure_tuple;
+    return f2;
+}
+
 py_obj_t rt_call_function_0(py_obj_t fun) {
     return rt_call_function_n(fun, 0, NULL);
 }