Просмотр исходного кода

1、【创建】Git 仓库。

Signed-off-by: armink <armink.ztl@gmail.com>
armink 8 лет назад
Сommit
58767e1e8e
100 измененных файлов с 22960 добавлено и 0 удалено
  1. 19 0
      SConscript
  2. 640 0
      extmod/machine_i2c.c
  3. 56 0
      extmod/machine_i2c.h
  4. 103 0
      extmod/machine_mem.c
  5. 49 0
      extmod/machine_mem.h
  6. 87 0
      extmod/machine_pinbase.c
  7. 33 0
      extmod/machine_pinbase.h
  8. 65 0
      extmod/machine_pulse.c
  9. 36 0
      extmod/machine_pulse.h
  10. 183 0
      extmod/machine_signal.c
  11. 33 0
      extmod/machine_signal.h
  12. 339 0
      extmod/machine_spi.c
  13. 61 0
      extmod/machine_spi.h
  14. 45 0
      extmod/misc.h
  15. 594 0
      extmod/modframebuf.c
  16. 253 0
      extmod/modubinascii.c
  17. 41 0
      extmod/modubinascii.h
  18. 715 0
      extmod/moductypes.c
  19. 158 0
      extmod/moduhashlib.c
  20. 120 0
      extmod/moduheapq.c
  21. 298 0
      extmod/modujson.c
  22. 225 0
      extmod/modurandom.c
  23. 257 0
      extmod/modure.c
  24. 378 0
      extmod/moduselect.c
  25. 238 0
      extmod/modussl_axtls.c
  26. 342 0
      extmod/modussl_mbedtls.c
  27. 230 0
      extmod/modutimeq.c
  28. 224 0
      extmod/moduzlib.c
  29. 352 0
      extmod/modwebrepl.c
  30. 316 0
      extmod/modwebsocket.c
  31. 10 0
      extmod/modwebsocket.h
  32. 145 0
      extmod/uos_dupterm.c
  33. 102 0
      extmod/utime_mphal.c
  34. 41 0
      extmod/utime_mphal.h
  35. 461 0
      extmod/vfs.c
  36. 85 0
      extmod/vfs.h
  37. 344 0
      extmod/vfs_fat.c
  38. 64 0
      extmod/vfs_fat.h
  39. 280 0
      extmod/vfs_fat_diskio.c
  40. 299 0
      extmod/vfs_fat_file.c
  41. 108 0
      extmod/vfs_fat_misc.c
  42. 87 0
      extmod/vfs_reader.c
  43. 39 0
      extmod/virtpin.c
  44. 47 0
      extmod/virtpin.h
  45. 447 0
      lib/mp-readline/readline.c
  46. 48 0
      lib/mp-readline/readline.h
  47. 50 0
      lib/utils/interrupt_char.c
  48. 33 0
      lib/utils/interrupt_char.h
  49. 135 0
      lib/utils/printf.c
  50. 530 0
      lib/utils/pyexec.c
  51. 54 0
      lib/utils/pyexec.h
  52. 26 0
      lib/utils/stdout_helpers.c
  53. 120 0
      port/_frozen_mpy.c
  54. 8 0
      port/genhdr/mpversion.h
  55. 464 0
      port/genhdr/qstrdefs.generated.h
  56. 40 0
      port/help.c
  57. 242 0
      port/machine_pin.c
  58. 207 0
      port/modmachine.c
  59. 52 0
      port/modmachine.h
  60. 166 0
      port/modpyb.c
  61. 87 0
      port/modrtthread.c
  62. 61 0
      port/modutime.c
  63. 212 0
      port/mpconfigport.h
  64. 48 0
      port/mphalport.h
  65. 151 0
      port/mpy_main.c
  66. 35 0
      port/mpy_project_cfg.h
  67. 36 0
      port/portmodules.h
  68. 1 0
      port/qstrdefsport.h
  69. 104 0
      port/rtt_getchar.c
  70. 34 0
      port/rtt_getchar.h
  71. 48 0
      port/uart_core.c
  72. 144 0
      py/argcheck.c
  73. 368 0
      py/asmarm.c
  74. 205 0
      py/asmarm.h
  75. 102 0
      py/asmbase.c
  76. 69 0
      py/asmbase.h
  77. 380 0
      py/asmthumb.c
  78. 321 0
      py/asmthumb.h
  79. 632 0
      py/asmx64.c
  80. 200 0
      py/asmx64.h
  81. 511 0
      py/asmx86.c
  82. 198 0
      py/asmx86.h
  83. 174 0
      py/asmxtensa.c
  84. 324 0
      py/asmxtensa.h
  85. 415 0
      py/bc.c
  86. 121 0
      py/bc.h
  87. 119 0
      py/bc0.h
  88. 392 0
      py/binary.c
  89. 45 0
      py/binary.h
  90. 123 0
      py/builtin.h
  91. 167 0
      py/builtinevex.c
  92. 179 0
      py/builtinhelp.c
  93. 474 0
      py/builtinimport.c
  94. 3517 0
      py/compile.c
  95. 54 0
      py/compile.h
  96. 285 0
      py/emit.h
  97. 1076 0
      py/emitbc.c
  98. 77 0
      py/emitcommon.c
  99. 170 0
      py/emitglue.c
  100. 77 0
      py/emitglue.h

+ 19 - 0
SConscript

@@ -0,0 +1,19 @@
+from building import *
+
+# get current directory
+cwd     = GetCurrentDir()
+# The set of source files associated with this SConscript file.
+src     = Glob('py/*.c')
+src    += Glob('lib/mp-readline/*.c')
+src    += Glob('lib/utils/*.c')
+src    += Glob('extmod/*.c')
+src    += Glob('port/*.c')
+
+path    = [cwd + '/']
+path   += [cwd + '/port']
+
+LOCAL_CCFLAGS = ' -include "port/mpy_project_cfg.h"'
+
+group = DefineGroup('MicroPython', src, depend = ['PKG_USING_WEBTERMINAL'], CPPPATH = path, LOCAL_CCFLAGS = LOCAL_CCFLAGS)
+
+Return('group')

+ 640 - 0
extmod/machine_i2c.c

@@ -0,0 +1,640 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "py/mperrno.h"
+#include "py/mphal.h"
+#include "py/runtime.h"
+#include "extmod/machine_i2c.h"
+
+#if MICROPY_PY_MACHINE_I2C
+
+typedef mp_machine_soft_i2c_obj_t machine_i2c_obj_t;
+
+STATIC void mp_hal_i2c_delay(machine_i2c_obj_t *self) {
+    // We need to use an accurate delay to get acceptable I2C
+    // speeds (eg 1us should be not much more than 1us).
+    mp_hal_delay_us_fast(self->us_delay);
+}
+
+STATIC void mp_hal_i2c_scl_low(machine_i2c_obj_t *self) {
+    mp_hal_pin_od_low(self->scl);
+}
+
+STATIC int mp_hal_i2c_scl_release(machine_i2c_obj_t *self) {
+    uint32_t count = self->us_timeout;
+
+    mp_hal_pin_od_high(self->scl);
+    mp_hal_i2c_delay(self);
+    // For clock stretching, wait for the SCL pin to be released, with timeout.
+    for (; mp_hal_pin_read(self->scl) == 0 && count; --count) {
+        mp_hal_delay_us_fast(1);
+    }
+    if (count == 0) {
+        return -MP_ETIMEDOUT;
+    }
+    return 0; // success
+}
+
+STATIC void mp_hal_i2c_sda_low(machine_i2c_obj_t *self) {
+    mp_hal_pin_od_low(self->sda);
+}
+
+STATIC void mp_hal_i2c_sda_release(machine_i2c_obj_t *self) {
+    mp_hal_pin_od_high(self->sda);
+}
+
+STATIC int mp_hal_i2c_sda_read(machine_i2c_obj_t *self) {
+    return mp_hal_pin_read(self->sda);
+}
+
+STATIC int mp_hal_i2c_start(machine_i2c_obj_t *self) {
+    mp_hal_i2c_sda_release(self);
+    mp_hal_i2c_delay(self);
+    int ret = mp_hal_i2c_scl_release(self);
+    if (ret != 0) {
+        return ret;
+    }
+    mp_hal_i2c_sda_low(self);
+    mp_hal_i2c_delay(self);
+    return 0; // success
+}
+
+STATIC int mp_hal_i2c_stop(machine_i2c_obj_t *self) {
+    mp_hal_i2c_delay(self);
+    mp_hal_i2c_sda_low(self);
+    mp_hal_i2c_delay(self);
+    int ret = mp_hal_i2c_scl_release(self);
+    mp_hal_i2c_sda_release(self);
+    mp_hal_i2c_delay(self);
+    return ret;
+}
+
+STATIC void mp_hal_i2c_init(machine_i2c_obj_t *self, uint32_t freq) {
+    self->us_delay = 500000 / freq;
+    if (self->us_delay == 0) {
+        self->us_delay = 1;
+    }
+    mp_hal_pin_open_drain(self->scl);
+    mp_hal_pin_open_drain(self->sda);
+    mp_hal_i2c_stop(self); // ignore error
+}
+
+// return value:
+//    0 - byte written and ack received
+//    1 - byte written and nack received
+//   <0 - error, with errno being the negative of the return value
+STATIC int mp_hal_i2c_write_byte(machine_i2c_obj_t *self, uint8_t val) {
+    mp_hal_i2c_delay(self);
+    mp_hal_i2c_scl_low(self);
+
+    for (int i = 7; i >= 0; i--) {
+        if ((val >> i) & 1) {
+            mp_hal_i2c_sda_release(self);
+        } else {
+            mp_hal_i2c_sda_low(self);
+        }
+        mp_hal_i2c_delay(self);
+        int ret = mp_hal_i2c_scl_release(self);
+        if (ret != 0) {
+            mp_hal_i2c_sda_release(self);
+            return ret;
+        }
+        mp_hal_i2c_scl_low(self);
+    }
+
+    mp_hal_i2c_sda_release(self);
+    mp_hal_i2c_delay(self);
+    int ret = mp_hal_i2c_scl_release(self);
+    if (ret != 0) {
+        return ret;
+    }
+
+    int ack = mp_hal_i2c_sda_read(self);
+    mp_hal_i2c_delay(self);
+    mp_hal_i2c_scl_low(self);
+
+    return ack;
+}
+
+// return value:
+//    0 - success
+//   <0 - error, with errno being the negative of the return value
+STATIC int mp_hal_i2c_read_byte(machine_i2c_obj_t *self, uint8_t *val, int nack) {
+    mp_hal_i2c_delay(self);
+    mp_hal_i2c_scl_low(self);
+    mp_hal_i2c_delay(self);
+
+    uint8_t data = 0;
+    for (int i = 7; i >= 0; i--) {
+        int ret = mp_hal_i2c_scl_release(self);
+        if (ret != 0) {
+            return ret;
+        }
+        data = (data << 1) | mp_hal_i2c_sda_read(self);
+        mp_hal_i2c_scl_low(self);
+        mp_hal_i2c_delay(self);
+    }
+    *val = data;
+
+    // send ack/nack bit
+    if (!nack) {
+        mp_hal_i2c_sda_low(self);
+    }
+    mp_hal_i2c_delay(self);
+    int ret = mp_hal_i2c_scl_release(self);
+    if (ret != 0) {
+        mp_hal_i2c_sda_release(self);
+        return ret;
+    }
+    mp_hal_i2c_scl_low(self);
+    mp_hal_i2c_sda_release(self);
+
+    return 0; // success
+}
+
+// return value:
+//  >=0 - number of acks received
+//   <0 - error, with errno being the negative of the return value
+int mp_machine_soft_i2c_writeto(mp_obj_base_t *self_in, uint16_t addr, const uint8_t *src, size_t len, bool stop) {
+    machine_i2c_obj_t *self = (machine_i2c_obj_t*)self_in;
+
+    // start the I2C transaction
+    int ret = mp_hal_i2c_start(self);
+    if (ret != 0) {
+        return ret;
+    }
+
+    // write the slave address
+    ret = mp_hal_i2c_write_byte(self, addr << 1);
+    if (ret < 0) {
+        return ret;
+    } else if (ret != 0) {
+        // nack received, release the bus cleanly
+        mp_hal_i2c_stop(self);
+        return -MP_ENODEV;
+    }
+
+    // write the buffer to the I2C memory
+    int num_acks = 0;
+    while (len--) {
+        ret = mp_hal_i2c_write_byte(self, *src++);
+        if (ret < 0) {
+            return ret;
+        } else if (ret != 0) {
+            // nack received, stop sending
+            break;
+        }
+        ++num_acks;
+    }
+
+    // finish the I2C transaction
+    if (stop) {
+        ret = mp_hal_i2c_stop(self);
+        if (ret != 0) {
+            return ret;
+        }
+    }
+
+    return num_acks;
+}
+
+// return value:
+//    0 - success
+//   <0 - error, with errno being the negative of the return value
+int mp_machine_soft_i2c_readfrom(mp_obj_base_t *self_in, uint16_t addr, uint8_t *dest, size_t len, bool stop) {
+    machine_i2c_obj_t *self = (machine_i2c_obj_t*)self_in;
+
+    // start the I2C transaction
+    int ret = mp_hal_i2c_start(self);
+    if (ret != 0) {
+        return ret;
+    }
+
+    // write the slave address
+    ret = mp_hal_i2c_write_byte(self, (addr << 1) | 1);
+    if (ret < 0) {
+        return ret;
+    } else if (ret != 0) {
+        // nack received, release the bus cleanly
+        mp_hal_i2c_stop(self);
+        return -MP_ENODEV;
+    }
+
+    // read the bytes from the slave
+    while (len--) {
+        ret = mp_hal_i2c_read_byte(self, dest++, len == 0);
+        if (ret != 0) {
+            return ret;
+        }
+    }
+
+    // finish the I2C transaction
+    if (stop) {
+        ret = mp_hal_i2c_stop(self);
+        if (ret != 0) {
+            return ret;
+        }
+    }
+
+    return 0; // success
+}
+
+/******************************************************************************/
+// MicroPython bindings for I2C
+
+STATIC void machine_i2c_obj_init_helper(machine_i2c_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+    enum { ARG_scl, ARG_sda, ARG_freq, ARG_timeout };
+    static const mp_arg_t allowed_args[] = {
+        { MP_QSTR_scl, MP_ARG_REQUIRED | MP_ARG_OBJ },
+        { MP_QSTR_sda, MP_ARG_REQUIRED | MP_ARG_OBJ },
+        { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 400000} },
+        { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 255} },
+    };
+    mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+    mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+    self->scl = mp_hal_get_pin_obj(args[ARG_scl].u_obj);
+    self->sda = mp_hal_get_pin_obj(args[ARG_sda].u_obj);
+    self->us_timeout = args[ARG_timeout].u_int;
+    mp_hal_i2c_init(self, args[ARG_freq].u_int);
+}
+
+STATIC mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+    // check the id argument, if given
+    if (n_args > 0) {
+        if (args[0] != MP_OBJ_NEW_SMALL_INT(-1)) {
+            #if defined(MICROPY_PY_MACHINE_I2C_MAKE_NEW)
+            // dispatch to port-specific constructor
+            extern mp_obj_t MICROPY_PY_MACHINE_I2C_MAKE_NEW(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args);
+            return MICROPY_PY_MACHINE_I2C_MAKE_NEW(type, n_args, n_kw, args);
+            #else
+            mp_raise_ValueError("invalid I2C peripheral");
+            #endif
+        }
+        --n_args;
+        ++args;
+    }
+
+    // create new soft I2C object
+    machine_i2c_obj_t *self = m_new_obj(machine_i2c_obj_t);
+    self->base.type = &machine_i2c_type;
+    mp_map_t kw_args;
+    mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
+    machine_i2c_obj_init_helper(self, n_args, args, &kw_args);
+    return (mp_obj_t)self;
+}
+
+STATIC mp_obj_t machine_i2c_obj_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
+    machine_i2c_obj_init_helper(args[0], n_args - 1, args + 1, kw_args);
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_init_obj, 1, machine_i2c_obj_init);
+
+STATIC mp_obj_t machine_i2c_scan(mp_obj_t self_in) {
+    mp_obj_base_t *self = MP_OBJ_TO_PTR(self_in);
+    mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol;
+    mp_obj_t list = mp_obj_new_list(0, NULL);
+    // 7-bit addresses 0b0000xxx and 0b1111xxx are reserved
+    for (int addr = 0x08; addr < 0x78; ++addr) {
+        int ret = i2c_p->writeto(self, addr, NULL, 0, true);
+        if (ret == 0) {
+            mp_obj_list_append(list, MP_OBJ_NEW_SMALL_INT(addr));
+        }
+    }
+    return list;
+}
+MP_DEFINE_CONST_FUN_OBJ_1(machine_i2c_scan_obj, machine_i2c_scan);
+
+STATIC mp_obj_t machine_i2c_start(mp_obj_t self_in) {
+    mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(self_in);
+    mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol;
+    if (i2c_p->start == NULL) {
+        mp_raise_msg(&mp_type_OSError, "I2C operation not supported");
+    }
+    int ret = i2c_p->start(self);
+    if (ret != 0) {
+        mp_raise_OSError(-ret);
+    }
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_1(machine_i2c_start_obj, machine_i2c_start);
+
+STATIC mp_obj_t machine_i2c_stop(mp_obj_t self_in) {
+    mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(self_in);
+    mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol;
+    if (i2c_p->stop == NULL) {
+        mp_raise_msg(&mp_type_OSError, "I2C operation not supported");
+    }
+    int ret = i2c_p->stop(self);
+    if (ret != 0) {
+        mp_raise_OSError(-ret);
+    }
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_1(machine_i2c_stop_obj, machine_i2c_stop);
+
+STATIC mp_obj_t machine_i2c_readinto(size_t n_args, const mp_obj_t *args) {
+    mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]);
+    mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol;
+    if (i2c_p->read == NULL) {
+        mp_raise_msg(&mp_type_OSError, "I2C operation not supported");
+    }
+
+    // get the buffer to read into
+    mp_buffer_info_t bufinfo;
+    mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_WRITE);
+
+    // work out if we want to send a nack at the end
+    bool nack = (n_args == 2) ? true : mp_obj_is_true(args[2]);
+
+    // do the read
+    int ret = i2c_p->read(self, bufinfo.buf, bufinfo.len, nack);
+    if (ret != 0) {
+        mp_raise_OSError(-ret);
+    }
+
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_i2c_readinto_obj, 2, 3, machine_i2c_readinto);
+
+STATIC mp_obj_t machine_i2c_write(mp_obj_t self_in, mp_obj_t buf_in) {
+    mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(self_in);
+    mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol;
+    if (i2c_p->write == NULL) {
+        mp_raise_msg(&mp_type_OSError, "I2C operation not supported");
+    }
+
+    // get the buffer to write from
+    mp_buffer_info_t bufinfo;
+    mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ);
+
+    // do the write
+    int ret = i2c_p->write(self, bufinfo.buf, bufinfo.len);
+    if (ret < 0) {
+        mp_raise_OSError(-ret);
+    }
+
+    // return number of acks received
+    return MP_OBJ_NEW_SMALL_INT(ret);
+}
+MP_DEFINE_CONST_FUN_OBJ_2(machine_i2c_write_obj, machine_i2c_write);
+
+STATIC mp_obj_t machine_i2c_readfrom(size_t n_args, const mp_obj_t *args) {
+    mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]);
+    mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol;
+    mp_int_t addr = mp_obj_get_int(args[1]);
+    vstr_t vstr;
+    vstr_init_len(&vstr, mp_obj_get_int(args[2]));
+    bool stop = (n_args == 3) ? true : mp_obj_is_true(args[3]);
+    int ret = i2c_p->readfrom(self, addr, (uint8_t*)vstr.buf, vstr.len, stop);
+    if (ret < 0) {
+        mp_raise_OSError(-ret);
+    }
+    return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_i2c_readfrom_obj, 3, 4, machine_i2c_readfrom);
+
+STATIC mp_obj_t machine_i2c_readfrom_into(size_t n_args, const mp_obj_t *args) {
+    mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]);
+    mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol;
+    mp_int_t addr = mp_obj_get_int(args[1]);
+    mp_buffer_info_t bufinfo;
+    mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_WRITE);
+    bool stop = (n_args == 3) ? true : mp_obj_is_true(args[3]);
+    int ret = i2c_p->readfrom(self, addr, bufinfo.buf, bufinfo.len, stop);
+    if (ret < 0) {
+        mp_raise_OSError(-ret);
+    }
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_i2c_readfrom_into_obj, 3, 4, machine_i2c_readfrom_into);
+
+STATIC mp_obj_t machine_i2c_writeto(size_t n_args, const mp_obj_t *args) {
+    mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]);
+    mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol;
+    mp_int_t addr = mp_obj_get_int(args[1]);
+    mp_buffer_info_t bufinfo;
+    mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ);
+    bool stop = (n_args == 3) ? true : mp_obj_is_true(args[3]);
+    int ret = i2c_p->writeto(self, addr,  bufinfo.buf, bufinfo.len, stop);
+    if (ret < 0) {
+        mp_raise_OSError(-ret);
+    }
+    // return number of acks received
+    return MP_OBJ_NEW_SMALL_INT(ret);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_i2c_writeto_obj, 3, 4, machine_i2c_writeto);
+
+STATIC int read_mem(mp_obj_t self_in, uint16_t addr, uint32_t memaddr, uint8_t addrsize, uint8_t *buf, size_t len) {
+    mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(self_in);
+    mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol;
+    uint8_t memaddr_buf[4];
+    size_t memaddr_len = 0;
+    for (int16_t i = addrsize - 8; i >= 0; i -= 8) {
+        memaddr_buf[memaddr_len++] = memaddr >> i;
+    }
+    int ret = i2c_p->writeto(self, addr, memaddr_buf, memaddr_len, false);
+    if (ret != memaddr_len) {
+        // must generate STOP
+        i2c_p->writeto(self, addr, NULL, 0, true);
+        return ret;
+    }
+    return i2c_p->readfrom(self, addr, buf, len, true);
+}
+
+#define MAX_MEMADDR_SIZE (4)
+#define BUF_STACK_SIZE (12)
+
+STATIC int write_mem(mp_obj_t self_in, uint16_t addr, uint32_t memaddr, uint8_t addrsize, const uint8_t *buf, size_t len) {
+    mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(self_in);
+    mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol;
+
+    // need some memory to create the buffer to send; try to use stack if possible
+    uint8_t buf2_stack[MAX_MEMADDR_SIZE + BUF_STACK_SIZE];
+    uint8_t *buf2;
+    size_t buf2_alloc = 0;
+    if (len <= BUF_STACK_SIZE) {
+        buf2 = buf2_stack;
+    } else {
+        buf2_alloc = MAX_MEMADDR_SIZE + len;
+        buf2 = m_new(uint8_t, buf2_alloc);
+    }
+
+    // create the buffer to send
+    size_t memaddr_len = 0;
+    for (int16_t i = addrsize - 8; i >= 0; i -= 8) {
+        buf2[memaddr_len++] = memaddr >> i;
+    }
+    memcpy(buf2 + memaddr_len, buf, len);
+
+    int ret = i2c_p->writeto(self, addr, buf2, memaddr_len + len, true);
+    if (buf2_alloc != 0) {
+        m_del(uint8_t, buf2, buf2_alloc);
+    }
+    return ret;
+}
+
+STATIC const mp_arg_t machine_i2c_mem_allowed_args[] = {
+    { MP_QSTR_addr,    MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
+    { MP_QSTR_memaddr, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
+    { MP_QSTR_arg,     MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
+    { MP_QSTR_addrsize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} },
+};
+
+STATIC mp_obj_t machine_i2c_readfrom_mem(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+    enum { ARG_addr, ARG_memaddr, ARG_n, ARG_addrsize };
+    mp_arg_val_t args[MP_ARRAY_SIZE(machine_i2c_mem_allowed_args)];
+    mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args,
+        MP_ARRAY_SIZE(machine_i2c_mem_allowed_args), machine_i2c_mem_allowed_args, args);
+
+    // create the buffer to store data into
+    vstr_t vstr;
+    vstr_init_len(&vstr, mp_obj_get_int(args[ARG_n].u_obj));
+
+    // do the transfer
+    int ret = read_mem(pos_args[0], args[ARG_addr].u_int, args[ARG_memaddr].u_int,
+        args[ARG_addrsize].u_int, (uint8_t*)vstr.buf, vstr.len);
+    if (ret < 0) {
+        mp_raise_OSError(-ret);
+    }
+
+    return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
+}
+MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_readfrom_mem_obj, 1, machine_i2c_readfrom_mem);
+
+
+STATIC mp_obj_t machine_i2c_readfrom_mem_into(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+    enum { ARG_addr, ARG_memaddr, ARG_buf, ARG_addrsize };
+    mp_arg_val_t args[MP_ARRAY_SIZE(machine_i2c_mem_allowed_args)];
+    mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args,
+        MP_ARRAY_SIZE(machine_i2c_mem_allowed_args), machine_i2c_mem_allowed_args, args);
+
+    // get the buffer to store data into
+    mp_buffer_info_t bufinfo;
+    mp_get_buffer_raise(args[ARG_buf].u_obj, &bufinfo, MP_BUFFER_WRITE);
+
+    // do the transfer
+    int ret = read_mem(pos_args[0], args[ARG_addr].u_int, args[ARG_memaddr].u_int,
+        args[ARG_addrsize].u_int, bufinfo.buf, bufinfo.len);
+    if (ret < 0) {
+        mp_raise_OSError(-ret);
+    }
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_readfrom_mem_into_obj, 1, machine_i2c_readfrom_mem_into);
+
+STATIC mp_obj_t machine_i2c_writeto_mem(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+    enum { ARG_addr, ARG_memaddr, ARG_buf, ARG_addrsize };
+    mp_arg_val_t args[MP_ARRAY_SIZE(machine_i2c_mem_allowed_args)];
+    mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args,
+        MP_ARRAY_SIZE(machine_i2c_mem_allowed_args), machine_i2c_mem_allowed_args, args);
+
+    // get the buffer to write the data from
+    mp_buffer_info_t bufinfo;
+    mp_get_buffer_raise(args[ARG_buf].u_obj, &bufinfo, MP_BUFFER_READ);
+
+    // do the transfer
+    int ret = write_mem(pos_args[0], args[ARG_addr].u_int, args[ARG_memaddr].u_int,
+        args[ARG_addrsize].u_int, bufinfo.buf, bufinfo.len);
+    if (ret < 0) {
+        mp_raise_OSError(-ret);
+    }
+
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_writeto_mem_obj, 1, machine_i2c_writeto_mem);
+
+STATIC const mp_rom_map_elem_t machine_i2c_locals_dict_table[] = {
+    { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_i2c_init_obj) },
+    { MP_ROM_QSTR(MP_QSTR_scan), MP_ROM_PTR(&machine_i2c_scan_obj) },
+
+    // primitive I2C operations
+    { MP_ROM_QSTR(MP_QSTR_start), MP_ROM_PTR(&machine_i2c_start_obj) },
+    { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&machine_i2c_stop_obj) },
+    { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&machine_i2c_readinto_obj) },
+    { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&machine_i2c_write_obj) },
+
+    // standard bus operations
+    { MP_ROM_QSTR(MP_QSTR_readfrom), MP_ROM_PTR(&machine_i2c_readfrom_obj) },
+    { MP_ROM_QSTR(MP_QSTR_readfrom_into), MP_ROM_PTR(&machine_i2c_readfrom_into_obj) },
+    { MP_ROM_QSTR(MP_QSTR_writeto), MP_ROM_PTR(&machine_i2c_writeto_obj) },
+
+    // memory operations
+    { MP_ROM_QSTR(MP_QSTR_readfrom_mem), MP_ROM_PTR(&machine_i2c_readfrom_mem_obj) },
+    { MP_ROM_QSTR(MP_QSTR_readfrom_mem_into), MP_ROM_PTR(&machine_i2c_readfrom_mem_into_obj) },
+    { MP_ROM_QSTR(MP_QSTR_writeto_mem), MP_ROM_PTR(&machine_i2c_writeto_mem_obj) },
+};
+
+MP_DEFINE_CONST_DICT(mp_machine_soft_i2c_locals_dict, machine_i2c_locals_dict_table);
+
+int mp_machine_soft_i2c_read(mp_obj_base_t *self_in, uint8_t *dest, size_t len, bool nack) {
+    machine_i2c_obj_t *self = (machine_i2c_obj_t*)self_in;
+    while (len--) {
+        int ret = mp_hal_i2c_read_byte(self, dest++, nack && (len == 0));
+        if (ret != 0) {
+            return ret;
+        }
+    }
+    return 0; // success
+}
+
+int mp_machine_soft_i2c_write(mp_obj_base_t *self_in, const uint8_t *src, size_t len) {
+    machine_i2c_obj_t *self = (machine_i2c_obj_t*)self_in;
+    int num_acks = 0;
+    while (len--) {
+        int ret = mp_hal_i2c_write_byte(self, *src++);
+        if (ret < 0) {
+            return ret;
+        } else if (ret != 0) {
+            // nack received, stop sending
+            break;
+        }
+        ++num_acks;
+    }
+    return num_acks;
+}
+
+STATIC const mp_machine_i2c_p_t mp_machine_soft_i2c_p = {
+    .start = (int(*)(mp_obj_base_t*))mp_hal_i2c_start,
+    .stop = (int(*)(mp_obj_base_t*))mp_hal_i2c_stop,
+    .read = mp_machine_soft_i2c_read,
+    .write = mp_machine_soft_i2c_write,
+    .readfrom = mp_machine_soft_i2c_readfrom,
+    .writeto = mp_machine_soft_i2c_writeto,
+};
+
+const mp_obj_type_t machine_i2c_type = {
+    { &mp_type_type },
+    .name = MP_QSTR_I2C,
+    .make_new = machine_i2c_make_new,
+    .protocol = &mp_machine_soft_i2c_p,
+    .locals_dict = (mp_obj_dict_t*)&mp_machine_soft_i2c_locals_dict,
+};
+
+#endif // MICROPY_PY_MACHINE_I2C

+ 56 - 0
extmod/machine_i2c.h

@@ -0,0 +1,56 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_EXTMOD_MACHINE_I2C_H
+#define MICROPY_INCLUDED_EXTMOD_MACHINE_I2C_H
+
+#include "py/obj.h"
+
+// I2C protocol
+// the first 4 methods can be NULL, meaning operation is not supported
+typedef struct _mp_machine_i2c_p_t {
+    int (*start)(mp_obj_base_t *obj);
+    int (*stop)(mp_obj_base_t *obj);
+    int (*read)(mp_obj_base_t *obj, uint8_t *dest, size_t len, bool nack);
+    int (*write)(mp_obj_base_t *obj, const uint8_t *src, size_t len);
+    int (*readfrom)(mp_obj_base_t *obj, uint16_t addr, uint8_t *dest, size_t len, bool stop);
+    int (*writeto)(mp_obj_base_t *obj, uint16_t addr, const uint8_t *src, size_t len, bool stop);
+} mp_machine_i2c_p_t;
+
+typedef struct _mp_machine_soft_i2c_obj_t {
+    mp_obj_base_t base;
+    uint32_t us_delay;
+    uint32_t us_timeout;
+    mp_hal_pin_obj_t scl;
+    mp_hal_pin_obj_t sda;
+} mp_machine_soft_i2c_obj_t;
+
+extern const mp_obj_type_t machine_i2c_type;
+extern const mp_obj_dict_t mp_machine_soft_i2c_locals_dict;
+
+int mp_machine_soft_i2c_readfrom(mp_obj_base_t *self_in, uint16_t addr, uint8_t *dest, size_t len, bool stop);
+int mp_machine_soft_i2c_writeto(mp_obj_base_t *self_in, uint16_t addr, const uint8_t *src, size_t len, bool stop);
+
+#endif // MICROPY_INCLUDED_EXTMOD_MACHINE_I2C_H

+ 103 - 0
extmod/machine_mem.c

@@ -0,0 +1,103 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/runtime.h"
+#include "extmod/machine_mem.h"
+
+#if MICROPY_PY_MACHINE
+
+// If you wish to override the functions for mapping the machine_mem read/write
+// address, then add a #define for MICROPY_MACHINE_MEM_GET_READ_ADDR and/or
+// MICROPY_MACHINE_MEM_GET_WRITE_ADDR in your mpconfigport.h. Since the
+// prototypes are identical, it is allowable for both of the macros to evaluate
+// the to same function.
+//
+// It is expected that the modmachine.c file for a given port will provide the
+// implementations, if the default implementation isn't used.
+
+#if !defined(MICROPY_MACHINE_MEM_GET_READ_ADDR) || !defined(MICROPY_MACHINE_MEM_GET_WRITE_ADDR)
+STATIC uintptr_t machine_mem_get_addr(mp_obj_t addr_o, uint align) {
+    uintptr_t addr = mp_obj_int_get_truncated(addr_o);
+    if ((addr & (align - 1)) != 0) {
+        nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "address %08x is not aligned to %d bytes", addr, align));
+    }
+    return addr;
+}
+#if !defined(MICROPY_MACHINE_MEM_GET_READ_ADDR)
+#define MICROPY_MACHINE_MEM_GET_READ_ADDR machine_mem_get_addr
+#endif
+#if !defined(MICROPY_MACHINE_MEM_GET_WRITE_ADDR)
+#define MICROPY_MACHINE_MEM_GET_WRITE_ADDR machine_mem_get_addr
+#endif
+#endif
+
+STATIC void machine_mem_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
+    (void)kind;
+    machine_mem_obj_t *self = MP_OBJ_TO_PTR(self_in);
+    mp_printf(print, "<%u-bit memory>", 8 * self->elem_size);
+}
+
+STATIC mp_obj_t machine_mem_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
+    // TODO support slice index to read/write multiple values at once
+    machine_mem_obj_t *self = MP_OBJ_TO_PTR(self_in);
+    if (value == MP_OBJ_NULL) {
+        // delete
+        return MP_OBJ_NULL; // op not supported
+    } else if (value == MP_OBJ_SENTINEL) {
+        // load
+        uintptr_t addr = MICROPY_MACHINE_MEM_GET_READ_ADDR(index, self->elem_size);
+        uint32_t val;
+        switch (self->elem_size) {
+            case 1: val = (*(uint8_t*)addr); break;
+            case 2: val = (*(uint16_t*)addr); break;
+            default: val = (*(uint32_t*)addr); break;
+        }
+        return mp_obj_new_int(val);
+    } else {
+        // store
+        uintptr_t addr = MICROPY_MACHINE_MEM_GET_WRITE_ADDR(index, self->elem_size);
+        uint32_t val = mp_obj_get_int_truncated(value);
+        switch (self->elem_size) {
+            case 1: (*(uint8_t*)addr) = val; break;
+            case 2: (*(uint16_t*)addr) = val; break;
+            default: (*(uint32_t*)addr) = val; break;
+        }
+        return mp_const_none;
+    }
+}
+
+const mp_obj_type_t machine_mem_type = {
+    { &mp_type_type },
+    .name = MP_QSTR_mem,
+    .print = machine_mem_print,
+    .subscr = machine_mem_subscr,
+};
+
+const machine_mem_obj_t machine_mem8_obj = {{&machine_mem_type}, 1};
+const machine_mem_obj_t machine_mem16_obj = {{&machine_mem_type}, 2};
+const machine_mem_obj_t machine_mem32_obj = {{&machine_mem_type}, 4};
+
+#endif // MICROPY_PY_MACHINE

+ 49 - 0
extmod/machine_mem.h

@@ -0,0 +1,49 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_EXTMOD_MACHINE_MEM_H
+#define MICROPY_INCLUDED_EXTMOD_MACHINE_MEM_H
+
+#include "py/obj.h"
+
+typedef struct _machine_mem_obj_t {
+    mp_obj_base_t base;
+    unsigned elem_size; // in bytes
+} machine_mem_obj_t;
+
+extern const mp_obj_type_t machine_mem_type;
+
+extern const machine_mem_obj_t machine_mem8_obj;
+extern const machine_mem_obj_t machine_mem16_obj;
+extern const machine_mem_obj_t machine_mem32_obj;
+
+#if defined(MICROPY_MACHINE_MEM_GET_READ_ADDR)
+uintptr_t MICROPY_MACHINE_MEM_GET_READ_ADDR(mp_obj_t addr_o, uint align);
+#endif
+#if defined(MICROPY_MACHINE_MEM_GET_WRITE_ADDR)
+uintptr_t MICROPY_MACHINE_MEM_GET_WRITE_ADDR(mp_obj_t addr_o, uint align);
+#endif
+
+#endif // MICROPY_INCLUDED_EXTMOD_MACHINE_MEM_H

+ 87 - 0
extmod/machine_pinbase.c

@@ -0,0 +1,87 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Paul Sokolovsky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/mpconfig.h"
+#if MICROPY_PY_MACHINE
+
+#include "py/obj.h"
+#include "py/runtime.h"
+#include "extmod/virtpin.h"
+#include "extmod/machine_pinbase.h"
+
+// PinBase class
+
+// As this is abstract class, its instance is null.
+// But there should be an instance, as the rest of instance code
+// expects that there will be concrete object for inheritance.
+typedef struct _mp_pinbase_t {
+    mp_obj_base_t base;
+} mp_pinbase_t;
+
+STATIC const mp_pinbase_t pinbase_singleton = {
+    .base = { &machine_pinbase_type },
+};
+
+STATIC mp_obj_t pinbase_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+    (void)type;
+    (void)n_args;
+    (void)n_kw;
+    (void)args;
+    return MP_OBJ_FROM_PTR(&pinbase_singleton);
+}
+
+mp_uint_t pinbase_ioctl(mp_obj_t obj, mp_uint_t request, uintptr_t arg, int *errcode);
+mp_uint_t pinbase_ioctl(mp_obj_t obj, mp_uint_t request, uintptr_t arg, int *errcode) {
+    (void)errcode;
+    switch (request) {
+        case MP_PIN_READ: {
+            mp_obj_t dest[2];
+            mp_load_method(obj, MP_QSTR_value, dest);
+            return mp_obj_get_int(mp_call_method_n_kw(0, 0, dest));
+        }
+        case MP_PIN_WRITE: {
+            mp_obj_t dest[3];
+            mp_load_method(obj, MP_QSTR_value, dest);
+            dest[2] = (arg == 0 ? mp_const_false : mp_const_true);
+            mp_call_method_n_kw(1, 0, dest);
+            return 0;
+        }
+    }
+    return -1;
+}
+
+STATIC const mp_pin_p_t pinbase_pin_p = {
+    .ioctl = pinbase_ioctl,
+};
+
+const mp_obj_type_t machine_pinbase_type = {
+    { &mp_type_type },
+    .name = MP_QSTR_PinBase,
+    .make_new = pinbase_make_new,
+    .protocol = &pinbase_pin_p,
+};
+
+#endif // MICROPY_PY_MACHINE

+ 33 - 0
extmod/machine_pinbase.h

@@ -0,0 +1,33 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Paul Sokolovsky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_EXTMOD_MACHINE_PINBASE_H
+#define MICROPY_INCLUDED_EXTMOD_MACHINE_PINBASE_H
+
+#include "py/obj.h"
+
+extern const mp_obj_type_t machine_pinbase_type;
+
+#endif // MICROPY_INCLUDED_EXTMOD_MACHINE_PINBASE_H

+ 65 - 0
extmod/machine_pulse.c

@@ -0,0 +1,65 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/runtime.h"
+#include "py/mperrno.h"
+#include "extmod/machine_pulse.h"
+
+#if MICROPY_PY_MACHINE_PULSE
+
+mp_uint_t machine_time_pulse_us(mp_hal_pin_obj_t pin, int pulse_level, mp_uint_t timeout_us) {
+    mp_uint_t start = mp_hal_ticks_us();
+    while (mp_hal_pin_read(pin) != pulse_level) {
+        if ((mp_uint_t)(mp_hal_ticks_us() - start) >= timeout_us) {
+            return (mp_uint_t)-2;
+        }
+    }
+    start = mp_hal_ticks_us();
+    while (mp_hal_pin_read(pin) == pulse_level) {
+        if ((mp_uint_t)(mp_hal_ticks_us() - start) >= timeout_us) {
+            return (mp_uint_t)-1;
+        }
+    }
+    return mp_hal_ticks_us() - start;
+}
+
+STATIC mp_obj_t machine_time_pulse_us_(size_t n_args, const mp_obj_t *args) {
+    mp_hal_pin_obj_t pin = mp_hal_get_pin_obj(args[0]);
+    int level = 0;
+    if (mp_obj_is_true(args[1])) {
+        level = 1;
+    }
+    mp_uint_t timeout_us = 1000000;
+    if (n_args > 2) {
+        timeout_us = mp_obj_get_int(args[2]);
+    }
+    mp_uint_t us = machine_time_pulse_us(pin, level, timeout_us);
+    // May return -1 or -2 in case of timeout
+    return mp_obj_new_int(us);
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_time_pulse_us_obj, 2, 3, machine_time_pulse_us_);
+
+#endif

+ 36 - 0
extmod/machine_pulse.h

@@ -0,0 +1,36 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_EXTMOD_MACHINE_PULSE_H
+#define MICROPY_INCLUDED_EXTMOD_MACHINE_PULSE_H
+
+#include "py/obj.h"
+#include "py/mphal.h"
+
+mp_uint_t machine_time_pulse_us(mp_hal_pin_obj_t pin, int pulse_level, mp_uint_t timeout_us);
+
+MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(machine_time_pulse_us_obj);
+
+#endif // MICROPY_INCLUDED_EXTMOD_MACHINE_PULSE_H

+ 183 - 0
extmod/machine_signal.c

@@ -0,0 +1,183 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Paul Sokolovsky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/mpconfig.h"
+#if MICROPY_PY_MACHINE
+
+#include <string.h>
+
+#include "py/obj.h"
+#include "py/runtime.h"
+#include "extmod/virtpin.h"
+#include "extmod/machine_signal.h"
+
+// Signal class
+
+typedef struct _machine_signal_t {
+    mp_obj_base_t base;
+    mp_obj_t pin;
+    bool invert;
+} machine_signal_t;
+
+STATIC mp_obj_t signal_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+    mp_obj_t pin = args[0];
+    bool invert = false;
+
+    #if defined(MICROPY_PY_MACHINE_PIN_MAKE_NEW)
+    mp_pin_p_t *pin_p = NULL;
+
+    if (MP_OBJ_IS_OBJ(pin)) {
+        mp_obj_base_t *pin_base = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]);
+        pin_p = (mp_pin_p_t*)pin_base->type->protocol;
+    }
+
+    if (pin_p == NULL) {
+        // If first argument isn't a Pin-like object, we filter out "invert"
+        // from keyword arguments and pass them all to the exported Pin
+        // constructor to create one.
+        mp_obj_t *pin_args = alloca(n_args + n_kw * 2);
+        memcpy(pin_args, args, n_args * sizeof(mp_obj_t));
+        const mp_obj_t *src = args + n_args;
+        mp_obj_t *dst = pin_args + n_args;
+        mp_obj_t *sig_value = NULL;
+        for (size_t cnt = n_kw; cnt; cnt--) {
+            if (*src == MP_OBJ_NEW_QSTR(MP_QSTR_invert)) {
+                invert = mp_obj_is_true(src[1]);
+                n_kw--;
+            } else {
+                *dst++ = *src;
+                *dst++ = src[1];
+            }
+            if (*src == MP_OBJ_NEW_QSTR(MP_QSTR_value)) {
+                // Value is pertained to Signal, so we should invert
+                // it for Pin if needed, and we should do it only when
+                // inversion status is guaranteedly known.
+                sig_value = dst - 1;
+            }
+            src += 2;
+        }
+
+        if (invert && sig_value != NULL) {
+            *sig_value = mp_obj_is_true(*sig_value) ? MP_OBJ_NEW_SMALL_INT(0) : MP_OBJ_NEW_SMALL_INT(1);
+        }
+
+        // Here we pass NULL as a type, hoping that mp_pin_make_new()
+        // will just ignore it as set a concrete type. If not, we'd need
+        // to expose port's "default" pin type too.
+        pin = MICROPY_PY_MACHINE_PIN_MAKE_NEW(NULL, n_args, n_kw, pin_args);
+    }
+    else
+    #endif
+    // Otherwise there should be 1 or 2 args
+    {
+        if (n_args == 1) {
+            if (n_kw == 0) {
+            } else if (n_kw == 1 && args[1] == MP_OBJ_NEW_QSTR(MP_QSTR_invert)) {
+                invert = mp_obj_is_true(args[2]);
+            } else {
+                goto error;
+            }
+        } else {
+        error:
+            mp_raise_TypeError(NULL);
+        }
+    }
+
+    machine_signal_t *o = m_new_obj(machine_signal_t);
+    o->base.type = type;
+    o->pin = pin;
+    o->invert = invert;
+    return MP_OBJ_FROM_PTR(o);
+}
+
+STATIC mp_uint_t signal_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) {
+    (void)errcode;
+    machine_signal_t *self = MP_OBJ_TO_PTR(self_in);
+
+    switch (request) {
+        case MP_PIN_READ: {
+            return mp_virtual_pin_read(self->pin) ^ self->invert;
+        }
+        case MP_PIN_WRITE: {
+            mp_virtual_pin_write(self->pin, arg ^ self->invert);
+            return 0;
+        }
+    }
+    return -1;
+}
+
+// fast method for getting/setting signal value
+STATIC mp_obj_t signal_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+    mp_arg_check_num(n_args, n_kw, 0, 1, false);
+    if (n_args == 0) {
+        // get pin
+        return MP_OBJ_NEW_SMALL_INT(mp_virtual_pin_read(self_in));
+    } else {
+        // set pin
+        mp_virtual_pin_write(self_in, mp_obj_is_true(args[0]));
+        return mp_const_none;
+    }
+}
+
+STATIC mp_obj_t signal_value(size_t n_args, const mp_obj_t *args) {
+    return signal_call(args[0], n_args - 1, 0, args + 1);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(signal_value_obj, 1, 2, signal_value);
+
+STATIC mp_obj_t signal_on(mp_obj_t self_in) {
+    mp_virtual_pin_write(self_in, 1);
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(signal_on_obj, signal_on);
+
+STATIC mp_obj_t signal_off(mp_obj_t self_in) {
+    mp_virtual_pin_write(self_in, 0);
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(signal_off_obj, signal_off);
+
+STATIC const mp_rom_map_elem_t signal_locals_dict_table[] = {
+    { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&signal_value_obj) },
+    { MP_ROM_QSTR(MP_QSTR_on), MP_ROM_PTR(&signal_on_obj) },
+    { MP_ROM_QSTR(MP_QSTR_off), MP_ROM_PTR(&signal_off_obj) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(signal_locals_dict, signal_locals_dict_table);
+
+STATIC const mp_pin_p_t signal_pin_p = {
+    .ioctl = signal_ioctl,
+};
+
+const mp_obj_type_t machine_signal_type = {
+    { &mp_type_type },
+    .name = MP_QSTR_Signal,
+    .make_new = signal_make_new,
+    .call = signal_call,
+    .protocol = &signal_pin_p,
+    .locals_dict = (void*)&signal_locals_dict,
+};
+
+#endif // MICROPY_PY_MACHINE

+ 33 - 0
extmod/machine_signal.h

@@ -0,0 +1,33 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Paul Sokolovsky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_EXTMOD_MACHINE_SIGNAL_H
+#define MICROPY_INCLUDED_EXTMOD_MACHINE_SIGNAL_H
+
+#include "py/obj.h"
+
+extern const mp_obj_type_t machine_signal_type;
+
+#endif // MICROPY_INCLUDED_EXTMOD_MACHINE_SIGNAL_H

+ 339 - 0
extmod/machine_spi.c

@@ -0,0 +1,339 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "py/runtime.h"
+#include "extmod/machine_spi.h"
+
+#if MICROPY_PY_MACHINE_SPI
+
+// if a port didn't define MSB/LSB constants then provide them
+#ifndef MICROPY_PY_MACHINE_SPI_MSB
+#define MICROPY_PY_MACHINE_SPI_MSB (0)
+#define MICROPY_PY_MACHINE_SPI_LSB (1)
+#endif
+
+void mp_machine_soft_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) {
+    mp_machine_soft_spi_obj_t *self = (mp_machine_soft_spi_obj_t*)self_in;
+    uint32_t delay_half = self->delay_half;
+
+    // only MSB transfer is implemented
+
+    // If a port defines MICROPY_PY_MACHINE_SPI_MIN_DELAY, and the configured
+    // delay_half is equal to this value, then the software SPI implementation
+    // will run as fast as possible, limited only by CPU speed and GPIO time.
+    #ifdef MICROPY_PY_MACHINE_SPI_MIN_DELAY
+    if (delay_half == MICROPY_PY_MACHINE_SPI_MIN_DELAY) {
+        for (size_t i = 0; i < len; ++i) {
+            uint8_t data_out = src[i];
+            uint8_t data_in = 0;
+            for (int j = 0; j < 8; ++j, data_out <<= 1) {
+                mp_hal_pin_write(self->mosi, (data_out >> 7) & 1);
+                mp_hal_pin_write(self->sck, 1 - self->polarity);
+                data_in = (data_in << 1) | mp_hal_pin_read(self->miso);
+                mp_hal_pin_write(self->sck, self->polarity);
+            }
+            if (dest != NULL) {
+                dest[i] = data_in;
+            }
+        }
+        return;
+    }
+    #endif
+
+    for (size_t i = 0; i < len; ++i) {
+        uint8_t data_out = src[i];
+        uint8_t data_in = 0;
+        for (int j = 0; j < 8; ++j, data_out <<= 1) {
+            mp_hal_pin_write(self->mosi, (data_out >> 7) & 1);
+            if (self->phase == 0) {
+                mp_hal_delay_us_fast(delay_half);
+                mp_hal_pin_write(self->sck, 1 - self->polarity);
+            } else {
+                mp_hal_pin_write(self->sck, 1 - self->polarity);
+                mp_hal_delay_us_fast(delay_half);
+            }
+            data_in = (data_in << 1) | mp_hal_pin_read(self->miso);
+            if (self->phase == 0) {
+                mp_hal_delay_us_fast(delay_half);
+                mp_hal_pin_write(self->sck, self->polarity);
+            } else {
+                mp_hal_pin_write(self->sck, self->polarity);
+                mp_hal_delay_us_fast(delay_half);
+            }
+        }
+        if (dest != NULL) {
+            dest[i] = data_in;
+        }
+    }
+}
+
+/******************************************************************************/
+// MicroPython bindings for generic machine.SPI
+
+STATIC mp_obj_t mp_machine_soft_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args);
+
+mp_obj_t mp_machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+    // check the id argument, if given
+    if (n_args > 0) {
+        if (args[0] != MP_OBJ_NEW_SMALL_INT(-1)) {
+            #if defined(MICROPY_PY_MACHINE_SPI_MAKE_NEW)
+            // dispatch to port-specific constructor
+            extern mp_obj_t MICROPY_PY_MACHINE_SPI_MAKE_NEW(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args);
+            return MICROPY_PY_MACHINE_SPI_MAKE_NEW(type, n_args, n_kw, args);
+            #else
+            mp_raise_ValueError("invalid SPI peripheral");
+            #endif
+        }
+        --n_args;
+        ++args;
+    }
+
+    // software SPI
+    return mp_machine_soft_spi_make_new(type, n_args, n_kw, args);
+}
+
+STATIC mp_obj_t machine_spi_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
+    mp_obj_base_t *s = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]);
+    mp_machine_spi_p_t *spi_p = (mp_machine_spi_p_t*)s->type->protocol;
+    spi_p->init(s, n_args - 1, args + 1, kw_args);
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_spi_init_obj, 1, machine_spi_init);
+
+STATIC mp_obj_t machine_spi_deinit(mp_obj_t self) {
+    mp_obj_base_t *s = (mp_obj_base_t*)MP_OBJ_TO_PTR(self);
+    mp_machine_spi_p_t *spi_p = (mp_machine_spi_p_t*)s->type->protocol;
+    if (spi_p->deinit != NULL) {
+        spi_p->deinit(s);
+    }
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_spi_deinit_obj, machine_spi_deinit);
+
+STATIC void mp_machine_spi_transfer(mp_obj_t self, size_t len, const void *src, void *dest) {
+    mp_obj_base_t *s = (mp_obj_base_t*)MP_OBJ_TO_PTR(self);
+    mp_machine_spi_p_t *spi_p = (mp_machine_spi_p_t*)s->type->protocol;
+    spi_p->transfer(s, len, src, dest);
+}
+
+STATIC mp_obj_t mp_machine_spi_read(size_t n_args, const mp_obj_t *args) {
+    vstr_t vstr;
+    vstr_init_len(&vstr, mp_obj_get_int(args[1]));
+    memset(vstr.buf, n_args == 3 ? mp_obj_get_int(args[2]) : 0, vstr.len);
+    mp_machine_spi_transfer(args[0], vstr.len, vstr.buf, vstr.buf);
+    return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_read_obj, 2, 3, mp_machine_spi_read);
+
+STATIC mp_obj_t mp_machine_spi_readinto(size_t n_args, const mp_obj_t *args) {
+    mp_buffer_info_t bufinfo;
+    mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_WRITE);
+    memset(bufinfo.buf, n_args == 3 ? mp_obj_get_int(args[2]) : 0, bufinfo.len);
+    mp_machine_spi_transfer(args[0], bufinfo.len, bufinfo.buf, bufinfo.buf);
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_readinto_obj, 2, 3, mp_machine_spi_readinto);
+
+STATIC mp_obj_t mp_machine_spi_write(mp_obj_t self, mp_obj_t wr_buf) {
+    mp_buffer_info_t src;
+    mp_get_buffer_raise(wr_buf, &src, MP_BUFFER_READ);
+    mp_machine_spi_transfer(self, src.len, (const uint8_t*)src.buf, NULL);
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_2(mp_machine_spi_write_obj, mp_machine_spi_write);
+
+STATIC mp_obj_t mp_machine_spi_write_readinto(mp_obj_t self, mp_obj_t wr_buf, mp_obj_t rd_buf) {
+    mp_buffer_info_t src;
+    mp_get_buffer_raise(wr_buf, &src, MP_BUFFER_READ);
+    mp_buffer_info_t dest;
+    mp_get_buffer_raise(rd_buf, &dest, MP_BUFFER_WRITE);
+    if (src.len != dest.len) {
+        mp_raise_ValueError("buffers must be the same length");
+    }
+    mp_machine_spi_transfer(self, src.len, src.buf, dest.buf);
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_3(mp_machine_spi_write_readinto_obj, mp_machine_spi_write_readinto);
+
+STATIC const mp_rom_map_elem_t machine_spi_locals_dict_table[] = {
+    { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_spi_init_obj) },
+    { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_spi_deinit_obj) },
+    { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_machine_spi_read_obj) },
+    { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_machine_spi_readinto_obj) },
+    { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_machine_spi_write_obj) },
+    { MP_ROM_QSTR(MP_QSTR_write_readinto), MP_ROM_PTR(&mp_machine_spi_write_readinto_obj) },
+
+    { MP_ROM_QSTR(MP_QSTR_MSB), MP_ROM_INT(MICROPY_PY_MACHINE_SPI_MSB) },
+    { MP_ROM_QSTR(MP_QSTR_LSB), MP_ROM_INT(MICROPY_PY_MACHINE_SPI_LSB) },
+};
+
+MP_DEFINE_CONST_DICT(mp_machine_spi_locals_dict, machine_spi_locals_dict_table);
+
+/******************************************************************************/
+// Implementation of soft SPI
+
+STATIC uint32_t baudrate_from_delay_half(uint32_t delay_half) {
+    #ifdef MICROPY_PY_MACHINE_SPI_MIN_DELAY
+    if (delay_half == MICROPY_PY_MACHINE_SPI_MIN_DELAY) {
+        return MICROPY_PY_MACHINE_SPI_MAX_BAUDRATE;
+    } else
+    #endif
+    {
+        return 500000 / delay_half;
+    }
+}
+
+STATIC uint32_t baudrate_to_delay_half(uint32_t baudrate) {
+    #ifdef MICROPY_PY_MACHINE_SPI_MIN_DELAY
+    if (baudrate >= MICROPY_PY_MACHINE_SPI_MAX_BAUDRATE) {
+        return MICROPY_PY_MACHINE_SPI_MIN_DELAY;
+    } else
+    #endif
+    {
+        uint32_t delay_half = 500000 / baudrate;
+        // round delay_half up so that: actual_baudrate <= requested_baudrate
+        if (500000 % baudrate != 0) {
+            delay_half += 1;
+        }
+        return delay_half;
+    }
+}
+
+STATIC void mp_machine_soft_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
+    mp_machine_soft_spi_obj_t *self = MP_OBJ_TO_PTR(self_in);
+    mp_printf(print, "SoftSPI(baudrate=%u, polarity=%u, phase=%u,"
+        " sck=" MP_HAL_PIN_FMT ", mosi=" MP_HAL_PIN_FMT ", miso=" MP_HAL_PIN_FMT ")",
+        baudrate_from_delay_half(self->delay_half), self->polarity, self->phase,
+        mp_hal_pin_name(self->sck), mp_hal_pin_name(self->mosi), mp_hal_pin_name(self->miso));
+}
+
+STATIC mp_obj_t mp_machine_soft_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
+    enum { ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit, ARG_sck, ARG_mosi, ARG_miso };
+    static const mp_arg_t allowed_args[] = {
+        { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 500000} },
+        { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
+        { MP_QSTR_phase,    MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
+        { MP_QSTR_bits,     MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} },
+        { MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = MICROPY_PY_MACHINE_SPI_MSB} },
+        { MP_QSTR_sck,      MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
+        { MP_QSTR_mosi,     MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
+        { MP_QSTR_miso,     MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
+    };
+    mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+    mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+    // create new object
+    mp_machine_soft_spi_obj_t *self = m_new_obj(mp_machine_soft_spi_obj_t);
+    self->base.type = &mp_machine_soft_spi_type;
+
+    // set parameters
+    self->delay_half = baudrate_to_delay_half(args[ARG_baudrate].u_int);
+    self->polarity = args[ARG_polarity].u_int;
+    self->phase = args[ARG_phase].u_int;
+    if (args[ARG_bits].u_int != 8) {
+        mp_raise_ValueError("bits must be 8");
+    }
+    if (args[ARG_firstbit].u_int != MICROPY_PY_MACHINE_SPI_MSB) {
+        mp_raise_ValueError("firstbit must be MSB");
+    }
+    if (args[ARG_sck].u_obj == MP_OBJ_NULL
+        || args[ARG_mosi].u_obj == MP_OBJ_NULL
+        || args[ARG_miso].u_obj == MP_OBJ_NULL) {
+        mp_raise_ValueError("must specify all of sck/mosi/miso");
+    }
+    self->sck = mp_hal_get_pin_obj(args[ARG_sck].u_obj);
+    self->mosi = mp_hal_get_pin_obj(args[ARG_mosi].u_obj);
+    self->miso = mp_hal_get_pin_obj(args[ARG_miso].u_obj);
+
+    // configure pins
+    mp_hal_pin_write(self->sck, self->polarity);
+    mp_hal_pin_output(self->sck);
+    mp_hal_pin_output(self->mosi);
+    mp_hal_pin_input(self->miso);
+
+    return MP_OBJ_FROM_PTR(self);
+}
+
+STATIC void mp_machine_soft_spi_init(mp_obj_base_t *self_in, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+    mp_machine_soft_spi_obj_t *self = (mp_machine_soft_spi_obj_t*)self_in;
+
+    enum { ARG_baudrate, ARG_polarity, ARG_phase, ARG_sck, ARG_mosi, ARG_miso };
+    static const mp_arg_t allowed_args[] = {
+        { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = -1} },
+        { MP_QSTR_polarity, MP_ARG_INT, {.u_int = -1} },
+        { MP_QSTR_phase, MP_ARG_INT, {.u_int = -1} },
+        { MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
+        { MP_QSTR_mosi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
+        { MP_QSTR_miso, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
+    };
+    mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+    mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+    if (args[ARG_baudrate].u_int != -1) {
+        self->delay_half = baudrate_to_delay_half(args[ARG_baudrate].u_int);
+    }
+    if (args[ARG_polarity].u_int != -1) {
+        self->polarity = args[ARG_polarity].u_int;
+    }
+    if (args[ARG_phase].u_int != -1) {
+        self->phase = args[ARG_phase].u_int;
+    }
+    if (args[ARG_sck].u_obj != MP_OBJ_NULL) {
+        self->sck = mp_hal_get_pin_obj(args[ARG_sck].u_obj);
+    }
+    if (args[ARG_mosi].u_obj != MP_OBJ_NULL) {
+        self->mosi = mp_hal_get_pin_obj(args[ARG_mosi].u_obj);
+    }
+    if (args[ARG_miso].u_obj != MP_OBJ_NULL) {
+        self->miso = mp_hal_get_pin_obj(args[ARG_miso].u_obj);
+    }
+
+    // configure pins
+    mp_hal_pin_write(self->sck, self->polarity);
+    mp_hal_pin_output(self->sck);
+    mp_hal_pin_output(self->mosi);
+    mp_hal_pin_input(self->miso);
+}
+
+STATIC const mp_machine_spi_p_t mp_machine_soft_spi_p = {
+    .init = mp_machine_soft_spi_init,
+    .deinit = NULL,
+    .transfer = mp_machine_soft_spi_transfer,
+};
+
+const mp_obj_type_t mp_machine_soft_spi_type = {
+    { &mp_type_type },
+    .name = MP_QSTR_SoftSPI,
+    .print = mp_machine_soft_spi_print,
+    .make_new = mp_machine_spi_make_new, // delegate to master constructor
+    .protocol = &mp_machine_soft_spi_p,
+    .locals_dict = (mp_obj_dict_t*)&mp_machine_spi_locals_dict,
+};
+
+#endif // MICROPY_PY_MACHINE_SPI

+ 61 - 0
extmod/machine_spi.h

@@ -0,0 +1,61 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_EXTMOD_MACHINE_SPI_H
+#define MICROPY_INCLUDED_EXTMOD_MACHINE_SPI_H
+
+#include "py/obj.h"
+#include "py/mphal.h"
+
+// SPI protocol
+typedef struct _mp_machine_spi_p_t {
+    void (*init)(mp_obj_base_t *obj, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
+    void (*deinit)(mp_obj_base_t *obj); // can be NULL
+    void (*transfer)(mp_obj_base_t *obj, size_t len, const uint8_t *src, uint8_t *dest);
+} mp_machine_spi_p_t;
+
+typedef struct _mp_machine_soft_spi_obj_t {
+    mp_obj_base_t base;
+    uint32_t delay_half; // microsecond delay for half SCK period
+    uint8_t polarity;
+    uint8_t phase;
+    mp_hal_pin_obj_t sck;
+    mp_hal_pin_obj_t mosi;
+    mp_hal_pin_obj_t miso;
+} mp_machine_soft_spi_obj_t;
+
+extern const mp_obj_type_t mp_machine_soft_spi_type;
+extern const mp_obj_dict_t mp_machine_spi_locals_dict;
+
+void mp_machine_soft_spi_transfer(mp_obj_base_t *self, size_t len, const uint8_t *src, uint8_t *dest);
+
+mp_obj_t mp_machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args);
+
+MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_read_obj);
+MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_readinto_obj);
+MP_DECLARE_CONST_FUN_OBJ_2(mp_machine_spi_write_obj);
+MP_DECLARE_CONST_FUN_OBJ_3(mp_machine_spi_write_readinto_obj);
+
+#endif // MICROPY_INCLUDED_EXTMOD_MACHINE_SPI_H

+ 45 - 0
extmod/misc.h

@@ -0,0 +1,45 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014-2016 Damien P. George
+ * Copyright (c) 2016 Paul Sokolovsky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_EXTMOD_MISC_H
+#define MICROPY_INCLUDED_EXTMOD_MISC_H
+
+// This file contains cumulative declarations for extmod/ .
+
+#include <stddef.h>
+#include "py/runtime.h"
+
+MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_uos_dupterm_obj);
+
+#if MICROPY_PY_OS_DUPTERM
+int mp_uos_dupterm_rx_chr(void);
+void mp_uos_dupterm_tx_strn(const char *str, size_t len);
+void mp_uos_deactivate(size_t dupterm_idx, const char *msg, mp_obj_t exc);
+#else
+#define mp_uos_dupterm_tx_strn(s, l)
+#endif
+
+#endif // MICROPY_INCLUDED_EXTMOD_MISC_H

+ 594 - 0
extmod/modframebuf.c

@@ -0,0 +1,594 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "py/runtime.h"
+
+#if MICROPY_PY_FRAMEBUF
+
+#include "ports/stm32/font_petme128_8x8.h"
+
+typedef struct _mp_obj_framebuf_t {
+    mp_obj_base_t base;
+    mp_obj_t buf_obj; // need to store this to prevent GC from reclaiming buf
+    void *buf;
+    uint16_t width, height, stride;
+    uint8_t format;
+} mp_obj_framebuf_t;
+
+typedef void (*setpixel_t)(const mp_obj_framebuf_t*, int, int, uint32_t);
+typedef uint32_t (*getpixel_t)(const mp_obj_framebuf_t*, int, int);
+typedef void (*fill_rect_t)(const mp_obj_framebuf_t *, int, int, int, int, uint32_t);
+
+typedef struct _mp_framebuf_p_t {
+    setpixel_t setpixel;
+    getpixel_t getpixel;
+    fill_rect_t fill_rect;
+} mp_framebuf_p_t;
+
+// constants for formats
+#define FRAMEBUF_MVLSB    (0)
+#define FRAMEBUF_RGB565   (1)
+#define FRAMEBUF_GS4_HMSB (2)
+#define FRAMEBUF_MHLSB    (3)
+#define FRAMEBUF_MHMSB    (4)
+
+// Functions for MHLSB and MHMSB
+
+STATIC void mono_horiz_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) {
+    size_t index = (x + y * fb->stride) >> 3;
+    int offset = fb->format == FRAMEBUF_MHMSB ? x & 0x07 : 7 - (x & 0x07);
+    ((uint8_t*)fb->buf)[index] = (((uint8_t*)fb->buf)[index] & ~(0x01 << offset)) | ((col != 0) << offset);
+}
+
+STATIC uint32_t mono_horiz_getpixel(const mp_obj_framebuf_t *fb, int x, int y) {
+    size_t index = (x + y * fb->stride) >> 3;
+    int offset = fb->format == FRAMEBUF_MHMSB ? x & 0x07 : 7 - (x & 0x07);
+    return (((uint8_t*)fb->buf)[index] >> (offset)) & 0x01;
+}
+
+STATIC void mono_horiz_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) {
+    int reverse = fb->format == FRAMEBUF_MHMSB;
+    int advance = fb->stride >> 3;
+    while (w--) {
+        uint8_t *b = &((uint8_t*)fb->buf)[(x >> 3) + y * advance];
+        int offset = reverse ?  x & 7 : 7 - (x & 7);
+        for (int hh = h; hh; --hh) {
+            *b = (*b & ~(0x01 << offset)) | ((col != 0) << offset);
+            b += advance;
+        }
+        ++x;
+    }
+}
+
+// Functions for MVLSB format
+
+STATIC void mvlsb_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) {
+    size_t index = (y >> 3) * fb->stride + x;
+    uint8_t offset = y & 0x07;
+    ((uint8_t*)fb->buf)[index] = (((uint8_t*)fb->buf)[index] & ~(0x01 << offset)) | ((col != 0) << offset);
+}
+
+STATIC uint32_t mvlsb_getpixel(const mp_obj_framebuf_t *fb, int x, int y) {
+    return (((uint8_t*)fb->buf)[(y >> 3) * fb->stride + x] >> (y & 0x07)) & 0x01;
+}
+
+STATIC void mvlsb_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) {
+    while (h--) {
+        uint8_t *b = &((uint8_t*)fb->buf)[(y >> 3) * fb->stride + x];
+        uint8_t offset = y & 0x07;
+        for (int ww = w; ww; --ww) {
+            *b = (*b & ~(0x01 << offset)) | ((col != 0) << offset);
+            ++b;
+        }
+        ++y;
+    }
+}
+
+// Functions for RGB565 format
+
+STATIC void rgb565_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) {
+    ((uint16_t*)fb->buf)[x + y * fb->stride] = col;
+}
+
+STATIC uint32_t rgb565_getpixel(const mp_obj_framebuf_t *fb, int x, int y) {
+    return ((uint16_t*)fb->buf)[x + y * fb->stride];
+}
+
+STATIC void rgb565_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) {
+    uint16_t *b = &((uint16_t*)fb->buf)[x + y * fb->stride];
+    while (h--) {
+        for (int ww = w; ww; --ww) {
+            *b++ = col;
+        }
+        b += fb->stride - w;
+    }
+}
+
+// Functions for GS4_HMSB format
+
+STATIC void gs4_hmsb_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) {
+    uint8_t *pixel = &((uint8_t*)fb->buf)[(x + y * fb->stride) >> 1];
+
+    if (x % 2) {
+        *pixel = ((uint8_t)col & 0x0f) | (*pixel & 0xf0);
+    } else {
+        *pixel = ((uint8_t)col << 4) | (*pixel & 0x0f);
+    }
+}
+
+STATIC uint32_t gs4_hmsb_getpixel(const mp_obj_framebuf_t *fb, int x, int y) {
+    if (x % 2) {
+        return ((uint8_t*)fb->buf)[(x + y * fb->stride) >> 1] & 0x0f;
+    }
+
+    return ((uint8_t*)fb->buf)[(x + y * fb->stride) >> 1] >> 4;
+}
+
+STATIC void gs4_hmsb_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) {
+    col &= 0x0f;
+    uint8_t *pixel_pair = &((uint8_t*)fb->buf)[(x + y * fb->stride) >> 1];
+    uint8_t col_shifted_left = col << 4;
+    uint8_t col_pixel_pair = col_shifted_left | col;
+    int pixel_count_till_next_line = (fb->stride - w) >> 1;
+    bool odd_x = (x % 2 == 1);
+
+    while (h--) {
+        int ww = w;
+
+        if (odd_x && ww > 0) {
+            *pixel_pair = (*pixel_pair & 0xf0) | col;
+            pixel_pair++;
+            ww--;
+        }
+
+        memset(pixel_pair, col_pixel_pair, ww >> 1);
+        pixel_pair += ww >> 1;
+
+        if (ww % 2) {
+            *pixel_pair = col_shifted_left | (*pixel_pair & 0x0f);
+            if (!odd_x) {
+                pixel_pair++;
+            }
+        }
+
+        pixel_pair += pixel_count_till_next_line;
+    }
+}
+
+STATIC mp_framebuf_p_t formats[] = {
+    [FRAMEBUF_MVLSB] = {mvlsb_setpixel, mvlsb_getpixel, mvlsb_fill_rect},
+    [FRAMEBUF_RGB565] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect},
+    [FRAMEBUF_GS4_HMSB] = {gs4_hmsb_setpixel, gs4_hmsb_getpixel, gs4_hmsb_fill_rect},
+    [FRAMEBUF_MHLSB] = {mono_horiz_setpixel, mono_horiz_getpixel, mono_horiz_fill_rect},
+    [FRAMEBUF_MHMSB] = {mono_horiz_setpixel, mono_horiz_getpixel, mono_horiz_fill_rect},
+};
+
+static inline void setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) {
+    formats[fb->format].setpixel(fb, x, y, col);
+}
+
+static inline uint32_t getpixel(const mp_obj_framebuf_t *fb, int x, int y) {
+    return formats[fb->format].getpixel(fb, x, y);
+}
+
+STATIC void fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) {
+    if (h < 1 || w < 1 || x + w <= 0 || y + h <= 0 || y >= fb->height || x >= fb->width) {
+        // No operation needed.
+        return;
+    }
+
+    // clip to the framebuffer
+    int xend = MIN(fb->width, x + w);
+    int yend = MIN(fb->height, y + h);
+    x = MAX(x, 0);
+    y = MAX(y, 0);
+
+    formats[fb->format].fill_rect(fb, x, y, xend - x, yend - y, col);
+}
+
+STATIC mp_obj_t framebuf_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+    mp_arg_check_num(n_args, n_kw, 4, 5, false);
+
+    mp_obj_framebuf_t *o = m_new_obj(mp_obj_framebuf_t);
+    o->base.type = type;
+    o->buf_obj = args[0];
+
+    mp_buffer_info_t bufinfo;
+    mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_WRITE);
+    o->buf = bufinfo.buf;
+
+    o->width = mp_obj_get_int(args[1]);
+    o->height = mp_obj_get_int(args[2]);
+    o->format = mp_obj_get_int(args[3]);
+    if (n_args >= 5) {
+        o->stride = mp_obj_get_int(args[4]);
+    } else {
+        o->stride = o->width;
+    }
+
+    switch (o->format) {
+        case FRAMEBUF_MVLSB:
+        case FRAMEBUF_RGB565:
+            break;
+        case FRAMEBUF_MHLSB:
+        case FRAMEBUF_MHMSB:
+            o->stride = (o->stride + 7) & ~7;
+            break;
+        case FRAMEBUF_GS4_HMSB:
+            o->stride = (o->stride + 1) & ~1;
+            break;
+        default:
+            mp_raise_ValueError("invalid format");
+    }
+
+    return MP_OBJ_FROM_PTR(o);
+}
+
+STATIC mp_int_t framebuf_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) {
+    (void)flags;
+    mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in);
+    bufinfo->buf = self->buf;
+    bufinfo->len = self->stride * self->height * (self->format == FRAMEBUF_RGB565 ? 2 : 1);
+    bufinfo->typecode = 'B'; // view framebuf as bytes
+    return 0;
+}
+
+STATIC mp_obj_t framebuf_fill(mp_obj_t self_in, mp_obj_t col_in) {
+    mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in);
+    mp_int_t col = mp_obj_get_int(col_in);
+    formats[self->format].fill_rect(self, 0, 0, self->width, self->height, col);
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(framebuf_fill_obj, framebuf_fill);
+
+STATIC mp_obj_t framebuf_fill_rect(size_t n_args, const mp_obj_t *args) {
+    (void)n_args;
+
+    mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]);
+    mp_int_t x = mp_obj_get_int(args[1]);
+    mp_int_t y = mp_obj_get_int(args[2]);
+    mp_int_t width = mp_obj_get_int(args[3]);
+    mp_int_t height = mp_obj_get_int(args[4]);
+    mp_int_t col = mp_obj_get_int(args[5]);
+
+    fill_rect(self, x, y, width, height, col);
+
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_fill_rect_obj, 6, 6, framebuf_fill_rect);
+
+STATIC mp_obj_t framebuf_pixel(size_t n_args, const mp_obj_t *args) {
+    mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]);
+    mp_int_t x = mp_obj_get_int(args[1]);
+    mp_int_t y = mp_obj_get_int(args[2]);
+    if (0 <= x && x < self->width && 0 <= y && y < self->height) {
+        if (n_args == 3) {
+            // get
+            return MP_OBJ_NEW_SMALL_INT(getpixel(self, x, y));
+        } else {
+            // set
+            setpixel(self, x, y, mp_obj_get_int(args[3]));
+        }
+    }
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_pixel_obj, 3, 4, framebuf_pixel);
+
+STATIC mp_obj_t framebuf_hline(size_t n_args, const mp_obj_t *args) {
+    (void)n_args;
+
+    mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]);
+    mp_int_t x = mp_obj_get_int(args[1]);
+    mp_int_t y = mp_obj_get_int(args[2]);
+    mp_int_t w = mp_obj_get_int(args[3]);
+    mp_int_t col = mp_obj_get_int(args[4]);
+
+    fill_rect(self, x, y, w, 1, col);
+
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_hline_obj, 5, 5, framebuf_hline);
+
+STATIC mp_obj_t framebuf_vline(size_t n_args, const mp_obj_t *args) {
+    (void)n_args;
+
+    mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]);
+    mp_int_t x = mp_obj_get_int(args[1]);
+    mp_int_t y = mp_obj_get_int(args[2]);
+    mp_int_t h = mp_obj_get_int(args[3]);
+    mp_int_t col = mp_obj_get_int(args[4]);
+
+    fill_rect(self, x, y, 1, h, col);
+
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_vline_obj, 5, 5, framebuf_vline);
+
+STATIC mp_obj_t framebuf_rect(size_t n_args, const mp_obj_t *args) {
+    (void)n_args;
+
+    mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]);
+    mp_int_t x = mp_obj_get_int(args[1]);
+    mp_int_t y = mp_obj_get_int(args[2]);
+    mp_int_t w = mp_obj_get_int(args[3]);
+    mp_int_t h = mp_obj_get_int(args[4]);
+    mp_int_t col = mp_obj_get_int(args[5]);
+
+    fill_rect(self, x, y, w, 1, col);
+    fill_rect(self, x, y + h- 1, w, 1, col);
+    fill_rect(self, x, y, 1, h, col);
+    fill_rect(self, x + w- 1, y, 1, h, col);
+
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_rect_obj, 6, 6, framebuf_rect);
+
+STATIC mp_obj_t framebuf_line(size_t n_args, const mp_obj_t *args) {
+    (void)n_args;
+
+    mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]);
+    mp_int_t x1 = mp_obj_get_int(args[1]);
+    mp_int_t y1 = mp_obj_get_int(args[2]);
+    mp_int_t x2 = mp_obj_get_int(args[3]);
+    mp_int_t y2 = mp_obj_get_int(args[4]);
+    mp_int_t col = mp_obj_get_int(args[5]);
+
+    mp_int_t dx = x2 - x1;
+    mp_int_t sx;
+    if (dx > 0) {
+        sx = 1;
+    } else {
+        dx = -dx;
+        sx = -1;
+    }
+
+    mp_int_t dy = y2 - y1;
+    mp_int_t sy;
+    if (dy > 0) {
+        sy = 1;
+    } else {
+        dy = -dy;
+        sy = -1;
+    }
+
+    bool steep;
+    if (dy > dx) {
+        mp_int_t temp;
+        temp = x1; x1 = y1; y1 = temp;
+        temp = dx; dx = dy; dy = temp;
+        temp = sx; sx = sy; sy = temp;
+        steep = true;
+    } else {
+        steep = false;
+    }
+
+    mp_int_t e = 2 * dy - dx;
+    for (mp_int_t i = 0; i < dx; ++i) {
+        if (steep) {
+            if (0 <= y1 && y1 < self->width && 0 <= x1 && x1 < self->height) {
+                setpixel(self, y1, x1, col);
+            }
+        } else {
+            if (0 <= x1 && x1 < self->width && 0 <= y1 && y1 < self->height) {
+                setpixel(self, x1, y1, col);
+            }
+        }
+        while (e >= 0) {
+            y1 += sy;
+            e -= 2 * dx;
+        }
+        x1 += sx;
+        e += 2 * dy;
+    }
+
+    if (0 <= x2 && x2 < self->width && 0 <= y2 && y2 < self->height) {
+        setpixel(self, x2, y2, col);
+    }
+
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_line_obj, 6, 6, framebuf_line);
+
+STATIC mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args) {
+    mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]);
+    mp_obj_framebuf_t *source = MP_OBJ_TO_PTR(args[1]);
+    mp_int_t x = mp_obj_get_int(args[2]);
+    mp_int_t y = mp_obj_get_int(args[3]);
+    mp_int_t key = -1;
+    if (n_args > 4) {
+        key = mp_obj_get_int(args[4]);
+    }
+
+    if (
+        (x >= self->width) ||
+        (y >= self->height) ||
+        (-x >= source->width) ||
+        (-y >= source->height)
+    ) {
+        // Out of bounds, no-op.
+        return mp_const_none;
+    }
+
+    // Clip.
+    int x0 = MAX(0, x);
+    int y0 = MAX(0, y);
+    int x1 = MAX(0, -x);
+    int y1 = MAX(0, -y);
+    int x0end = MIN(self->width, x + source->width);
+    int y0end = MIN(self->height, y + source->height);
+
+    for (; y0 < y0end; ++y0) {
+        int cx1 = x1;
+        for (int cx0 = x0; cx0 < x0end; ++cx0) {
+            uint32_t col = getpixel(source, cx1, y1);
+            if (col != (uint32_t)key) {
+                setpixel(self, cx0, y0, col);
+            }
+            ++cx1;
+        }
+        ++y1;
+    }
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_blit_obj, 4, 5, framebuf_blit);
+
+STATIC mp_obj_t framebuf_scroll(mp_obj_t self_in, mp_obj_t xstep_in, mp_obj_t ystep_in) {
+    mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in);
+    mp_int_t xstep = mp_obj_get_int(xstep_in);
+    mp_int_t ystep = mp_obj_get_int(ystep_in);
+    int sx, y, xend, yend, dx, dy;
+    if (xstep < 0) {
+        sx = 0;
+        xend = self->width + xstep;
+        dx = 1;
+    } else {
+        sx = self->width - 1;
+        xend = xstep - 1;
+        dx = -1;
+    }
+    if (ystep < 0) {
+        y = 0;
+        yend = self->height + ystep;
+        dy = 1;
+    } else {
+        y = self->height - 1;
+        yend = ystep - 1;
+        dy = -1;
+    }
+    for (; y != yend; y += dy) {
+        for (int x = sx; x != xend; x += dx) {
+            setpixel(self, x, y, getpixel(self, x - xstep, y - ystep));
+        }
+    }
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_3(framebuf_scroll_obj, framebuf_scroll);
+
+STATIC mp_obj_t framebuf_text(size_t n_args, const mp_obj_t *args) {
+    // extract arguments
+    mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]);
+    const char *str = mp_obj_str_get_str(args[1]);
+    mp_int_t x0 = mp_obj_get_int(args[2]);
+    mp_int_t y0 = mp_obj_get_int(args[3]);
+    mp_int_t col = 1;
+    if (n_args >= 5) {
+        col = mp_obj_get_int(args[4]);
+    }
+
+    // loop over chars
+    for (; *str; ++str) {
+        // get char and make sure its in range of font
+        int chr = *(uint8_t*)str;
+        if (chr < 32 || chr > 127) {
+            chr = 127;
+        }
+        // get char data
+        const uint8_t *chr_data = &font_petme128_8x8[(chr - 32) * 8];
+        // loop over char data
+        for (int j = 0; j < 8; j++, x0++) {
+            if (0 <= x0 && x0 < self->width) { // clip x
+                uint vline_data = chr_data[j]; // each byte is a column of 8 pixels, LSB at top
+                for (int y = y0; vline_data; vline_data >>= 1, y++) { // scan over vertical column
+                    if (vline_data & 1) { // only draw if pixel set
+                        if (0 <= y && y < self->height) { // clip y
+                            setpixel(self, x0, y, col);
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_text_obj, 4, 5, framebuf_text);
+
+STATIC const mp_rom_map_elem_t framebuf_locals_dict_table[] = {
+    { MP_ROM_QSTR(MP_QSTR_fill), MP_ROM_PTR(&framebuf_fill_obj) },
+    { MP_ROM_QSTR(MP_QSTR_fill_rect), MP_ROM_PTR(&framebuf_fill_rect_obj) },
+    { MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&framebuf_pixel_obj) },
+    { MP_ROM_QSTR(MP_QSTR_hline), MP_ROM_PTR(&framebuf_hline_obj) },
+    { MP_ROM_QSTR(MP_QSTR_vline), MP_ROM_PTR(&framebuf_vline_obj) },
+    { MP_ROM_QSTR(MP_QSTR_rect), MP_ROM_PTR(&framebuf_rect_obj) },
+    { MP_ROM_QSTR(MP_QSTR_line), MP_ROM_PTR(&framebuf_line_obj) },
+    { MP_ROM_QSTR(MP_QSTR_blit), MP_ROM_PTR(&framebuf_blit_obj) },
+    { MP_ROM_QSTR(MP_QSTR_scroll), MP_ROM_PTR(&framebuf_scroll_obj) },
+    { MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&framebuf_text_obj) },
+};
+STATIC MP_DEFINE_CONST_DICT(framebuf_locals_dict, framebuf_locals_dict_table);
+
+STATIC const mp_obj_type_t mp_type_framebuf = {
+    { &mp_type_type },
+    .name = MP_QSTR_FrameBuffer,
+    .make_new = framebuf_make_new,
+    .buffer_p = { .get_buffer = framebuf_get_buffer },
+    .locals_dict = (mp_obj_dict_t*)&framebuf_locals_dict,
+};
+
+// this factory function is provided for backwards compatibility with old FrameBuffer1 class
+STATIC mp_obj_t legacy_framebuffer1(size_t n_args, const mp_obj_t *args) {
+    mp_obj_framebuf_t *o = m_new_obj(mp_obj_framebuf_t);
+    o->base.type = &mp_type_framebuf;
+
+    mp_buffer_info_t bufinfo;
+    mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_WRITE);
+    o->buf = bufinfo.buf;
+
+    o->width = mp_obj_get_int(args[1]);
+    o->height = mp_obj_get_int(args[2]);
+    o->format = FRAMEBUF_MVLSB;
+    if (n_args >= 4) {
+        o->stride = mp_obj_get_int(args[3]);
+    } else {
+        o->stride = o->width;
+    }
+
+    return MP_OBJ_FROM_PTR(o);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(legacy_framebuffer1_obj, 3, 4, legacy_framebuffer1);
+
+STATIC const mp_rom_map_elem_t framebuf_module_globals_table[] = {
+    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_framebuf) },
+    { MP_ROM_QSTR(MP_QSTR_FrameBuffer), MP_ROM_PTR(&mp_type_framebuf) },
+    { MP_ROM_QSTR(MP_QSTR_FrameBuffer1), MP_ROM_PTR(&legacy_framebuffer1_obj) },
+    { MP_ROM_QSTR(MP_QSTR_MVLSB), MP_ROM_INT(FRAMEBUF_MVLSB) },
+    { MP_ROM_QSTR(MP_QSTR_MONO_VLSB), MP_ROM_INT(FRAMEBUF_MVLSB) },
+    { MP_ROM_QSTR(MP_QSTR_RGB565), MP_ROM_INT(FRAMEBUF_RGB565) },
+    { MP_ROM_QSTR(MP_QSTR_GS4_HMSB), MP_ROM_INT(FRAMEBUF_GS4_HMSB) },
+    { MP_ROM_QSTR(MP_QSTR_MONO_HLSB), MP_ROM_INT(FRAMEBUF_MHLSB) },
+    { MP_ROM_QSTR(MP_QSTR_MONO_HMSB), MP_ROM_INT(FRAMEBUF_MHMSB) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(framebuf_module_globals, framebuf_module_globals_table);
+
+const mp_obj_module_t mp_module_framebuf = {
+    .base = { &mp_type_module },
+    .globals = (mp_obj_dict_t*)&framebuf_module_globals,
+};
+
+#endif // MICROPY_PY_FRAMEBUF

+ 253 - 0
extmod/modubinascii.c

@@ -0,0 +1,253 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014 Paul Sokolovsky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+#include "py/runtime.h"
+#include "py/binary.h"
+#include "extmod/modubinascii.h"
+
+mp_obj_t mod_binascii_hexlify(size_t n_args, const mp_obj_t *args) {
+    // Second argument is for an extension to allow a separator to be used
+    // between values.
+    const char *sep = NULL;
+    mp_buffer_info_t bufinfo;
+    mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ);
+
+    // Code below assumes non-zero buffer length when computing size with
+    // separator, so handle the zero-length case here.
+    if (bufinfo.len == 0) {
+        return mp_const_empty_bytes;
+    }
+
+    vstr_t vstr;
+    size_t out_len = bufinfo.len * 2;
+    if (n_args > 1) {
+        // 1-char separator between hex numbers
+        out_len += bufinfo.len - 1;
+        sep = mp_obj_str_get_str(args[1]);
+    }
+    vstr_init_len(&vstr, out_len);
+    byte *in = bufinfo.buf, *out = (byte*)vstr.buf;
+    for (mp_uint_t i = bufinfo.len; i--;) {
+        byte d = (*in >> 4);
+        if (d > 9) {
+            d += 'a' - '9' - 1;
+        }
+        *out++ = d + '0';
+        d = (*in++ & 0xf);
+        if (d > 9) {
+            d += 'a' - '9' - 1;
+        }
+        *out++ = d + '0';
+        if (sep != NULL && i != 0) {
+            *out++ = *sep;
+        }
+    }
+    return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_binascii_hexlify_obj, 1, 2, mod_binascii_hexlify);
+
+mp_obj_t mod_binascii_unhexlify(mp_obj_t data) {
+    mp_buffer_info_t bufinfo;
+    mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ);
+
+    if ((bufinfo.len & 1) != 0) {
+        mp_raise_ValueError("odd-length string");
+    }
+    vstr_t vstr;
+    vstr_init_len(&vstr, bufinfo.len / 2);
+    byte *in = bufinfo.buf, *out = (byte*)vstr.buf;
+    byte hex_byte = 0;
+    for (mp_uint_t i = bufinfo.len; i--;) {
+        byte hex_ch = *in++;
+        if (unichar_isxdigit(hex_ch)) {
+            hex_byte += unichar_xdigit_value(hex_ch);
+        } else {
+            mp_raise_ValueError("non-hex digit found");
+        }
+        if (i & 1) {
+            hex_byte <<= 4;
+        } else {
+            *out++ = hex_byte;
+            hex_byte = 0;
+        }
+    }
+    return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
+}
+MP_DEFINE_CONST_FUN_OBJ_1(mod_binascii_unhexlify_obj, mod_binascii_unhexlify);
+
+// If ch is a character in the base64 alphabet, and is not a pad character, then
+// the corresponding integer between 0 and 63, inclusively, is returned.
+// Otherwise, -1 is returned.
+static int mod_binascii_sextet(byte ch) {
+    if (ch >= 'A' && ch <= 'Z') {
+        return ch - 'A';
+    } else if (ch >= 'a' && ch <= 'z') {
+        return ch - 'a' + 26;
+    } else if (ch >= '0' && ch <= '9') {
+        return ch - '0' + 52;
+    } else if (ch == '+') {
+        return 62;
+    } else if (ch == '/') {
+        return 63;
+    } else {
+        return -1;
+    }
+}
+
+mp_obj_t mod_binascii_a2b_base64(mp_obj_t data) {
+    mp_buffer_info_t bufinfo;
+    mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ);
+    byte *in = bufinfo.buf;
+
+    vstr_t vstr;
+    vstr_init(&vstr, (bufinfo.len / 4) * 3 + 1); // Potentially over-allocate
+    byte *out = (byte *)vstr.buf;
+
+    uint shift = 0;
+    int nbits = 0; // Number of meaningful bits in shift
+    bool hadpad = false; // Had a pad character since last valid character
+    for (size_t i = 0; i < bufinfo.len; i++) {
+        if (in[i] == '=') {
+            if ((nbits == 2) || ((nbits == 4) && hadpad)) {
+                nbits = 0;
+                break;
+            }
+            hadpad = true;
+        }
+
+        int sextet = mod_binascii_sextet(in[i]);
+        if (sextet == -1) {
+            continue;
+        }
+        hadpad = false;
+        shift = (shift << 6) | sextet;
+        nbits += 6;
+
+        if (nbits >= 8) {
+            nbits -= 8;
+            out[vstr.len++] = (shift >> nbits) & 0xFF;
+        }
+    }
+
+    if (nbits) {
+        mp_raise_ValueError("incorrect padding");
+    }
+
+    return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
+}
+MP_DEFINE_CONST_FUN_OBJ_1(mod_binascii_a2b_base64_obj, mod_binascii_a2b_base64);
+
+mp_obj_t mod_binascii_b2a_base64(mp_obj_t data) {
+    mp_buffer_info_t bufinfo;
+    mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ);
+
+    vstr_t vstr;
+    vstr_init_len(&vstr, ((bufinfo.len != 0) ? (((bufinfo.len - 1) / 3) + 1) * 4 : 0) + 1);
+
+    // First pass, we convert input buffer to numeric base 64 values
+    byte *in = bufinfo.buf, *out = (byte*)vstr.buf;
+    mp_uint_t i;
+    for (i = bufinfo.len; i >= 3; i -= 3) {
+        *out++ = (in[0] & 0xFC) >> 2;
+        *out++ = (in[0] & 0x03) << 4 | (in[1] & 0xF0) >> 4;
+        *out++ = (in[1] & 0x0F) << 2 | (in[2] & 0xC0) >> 6;
+        *out++ = in[2] & 0x3F;
+        in += 3;
+    }
+    if (i != 0) {
+        *out++ = (in[0] & 0xFC) >> 2;
+        if (i == 2) {
+            *out++ = (in[0] & 0x03) << 4 | (in[1] & 0xF0) >> 4;
+            *out++ = (in[1] & 0x0F) << 2;
+        }
+        else {
+            *out++ = (in[0] & 0x03) << 4;
+            *out++ = 64;
+        }
+        *out = 64;
+    }
+
+    // Second pass, we convert number base 64 values to actual base64 ascii encoding
+    out = (byte*)vstr.buf;
+    for (mp_uint_t j = vstr.len - 1; j--;) {
+        if (*out < 26) {
+            *out += 'A';
+        } else if (*out < 52) {
+            *out += 'a' - 26;
+        } else if (*out < 62) {
+            *out += '0' - 52;
+        } else if (*out == 62) {
+            *out ='+';
+        } else if (*out == 63) {
+            *out = '/';
+        } else {
+            *out = '=';
+        }
+        out++;
+    }
+    *out = '\n';
+    return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
+}
+MP_DEFINE_CONST_FUN_OBJ_1(mod_binascii_b2a_base64_obj, mod_binascii_b2a_base64);
+
+#if MICROPY_PY_UBINASCII_CRC32
+#include "uzlib/tinf.h"
+
+mp_obj_t mod_binascii_crc32(size_t n_args, const mp_obj_t *args) {
+    mp_buffer_info_t bufinfo;
+    mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ);
+    uint32_t crc = (n_args > 1) ? mp_obj_get_int_truncated(args[1]) : 0;
+    crc = uzlib_crc32(bufinfo.buf, bufinfo.len, crc ^ 0xffffffff);
+    return mp_obj_new_int_from_uint(crc ^ 0xffffffff);
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_binascii_crc32_obj, 1, 2, mod_binascii_crc32);
+#endif
+
+#if MICROPY_PY_UBINASCII
+
+STATIC const mp_rom_map_elem_t mp_module_binascii_globals_table[] = {
+    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ubinascii) },
+    { MP_ROM_QSTR(MP_QSTR_hexlify), MP_ROM_PTR(&mod_binascii_hexlify_obj) },
+    { MP_ROM_QSTR(MP_QSTR_unhexlify), MP_ROM_PTR(&mod_binascii_unhexlify_obj) },
+    { MP_ROM_QSTR(MP_QSTR_a2b_base64), MP_ROM_PTR(&mod_binascii_a2b_base64_obj) },
+    { MP_ROM_QSTR(MP_QSTR_b2a_base64), MP_ROM_PTR(&mod_binascii_b2a_base64_obj) },
+    #if MICROPY_PY_UBINASCII_CRC32
+    { MP_ROM_QSTR(MP_QSTR_crc32), MP_ROM_PTR(&mod_binascii_crc32_obj) },
+    #endif
+};
+
+STATIC MP_DEFINE_CONST_DICT(mp_module_binascii_globals, mp_module_binascii_globals_table);
+
+const mp_obj_module_t mp_module_ubinascii = {
+    .base = { &mp_type_module },
+    .globals = (mp_obj_dict_t*)&mp_module_binascii_globals,
+};
+
+#endif //MICROPY_PY_UBINASCII

+ 41 - 0
extmod/modubinascii.h

@@ -0,0 +1,41 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014 Paul Sokolovsky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_EXTMOD_MODUBINASCII_H
+#define MICROPY_INCLUDED_EXTMOD_MODUBINASCII_H
+
+extern mp_obj_t mod_binascii_hexlify(size_t n_args, const mp_obj_t *args);
+extern mp_obj_t mod_binascii_unhexlify(mp_obj_t data);
+extern mp_obj_t mod_binascii_a2b_base64(mp_obj_t data);
+extern mp_obj_t mod_binascii_b2a_base64(mp_obj_t data);
+extern mp_obj_t mod_binascii_crc32(size_t n_args, const mp_obj_t *args);
+
+MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mod_binascii_hexlify_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mod_binascii_unhexlify_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mod_binascii_a2b_base64_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mod_binascii_b2a_base64_obj);
+MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mod_binascii_crc32_obj);
+
+#endif // MICROPY_INCLUDED_EXTMOD_MODUBINASCII_H

+ 715 - 0
extmod/moductypes.c

@@ -0,0 +1,715 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014 Paul Sokolovsky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "py/runtime.h"
+#include "py/objtuple.h"
+#include "py/binary.h"
+
+#if MICROPY_PY_UCTYPES
+
+/// \module uctypes - Access data structures in memory
+///
+/// The module allows to define layout of raw data structure (using terms
+/// of C language), and then access memory buffers using this definition.
+/// The module also provides convenience functions to access memory buffers
+/// contained in Python objects or wrap memory buffers in Python objects.
+/// \constant UINT8_1 - uint8_t value type
+
+/// \class struct - C-like structure
+///
+/// Encapsulalation of in-memory data structure. This class doesn't define
+/// any methods, only attribute access (for structure fields) and
+/// indexing (for pointer and array fields).
+///
+/// Usage:
+///
+///     # Define layout of a structure with 2 fields
+///     # 0 and 4 are byte offsets of fields from the beginning of struct
+///     # they are logically ORed with field type
+///     FOO_STRUCT = {"a": 0 | uctypes.UINT32, "b": 4 | uctypes.UINT8}
+///
+///     # Example memory buffer to access (contained in bytes object)
+///     buf = b"\x64\0\0\0\0x14"
+///
+///     # Create structure object referring to address of
+///     # the data in the buffer above
+///     s = uctypes.struct(FOO_STRUCT, uctypes.addressof(buf))
+///
+///     # Access fields
+///     print(s.a, s.b)
+///     # Result:
+///     # 100, 20
+
+#define LAYOUT_LITTLE_ENDIAN (0)
+#define LAYOUT_BIG_ENDIAN    (1)
+#define LAYOUT_NATIVE        (2)
+
+#define VAL_TYPE_BITS 4
+#define BITF_LEN_BITS 5
+#define BITF_OFF_BITS 5
+#define OFFSET_BITS 17
+#if VAL_TYPE_BITS + BITF_LEN_BITS + BITF_OFF_BITS + OFFSET_BITS != 31
+#error Invalid encoding field length
+#endif
+
+enum {
+    UINT8, INT8, UINT16, INT16,
+    UINT32, INT32, UINT64, INT64,
+
+    BFUINT8, BFINT8, BFUINT16, BFINT16,
+    BFUINT32, BFINT32,
+
+    FLOAT32, FLOAT64,
+};
+
+#define AGG_TYPE_BITS 2
+
+enum {
+    STRUCT, PTR, ARRAY, BITFIELD,
+};
+
+// Here we need to set sign bit right
+#define TYPE2SMALLINT(x, nbits) ((((int)x) << (32 - nbits)) >> 1)
+#define GET_TYPE(x, nbits) (((x) >> (31 - nbits)) & ((1 << nbits) - 1))
+// Bit 0 is "is_signed"
+#define GET_SCALAR_SIZE(val_type) (1 << ((val_type) >> 1))
+#define VALUE_MASK(type_nbits) ~((int)0x80000000 >> type_nbits)
+
+#define IS_SCALAR_ARRAY(tuple_desc) ((tuple_desc)->len == 2)
+// We cannot apply the below to INT8, as their range [-128, 127]
+#define IS_SCALAR_ARRAY_OF_BYTES(tuple_desc) (GET_TYPE(MP_OBJ_SMALL_INT_VALUE((tuple_desc)->items[1]), VAL_TYPE_BITS) == UINT8)
+
+// "struct" in uctypes context means "structural", i.e. aggregate, type.
+STATIC const mp_obj_type_t uctypes_struct_type;
+
+typedef struct _mp_obj_uctypes_struct_t {
+    mp_obj_base_t base;
+    mp_obj_t desc;
+    byte *addr;
+    uint32_t flags;
+} mp_obj_uctypes_struct_t;
+
+STATIC NORETURN void syntax_error(void) {
+    mp_raise_TypeError("syntax error in uctypes descriptor");
+}
+
+STATIC mp_obj_t uctypes_struct_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+    mp_arg_check_num(n_args, n_kw, 2, 3, false);
+    mp_obj_uctypes_struct_t *o = m_new_obj(mp_obj_uctypes_struct_t);
+    o->base.type = type;
+    o->addr = (void*)(uintptr_t)mp_obj_int_get_truncated(args[0]);
+    o->desc = args[1];
+    o->flags = LAYOUT_NATIVE;
+    if (n_args == 3) {
+        o->flags = mp_obj_get_int(args[2]);
+    }
+    return MP_OBJ_FROM_PTR(o);
+}
+
+STATIC void uctypes_struct_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
+    (void)kind;
+    mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in);
+    const char *typen = "unk";
+    if (MP_OBJ_IS_TYPE(self->desc, &mp_type_dict)) {
+        typen = "STRUCT";
+    } else if (MP_OBJ_IS_TYPE(self->desc, &mp_type_tuple)) {
+        mp_obj_tuple_t *t = MP_OBJ_TO_PTR(self->desc);
+        mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(t->items[0]);
+        uint agg_type = GET_TYPE(offset, AGG_TYPE_BITS);
+        switch (agg_type) {
+            case PTR: typen = "PTR"; break;
+            case ARRAY: typen = "ARRAY"; break;
+        }
+    } else {
+        typen = "ERROR";
+    }
+    mp_printf(print, "<struct %s %p>", typen, self->addr);
+}
+
+// Get size of any type descriptor
+STATIC mp_uint_t uctypes_struct_size(mp_obj_t desc_in, int layout_type, mp_uint_t *max_field_size);
+
+// Get size of scalar type descriptor
+static inline mp_uint_t uctypes_struct_scalar_size(int val_type) {
+    if (val_type == FLOAT32) {
+        return 4;
+    } else {
+        return GET_SCALAR_SIZE(val_type & 7);
+    }
+}
+
+// Get size of aggregate type descriptor
+STATIC mp_uint_t uctypes_struct_agg_size(mp_obj_tuple_t *t, int layout_type, mp_uint_t *max_field_size) {
+    mp_uint_t total_size = 0;
+
+    mp_int_t offset_ = MP_OBJ_SMALL_INT_VALUE(t->items[0]);
+    mp_uint_t agg_type = GET_TYPE(offset_, AGG_TYPE_BITS);
+
+    switch (agg_type) {
+        case STRUCT:
+            return uctypes_struct_size(t->items[1], layout_type, max_field_size);
+        case PTR:
+            if (sizeof(void*) > *max_field_size) {
+                *max_field_size = sizeof(void*);
+            }
+            return sizeof(void*);
+        case ARRAY: {
+            mp_int_t arr_sz = MP_OBJ_SMALL_INT_VALUE(t->items[1]);
+            uint val_type = GET_TYPE(arr_sz, VAL_TYPE_BITS);
+            arr_sz &= VALUE_MASK(VAL_TYPE_BITS);
+            mp_uint_t item_s;
+            if (t->len == 2) {
+                // Elements of array are scalar
+                item_s = GET_SCALAR_SIZE(val_type);
+                if (item_s > *max_field_size) {
+                    *max_field_size = item_s;
+                }
+            } else {
+                // Elements of array are aggregates
+                item_s = uctypes_struct_size(t->items[2], layout_type, max_field_size);
+            }
+
+            return item_s * arr_sz;
+        }
+        default:
+            assert(0);
+    }
+
+    return total_size;
+}
+
+STATIC mp_uint_t uctypes_struct_size(mp_obj_t desc_in, int layout_type, mp_uint_t *max_field_size) {
+    if (!MP_OBJ_IS_TYPE(desc_in, &mp_type_dict)) {
+        if (MP_OBJ_IS_TYPE(desc_in, &mp_type_tuple)) {
+            return uctypes_struct_agg_size((mp_obj_tuple_t*)MP_OBJ_TO_PTR(desc_in), layout_type, max_field_size);
+        } else if (MP_OBJ_IS_SMALL_INT(desc_in)) {
+            // We allow sizeof on both type definitions and structures/structure fields,
+            // but scalar structure field is lowered into native Python int, so all
+            // type info is lost. So, we cannot say if it's scalar type description,
+            // or such lowered scalar.
+            mp_raise_TypeError("Cannot unambiguously get sizeof scalar");
+        }
+        syntax_error();
+    }
+
+    mp_obj_dict_t *d = MP_OBJ_TO_PTR(desc_in);
+    mp_uint_t total_size = 0;
+
+    for (mp_uint_t i = 0; i < d->map.alloc; i++) {
+        if (MP_MAP_SLOT_IS_FILLED(&d->map, i)) {
+            mp_obj_t v = d->map.table[i].value;
+            if (MP_OBJ_IS_SMALL_INT(v)) {
+                mp_uint_t offset = MP_OBJ_SMALL_INT_VALUE(v);
+                mp_uint_t val_type = GET_TYPE(offset, VAL_TYPE_BITS);
+                offset &= VALUE_MASK(VAL_TYPE_BITS);
+                if (val_type >= BFUINT8 && val_type <= BFINT32) {
+                    offset &= (1 << OFFSET_BITS) - 1;
+                }
+                mp_uint_t s = uctypes_struct_scalar_size(val_type);
+                if (s > *max_field_size) {
+                    *max_field_size = s;
+                }
+                if (offset + s > total_size) {
+                    total_size = offset + s;
+                }
+            } else {
+                if (!MP_OBJ_IS_TYPE(v, &mp_type_tuple)) {
+                    syntax_error();
+                }
+                mp_obj_tuple_t *t = MP_OBJ_TO_PTR(v);
+                mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(t->items[0]);
+                offset &= VALUE_MASK(AGG_TYPE_BITS);
+                mp_uint_t s = uctypes_struct_agg_size(t, layout_type, max_field_size);
+                if (offset + s > total_size) {
+                    total_size = offset + s;
+                }
+            }
+        }
+    }
+
+    // Round size up to alignment of biggest field
+    if (layout_type == LAYOUT_NATIVE) {
+        total_size = (total_size + *max_field_size - 1) & ~(*max_field_size - 1);
+    }
+    return total_size;
+}
+
+STATIC mp_obj_t uctypes_struct_sizeof(mp_obj_t obj_in) {
+    mp_uint_t max_field_size = 0;
+    if (MP_OBJ_IS_TYPE(obj_in, &mp_type_bytearray)) {
+        return mp_obj_len(obj_in);
+    }
+    int layout_type = LAYOUT_NATIVE;
+    // We can apply sizeof either to structure definition (a dict)
+    // or to instantiated structure
+    if (MP_OBJ_IS_TYPE(obj_in, &uctypes_struct_type)) {
+        // Extract structure definition
+        mp_obj_uctypes_struct_t *obj = MP_OBJ_TO_PTR(obj_in);
+        obj_in = obj->desc;
+        layout_type = obj->flags;
+    }
+    mp_uint_t size = uctypes_struct_size(obj_in, layout_type, &max_field_size);
+    return MP_OBJ_NEW_SMALL_INT(size);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(uctypes_struct_sizeof_obj, uctypes_struct_sizeof);
+
+static inline mp_obj_t get_unaligned(uint val_type, byte *p, int big_endian) {
+    char struct_type = big_endian ? '>' : '<';
+    static const char type2char[16] = "BbHhIiQq------fd";
+    return mp_binary_get_val(struct_type, type2char[val_type], &p);
+}
+
+static inline void set_unaligned(uint val_type, byte *p, int big_endian, mp_obj_t val) {
+    char struct_type = big_endian ? '>' : '<';
+    static const char type2char[16] = "BbHhIiQq------fd";
+    mp_binary_set_val(struct_type, type2char[val_type], val, &p);
+}
+
+static inline mp_uint_t get_aligned_basic(uint val_type, void *p) {
+    switch (val_type) {
+        case UINT8:
+            return *(uint8_t*)p;
+        case UINT16:
+            return *(uint16_t*)p;
+        case UINT32:
+            return *(uint32_t*)p;
+    }
+    assert(0);
+    return 0;
+}
+
+static inline void set_aligned_basic(uint val_type, void *p, mp_uint_t v) {
+    switch (val_type) {
+        case UINT8:
+            *(uint8_t*)p = (uint8_t)v; return;
+        case UINT16:
+            *(uint16_t*)p = (uint16_t)v; return;
+        case UINT32:
+            *(uint32_t*)p = (uint32_t)v; return;
+    }
+    assert(0);
+}
+
+STATIC mp_obj_t get_aligned(uint val_type, void *p, mp_int_t index) {
+    switch (val_type) {
+        case UINT8:
+            return MP_OBJ_NEW_SMALL_INT(((uint8_t*)p)[index]);
+        case INT8:
+            return MP_OBJ_NEW_SMALL_INT(((int8_t*)p)[index]);
+        case UINT16:
+            return MP_OBJ_NEW_SMALL_INT(((uint16_t*)p)[index]);
+        case INT16:
+            return MP_OBJ_NEW_SMALL_INT(((int16_t*)p)[index]);
+        case UINT32:
+            return mp_obj_new_int_from_uint(((uint32_t*)p)[index]);
+        case INT32:
+            return mp_obj_new_int(((int32_t*)p)[index]);
+        case UINT64:
+            return mp_obj_new_int_from_ull(((uint64_t*)p)[index]);
+        case INT64:
+            return mp_obj_new_int_from_ll(((int64_t*)p)[index]);
+        #if MICROPY_PY_BUILTINS_FLOAT
+        case FLOAT32:
+            return mp_obj_new_float(((float*)p)[index]);
+        case FLOAT64:
+            return mp_obj_new_float(((double*)p)[index]);
+        #endif
+        default:
+            assert(0);
+            return MP_OBJ_NULL;
+    }
+}
+
+STATIC void set_aligned(uint val_type, void *p, mp_int_t index, mp_obj_t val) {
+    #if MICROPY_PY_BUILTINS_FLOAT
+    if (val_type == FLOAT32 || val_type == FLOAT64) {
+        mp_float_t v = mp_obj_get_float(val);
+        if (val_type == FLOAT32) {
+            ((float*)p)[index] = v;
+        } else {
+            ((double*)p)[index] = v;
+        }
+        return;
+    }
+    #endif
+    mp_int_t v = mp_obj_get_int_truncated(val);
+    switch (val_type) {
+        case UINT8:
+            ((uint8_t*)p)[index] = (uint8_t)v; return;
+        case INT8:
+            ((int8_t*)p)[index] = (int8_t)v; return;
+        case UINT16:
+            ((uint16_t*)p)[index] = (uint16_t)v; return;
+        case INT16:
+            ((int16_t*)p)[index] = (int16_t)v; return;
+        case UINT32:
+            ((uint32_t*)p)[index] = (uint32_t)v; return;
+        case INT32:
+            ((int32_t*)p)[index] = (int32_t)v; return;
+        case INT64:
+        case UINT64:
+            if (sizeof(mp_int_t) == 8) {
+                ((uint64_t*)p)[index] = (uint64_t)v;
+            } else {
+                // TODO: Doesn't offer atomic store semantics, but should at least try
+                set_unaligned(val_type, p, MP_ENDIANNESS_BIG, val);
+            }
+            return;
+        default:
+            assert(0);
+    }
+}
+
+STATIC mp_obj_t uctypes_struct_attr_op(mp_obj_t self_in, qstr attr, mp_obj_t set_val) {
+    mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in);
+
+    // TODO: Support at least OrderedDict in addition
+    if (!MP_OBJ_IS_TYPE(self->desc, &mp_type_dict)) {
+            mp_raise_TypeError("struct: no fields");
+    }
+
+    mp_obj_t deref = mp_obj_dict_get(self->desc, MP_OBJ_NEW_QSTR(attr));
+    if (MP_OBJ_IS_SMALL_INT(deref)) {
+        mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(deref);
+        mp_uint_t val_type = GET_TYPE(offset, VAL_TYPE_BITS);
+        offset &= VALUE_MASK(VAL_TYPE_BITS);
+//printf("scalar type=%d offset=%x\n", val_type, offset);
+
+        if (val_type <= INT64 || val_type == FLOAT32 || val_type == FLOAT64) {
+//            printf("size=%d\n", GET_SCALAR_SIZE(val_type));
+            if (self->flags == LAYOUT_NATIVE) {
+                if (set_val == MP_OBJ_NULL) {
+                    return get_aligned(val_type, self->addr + offset, 0);
+                } else {
+                    set_aligned(val_type, self->addr + offset, 0, set_val);
+                    return set_val; // just !MP_OBJ_NULL
+                }
+            } else {
+                if (set_val == MP_OBJ_NULL) {
+                    return get_unaligned(val_type, self->addr + offset, self->flags);
+                } else {
+                    set_unaligned(val_type, self->addr + offset, self->flags, set_val);
+                    return set_val; // just !MP_OBJ_NULL
+                }
+            }
+        } else if (val_type >= BFUINT8 && val_type <= BFINT32) {
+            uint bit_offset = (offset >> 17) & 31;
+            uint bit_len = (offset >> 22) & 31;
+            offset &= (1 << 17) - 1;
+            mp_uint_t val;
+            if (self->flags == LAYOUT_NATIVE) {
+                val = get_aligned_basic(val_type & 6, self->addr + offset);
+            } else {
+                val = mp_binary_get_int(GET_SCALAR_SIZE(val_type & 7), val_type & 1, self->flags, self->addr + offset);
+            }
+            if (set_val == MP_OBJ_NULL) {
+                val >>= bit_offset;
+                val &= (1 << bit_len) - 1;
+                // TODO: signed
+                assert((val_type & 1) == 0);
+                return mp_obj_new_int(val);
+            } else {
+                mp_uint_t set_val_int = (mp_uint_t)mp_obj_get_int(set_val);
+                mp_uint_t mask = (1 << bit_len) - 1;
+                set_val_int &= mask;
+                set_val_int <<= bit_offset;
+                mask <<= bit_offset;
+                val = (val & ~mask) | set_val_int;
+
+                if (self->flags == LAYOUT_NATIVE) {
+                    set_aligned_basic(val_type & 6, self->addr + offset, val);
+                } else {
+                    mp_binary_set_int(GET_SCALAR_SIZE(val_type & 7), self->flags == LAYOUT_BIG_ENDIAN,
+                        self->addr + offset, val);
+                }
+                return set_val; // just !MP_OBJ_NULL
+            }
+        }
+
+        assert(0);
+        return MP_OBJ_NULL;
+    }
+
+    if (!MP_OBJ_IS_TYPE(deref, &mp_type_tuple)) {
+        syntax_error();
+    }
+
+    if (set_val != MP_OBJ_NULL) {
+        // Cannot assign to aggregate
+        syntax_error();
+    }
+
+    mp_obj_tuple_t *sub = MP_OBJ_TO_PTR(deref);
+    mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(sub->items[0]);
+    mp_uint_t agg_type = GET_TYPE(offset, AGG_TYPE_BITS);
+    offset &= VALUE_MASK(AGG_TYPE_BITS);
+//printf("agg type=%d offset=%x\n", agg_type, offset);
+
+    switch (agg_type) {
+        case STRUCT: {
+            mp_obj_uctypes_struct_t *o = m_new_obj(mp_obj_uctypes_struct_t);
+            o->base.type = &uctypes_struct_type;
+            o->desc = sub->items[1];
+            o->addr = self->addr + offset;
+            o->flags = self->flags;
+            return MP_OBJ_FROM_PTR(o);
+        }
+        case ARRAY: {
+            mp_uint_t dummy;
+            if (IS_SCALAR_ARRAY(sub) && IS_SCALAR_ARRAY_OF_BYTES(sub)) {
+                return mp_obj_new_bytearray_by_ref(uctypes_struct_agg_size(sub, self->flags, &dummy), self->addr + offset);
+            }
+            // Fall thru to return uctypes struct object
+        }
+        case PTR: {
+            mp_obj_uctypes_struct_t *o = m_new_obj(mp_obj_uctypes_struct_t);
+            o->base.type = &uctypes_struct_type;
+            o->desc = MP_OBJ_FROM_PTR(sub);
+            o->addr = self->addr + offset;
+            o->flags = self->flags;
+//printf("PTR/ARR base addr=%p\n", o->addr);
+            return MP_OBJ_FROM_PTR(o);
+        }
+    }
+
+    // Should be unreachable once all cases are handled
+    return MP_OBJ_NULL;
+}
+
+STATIC void uctypes_struct_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
+    if (dest[0] == MP_OBJ_NULL) {
+        // load attribute
+        mp_obj_t val = uctypes_struct_attr_op(self_in, attr, MP_OBJ_NULL);
+        dest[0] = val;
+    } else {
+        // delete/store attribute
+        if (uctypes_struct_attr_op(self_in, attr, dest[1]) != MP_OBJ_NULL) {
+            dest[0] = MP_OBJ_NULL; // indicate success
+        }
+    }
+}
+
+STATIC mp_obj_t uctypes_struct_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value) {
+    mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in);
+
+    if (value == MP_OBJ_NULL) {
+        // delete
+        return MP_OBJ_NULL; // op not supported
+    } else {
+        // load / store
+        if (!MP_OBJ_IS_TYPE(self->desc, &mp_type_tuple)) {
+            mp_raise_TypeError("struct: cannot index");
+        }
+
+        mp_obj_tuple_t *t = MP_OBJ_TO_PTR(self->desc);
+        mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(t->items[0]);
+        uint agg_type = GET_TYPE(offset, AGG_TYPE_BITS);
+
+        mp_int_t index = MP_OBJ_SMALL_INT_VALUE(index_in);
+
+        if (agg_type == ARRAY) {
+            mp_int_t arr_sz = MP_OBJ_SMALL_INT_VALUE(t->items[1]);
+            uint val_type = GET_TYPE(arr_sz, VAL_TYPE_BITS);
+            arr_sz &= VALUE_MASK(VAL_TYPE_BITS);
+            if (index >= arr_sz) {
+                nlr_raise(mp_obj_new_exception_msg(&mp_type_IndexError, "struct: index out of range"));
+            }
+
+            if (t->len == 2) {
+                // array of scalars
+                if (self->flags == LAYOUT_NATIVE) {
+                    if (value == MP_OBJ_SENTINEL) {
+                        return get_aligned(val_type, self->addr, index);
+                    } else {
+                        set_aligned(val_type, self->addr, index, value);
+                        return value; // just !MP_OBJ_NULL
+                    }
+                } else {
+                    byte *p = self->addr + GET_SCALAR_SIZE(val_type) * index;
+                    if (value == MP_OBJ_SENTINEL) {
+                        return get_unaligned(val_type, p, self->flags);
+                    } else {
+                        set_unaligned(val_type, p, self->flags, value);
+                        return value; // just !MP_OBJ_NULL
+                    }
+                }
+            } else if (value == MP_OBJ_SENTINEL) {
+                mp_uint_t dummy = 0;
+                mp_uint_t size = uctypes_struct_size(t->items[2], self->flags, &dummy);
+                mp_obj_uctypes_struct_t *o = m_new_obj(mp_obj_uctypes_struct_t);
+                o->base.type = &uctypes_struct_type;
+                o->desc = t->items[2];
+                o->addr = self->addr + size * index;
+                o->flags = self->flags;
+                return MP_OBJ_FROM_PTR(o);
+            } else {
+                return MP_OBJ_NULL; // op not supported
+            }
+
+        } else if (agg_type == PTR) {
+            byte *p = *(void**)self->addr;
+            if (MP_OBJ_IS_SMALL_INT(t->items[1])) {
+                uint val_type = GET_TYPE(MP_OBJ_SMALL_INT_VALUE(t->items[1]), VAL_TYPE_BITS);
+                return get_aligned(val_type, p, index);
+            } else {
+                mp_uint_t dummy = 0;
+                mp_uint_t size = uctypes_struct_size(t->items[1], self->flags, &dummy);
+                mp_obj_uctypes_struct_t *o = m_new_obj(mp_obj_uctypes_struct_t);
+                o->base.type = &uctypes_struct_type;
+                o->desc = t->items[1];
+                o->addr = p + size * index;
+                o->flags = self->flags;
+                return MP_OBJ_FROM_PTR(o);
+            }
+        }
+
+        assert(0);
+        return MP_OBJ_NULL;
+    }
+}
+
+STATIC mp_int_t uctypes_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) {
+    (void)flags;
+    mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in);
+    mp_uint_t max_field_size = 0;
+    mp_uint_t size = uctypes_struct_size(self->desc, self->flags, &max_field_size);
+
+    bufinfo->buf = self->addr;
+    bufinfo->len = size;
+    bufinfo->typecode = BYTEARRAY_TYPECODE;
+    return 0;
+}
+
+/// \function addressof()
+/// Return address of object's data (applies to object providing buffer
+/// interface).
+STATIC mp_obj_t uctypes_struct_addressof(mp_obj_t buf) {
+    mp_buffer_info_t bufinfo;
+    mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ);
+    return mp_obj_new_int((mp_int_t)(uintptr_t)bufinfo.buf);
+}
+MP_DEFINE_CONST_FUN_OBJ_1(uctypes_struct_addressof_obj, uctypes_struct_addressof);
+
+/// \function bytearray_at()
+/// Capture memory at given address of given size as bytearray. Memory is
+/// captured by reference (and thus memory pointed by bytearray may change
+/// or become invalid at later time). Use bytes_at() to capture by value.
+STATIC mp_obj_t uctypes_struct_bytearray_at(mp_obj_t ptr, mp_obj_t size) {
+    return mp_obj_new_bytearray_by_ref(mp_obj_int_get_truncated(size), (void*)(uintptr_t)mp_obj_int_get_truncated(ptr));
+}
+MP_DEFINE_CONST_FUN_OBJ_2(uctypes_struct_bytearray_at_obj, uctypes_struct_bytearray_at);
+
+/// \function bytes_at()
+/// Capture memory at given address of given size as bytes. Memory is
+/// captured by value, i.e. copied. Use bytearray_at() to capture by reference
+/// ("zero copy").
+STATIC mp_obj_t uctypes_struct_bytes_at(mp_obj_t ptr, mp_obj_t size) {
+    return mp_obj_new_bytes((void*)(uintptr_t)mp_obj_int_get_truncated(ptr), mp_obj_int_get_truncated(size));
+}
+MP_DEFINE_CONST_FUN_OBJ_2(uctypes_struct_bytes_at_obj, uctypes_struct_bytes_at);
+
+
+STATIC const mp_obj_type_t uctypes_struct_type = {
+    { &mp_type_type },
+    .name = MP_QSTR_struct,
+    .print = uctypes_struct_print,
+    .make_new = uctypes_struct_make_new,
+    .attr = uctypes_struct_attr,
+    .subscr = uctypes_struct_subscr,
+    .buffer_p = { .get_buffer = uctypes_get_buffer },
+};
+
+STATIC const mp_rom_map_elem_t mp_module_uctypes_globals_table[] = {
+    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uctypes) },
+    { MP_ROM_QSTR(MP_QSTR_struct), MP_ROM_PTR(&uctypes_struct_type) },
+    { MP_ROM_QSTR(MP_QSTR_sizeof), MP_ROM_PTR(&uctypes_struct_sizeof_obj) },
+    { MP_ROM_QSTR(MP_QSTR_addressof), MP_ROM_PTR(&uctypes_struct_addressof_obj) },
+    { MP_ROM_QSTR(MP_QSTR_bytes_at), MP_ROM_PTR(&uctypes_struct_bytes_at_obj) },
+    { MP_ROM_QSTR(MP_QSTR_bytearray_at), MP_ROM_PTR(&uctypes_struct_bytearray_at_obj) },
+
+    /// \moduleref uctypes
+
+    /// \constant NATIVE - Native structure layout - native endianness,
+    /// platform-specific field alignment
+    { MP_ROM_QSTR(MP_QSTR_NATIVE), MP_ROM_INT(LAYOUT_NATIVE) },
+    /// \constant LITTLE_ENDIAN - Little-endian structure layout, tightly packed
+    /// (no alignment constraints)
+    { MP_ROM_QSTR(MP_QSTR_LITTLE_ENDIAN), MP_ROM_INT(LAYOUT_LITTLE_ENDIAN) },
+    /// \constant BIG_ENDIAN - Big-endian structure layout, tightly packed
+    /// (no alignment constraints)
+    { MP_ROM_QSTR(MP_QSTR_BIG_ENDIAN), MP_ROM_INT(LAYOUT_BIG_ENDIAN) },
+
+    /// \constant VOID - void value type, may be used only as pointer target type.
+    { MP_ROM_QSTR(MP_QSTR_VOID), MP_ROM_INT(TYPE2SMALLINT(UINT8, VAL_TYPE_BITS)) },
+
+    /// \constant UINT8 - uint8_t value type
+    { MP_ROM_QSTR(MP_QSTR_UINT8), MP_ROM_INT(TYPE2SMALLINT(UINT8, 4)) },
+    /// \constant INT8 - int8_t value type
+    { MP_ROM_QSTR(MP_QSTR_INT8), MP_ROM_INT(TYPE2SMALLINT(INT8, 4)) },
+    /// \constant UINT16 - uint16_t value type
+    { MP_ROM_QSTR(MP_QSTR_UINT16), MP_ROM_INT(TYPE2SMALLINT(UINT16, 4)) },
+    /// \constant INT16 - int16_t value type
+    { MP_ROM_QSTR(MP_QSTR_INT16), MP_ROM_INT(TYPE2SMALLINT(INT16, 4)) },
+    /// \constant UINT32 - uint32_t value type
+    { MP_ROM_QSTR(MP_QSTR_UINT32), MP_ROM_INT(TYPE2SMALLINT(UINT32, 4)) },
+    /// \constant INT32 - int32_t value type
+    { MP_ROM_QSTR(MP_QSTR_INT32), MP_ROM_INT(TYPE2SMALLINT(INT32, 4)) },
+    /// \constant UINT64 - uint64_t value type
+    { MP_ROM_QSTR(MP_QSTR_UINT64), MP_ROM_INT(TYPE2SMALLINT(UINT64, 4)) },
+    /// \constant INT64 - int64_t value type
+    { MP_ROM_QSTR(MP_QSTR_INT64), MP_ROM_INT(TYPE2SMALLINT(INT64, 4)) },
+
+    { MP_ROM_QSTR(MP_QSTR_BFUINT8), MP_ROM_INT(TYPE2SMALLINT(BFUINT8, 4)) },
+    { MP_ROM_QSTR(MP_QSTR_BFINT8), MP_ROM_INT(TYPE2SMALLINT(BFINT8, 4)) },
+    { MP_ROM_QSTR(MP_QSTR_BFUINT16), MP_ROM_INT(TYPE2SMALLINT(BFUINT16, 4)) },
+    { MP_ROM_QSTR(MP_QSTR_BFINT16), MP_ROM_INT(TYPE2SMALLINT(BFINT16, 4)) },
+    { MP_ROM_QSTR(MP_QSTR_BFUINT32), MP_ROM_INT(TYPE2SMALLINT(BFUINT32, 4)) },
+    { MP_ROM_QSTR(MP_QSTR_BFINT32), MP_ROM_INT(TYPE2SMALLINT(BFINT32, 4)) },
+
+    { MP_ROM_QSTR(MP_QSTR_BF_POS), MP_ROM_INT(17) },
+    { MP_ROM_QSTR(MP_QSTR_BF_LEN), MP_ROM_INT(22) },
+
+    #if MICROPY_PY_BUILTINS_FLOAT
+    { MP_ROM_QSTR(MP_QSTR_FLOAT32), MP_ROM_INT(TYPE2SMALLINT(FLOAT32, 4)) },
+    { MP_ROM_QSTR(MP_QSTR_FLOAT64), MP_ROM_INT(TYPE2SMALLINT(FLOAT64, 4)) },
+    #endif
+
+    { MP_ROM_QSTR(MP_QSTR_PTR), MP_ROM_INT(TYPE2SMALLINT(PTR, AGG_TYPE_BITS)) },
+    { MP_ROM_QSTR(MP_QSTR_ARRAY), MP_ROM_INT(TYPE2SMALLINT(ARRAY, AGG_TYPE_BITS)) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(mp_module_uctypes_globals, mp_module_uctypes_globals_table);
+
+const mp_obj_module_t mp_module_uctypes = {
+    .base = { &mp_type_module },
+    .globals = (mp_obj_dict_t*)&mp_module_uctypes_globals,
+};
+
+#endif

+ 158 - 0
extmod/moduhashlib.c

@@ -0,0 +1,158 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014 Paul Sokolovsky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include "py/runtime.h"
+
+#if MICROPY_PY_UHASHLIB
+
+#include "crypto-algorithms/sha256.h"
+#if MICROPY_PY_UHASHLIB_SHA1
+#include "lib/axtls/crypto/crypto.h"
+#endif
+
+typedef struct _mp_obj_hash_t {
+    mp_obj_base_t base;
+    char state[0];
+} mp_obj_hash_t;
+
+STATIC mp_obj_t hash_update(mp_obj_t self_in, mp_obj_t arg);
+
+STATIC mp_obj_t hash_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+    mp_arg_check_num(n_args, n_kw, 0, 1, false);
+    mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(CRYAL_SHA256_CTX));
+    o->base.type = type;
+    sha256_init((CRYAL_SHA256_CTX*)o->state);
+    if (n_args == 1) {
+        hash_update(MP_OBJ_FROM_PTR(o), args[0]);
+    }
+    return MP_OBJ_FROM_PTR(o);
+}
+
+#if MICROPY_PY_UHASHLIB_SHA1
+STATIC mp_obj_t sha1_update(mp_obj_t self_in, mp_obj_t arg);
+
+STATIC mp_obj_t sha1_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+    mp_arg_check_num(n_args, n_kw, 0, 1, false);
+    mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(SHA1_CTX));
+    o->base.type = type;
+    SHA1_Init((SHA1_CTX*)o->state);
+    if (n_args == 1) {
+        sha1_update(MP_OBJ_FROM_PTR(o), args[0]);
+    }
+    return MP_OBJ_FROM_PTR(o);
+}
+#endif
+
+STATIC mp_obj_t hash_update(mp_obj_t self_in, mp_obj_t arg) {
+    mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
+    mp_buffer_info_t bufinfo;
+    mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ);
+    sha256_update((CRYAL_SHA256_CTX*)self->state, bufinfo.buf, bufinfo.len);
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_2(hash_update_obj, hash_update);
+
+#if MICROPY_PY_UHASHLIB_SHA1
+STATIC mp_obj_t sha1_update(mp_obj_t self_in, mp_obj_t arg) {
+    mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
+    mp_buffer_info_t bufinfo;
+    mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ);
+    SHA1_Update((SHA1_CTX*)self->state, bufinfo.buf, bufinfo.len);
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_2(sha1_update_obj, sha1_update);
+#endif
+
+STATIC mp_obj_t hash_digest(mp_obj_t self_in) {
+    mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
+    vstr_t vstr;
+    vstr_init_len(&vstr, SHA256_BLOCK_SIZE);
+    sha256_final((CRYAL_SHA256_CTX*)self->state, (byte*)vstr.buf);
+    return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
+}
+MP_DEFINE_CONST_FUN_OBJ_1(hash_digest_obj, hash_digest);
+
+#if MICROPY_PY_UHASHLIB_SHA1
+STATIC mp_obj_t sha1_digest(mp_obj_t self_in) {
+    mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
+    vstr_t vstr;
+    vstr_init_len(&vstr, SHA1_SIZE);
+    SHA1_Final((byte*)vstr.buf, (SHA1_CTX*)self->state);
+    return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
+}
+MP_DEFINE_CONST_FUN_OBJ_1(sha1_digest_obj, sha1_digest);
+#endif
+
+STATIC const mp_rom_map_elem_t hash_locals_dict_table[] = {
+    { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&hash_update_obj) },
+    { MP_ROM_QSTR(MP_QSTR_digest), MP_ROM_PTR(&hash_digest_obj) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(hash_locals_dict, hash_locals_dict_table);
+
+STATIC const mp_obj_type_t sha256_type = {
+    { &mp_type_type },
+    .name = MP_QSTR_sha256,
+    .make_new = hash_make_new,
+    .locals_dict = (void*)&hash_locals_dict,
+};
+
+#if MICROPY_PY_UHASHLIB_SHA1
+STATIC const mp_rom_map_elem_t sha1_locals_dict_table[] = {
+    { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&sha1_update_obj) },
+    { MP_ROM_QSTR(MP_QSTR_digest), MP_ROM_PTR(&sha1_digest_obj) },
+};
+STATIC MP_DEFINE_CONST_DICT(sha1_locals_dict, sha1_locals_dict_table);
+
+STATIC const mp_obj_type_t sha1_type = {
+    { &mp_type_type },
+    .name = MP_QSTR_sha1,
+    .make_new = sha1_make_new,
+    .locals_dict = (void*)&sha1_locals_dict,
+};
+#endif
+
+STATIC const mp_rom_map_elem_t mp_module_hashlib_globals_table[] = {
+    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uhashlib) },
+    { MP_ROM_QSTR(MP_QSTR_sha256), MP_ROM_PTR(&sha256_type) },
+    #if MICROPY_PY_UHASHLIB_SHA1
+    { MP_ROM_QSTR(MP_QSTR_sha1), MP_ROM_PTR(&sha1_type) },
+    #endif
+};
+
+STATIC MP_DEFINE_CONST_DICT(mp_module_hashlib_globals, mp_module_hashlib_globals_table);
+
+const mp_obj_module_t mp_module_uhashlib = {
+    .base = { &mp_type_module },
+    .globals = (mp_obj_dict_t*)&mp_module_hashlib_globals,
+};
+
+#include "crypto-algorithms/sha256.c"
+
+#endif //MICROPY_PY_UHASHLIB

+ 120 - 0
extmod/moduheapq.c

@@ -0,0 +1,120 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/objlist.h"
+#include "py/runtime.h"
+
+#if MICROPY_PY_UHEAPQ
+
+// the algorithm here is modelled on CPython's heapq.py
+
+STATIC mp_obj_list_t *get_heap(mp_obj_t heap_in) {
+    if (!MP_OBJ_IS_TYPE(heap_in, &mp_type_list)) {
+        mp_raise_TypeError("heap must be a list");
+    }
+    return MP_OBJ_TO_PTR(heap_in);
+}
+
+STATIC void heap_siftdown(mp_obj_list_t *heap, mp_uint_t start_pos, mp_uint_t pos) {
+    mp_obj_t item = heap->items[pos];
+    while (pos > start_pos) {
+        mp_uint_t parent_pos = (pos - 1) >> 1;
+        mp_obj_t parent = heap->items[parent_pos];
+        if (mp_binary_op(MP_BINARY_OP_LESS, item, parent) == mp_const_true) {
+            heap->items[pos] = parent;
+            pos = parent_pos;
+        } else {
+            break;
+        }
+    }
+    heap->items[pos] = item;
+}
+
+STATIC void heap_siftup(mp_obj_list_t *heap, mp_uint_t pos) {
+    mp_uint_t start_pos = pos;
+    mp_uint_t end_pos = heap->len;
+    mp_obj_t item = heap->items[pos];
+    for (mp_uint_t child_pos = 2 * pos + 1; child_pos < end_pos; child_pos = 2 * pos + 1) {
+        // choose right child if it's <= left child
+        if (child_pos + 1 < end_pos && mp_binary_op(MP_BINARY_OP_LESS, heap->items[child_pos], heap->items[child_pos + 1]) == mp_const_false) {
+            child_pos += 1;
+        }
+        // bubble up the smaller child
+        heap->items[pos] = heap->items[child_pos];
+        pos = child_pos;
+    }
+    heap->items[pos] = item;
+    heap_siftdown(heap, start_pos, pos);
+}
+
+STATIC mp_obj_t mod_uheapq_heappush(mp_obj_t heap_in, mp_obj_t item) {
+    mp_obj_list_t *heap = get_heap(heap_in);
+    mp_obj_list_append(heap_in, item);
+    heap_siftdown(heap, 0, heap->len - 1);
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_uheapq_heappush_obj, mod_uheapq_heappush);
+
+STATIC mp_obj_t mod_uheapq_heappop(mp_obj_t heap_in) {
+    mp_obj_list_t *heap = get_heap(heap_in);
+    if (heap->len == 0) {
+        nlr_raise(mp_obj_new_exception_msg(&mp_type_IndexError, "empty heap"));
+    }
+    mp_obj_t item = heap->items[0];
+    heap->len -= 1;
+    heap->items[0] = heap->items[heap->len];
+    heap->items[heap->len] = MP_OBJ_NULL; // so we don't retain a pointer
+    if (heap->len) {
+        heap_siftup(heap, 0);
+    }
+    return item;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_uheapq_heappop_obj, mod_uheapq_heappop);
+
+STATIC mp_obj_t mod_uheapq_heapify(mp_obj_t heap_in) {
+    mp_obj_list_t *heap = get_heap(heap_in);
+    for (mp_uint_t i = heap->len / 2; i > 0;) {
+        heap_siftup(heap, --i);
+    }
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_uheapq_heapify_obj, mod_uheapq_heapify);
+
+STATIC const mp_rom_map_elem_t mp_module_uheapq_globals_table[] = {
+    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uheapq) },
+    { MP_ROM_QSTR(MP_QSTR_heappush), MP_ROM_PTR(&mod_uheapq_heappush_obj) },
+    { MP_ROM_QSTR(MP_QSTR_heappop), MP_ROM_PTR(&mod_uheapq_heappop_obj) },
+    { MP_ROM_QSTR(MP_QSTR_heapify), MP_ROM_PTR(&mod_uheapq_heapify_obj) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(mp_module_uheapq_globals, mp_module_uheapq_globals_table);
+
+const mp_obj_module_t mp_module_uheapq = {
+    .base = { &mp_type_module },
+    .globals = (mp_obj_dict_t*)&mp_module_uheapq_globals,
+};
+
+#endif //MICROPY_PY_UHEAPQ

+ 298 - 0
extmod/modujson.c

@@ -0,0 +1,298 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014-2016 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdio.h>
+
+#include "py/objlist.h"
+#include "py/objstringio.h"
+#include "py/parsenum.h"
+#include "py/runtime.h"
+#include "py/stream.h"
+
+#if MICROPY_PY_UJSON
+
+STATIC mp_obj_t mod_ujson_dumps(mp_obj_t obj) {
+    vstr_t vstr;
+    mp_print_t print;
+    vstr_init_print(&vstr, 8, &print);
+    mp_obj_print_helper(&print, obj, PRINT_JSON);
+    return mp_obj_new_str_from_vstr(&mp_type_str, &vstr);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_ujson_dumps_obj, mod_ujson_dumps);
+
+// The function below implements a simple non-recursive JSON parser.
+//
+// The JSON specification is at http://www.ietf.org/rfc/rfc4627.txt
+// The parser here will parse any valid JSON and return the correct
+// corresponding Python object.  It allows through a superset of JSON, since
+// it treats commas and colons as "whitespace", and doesn't care if
+// brackets/braces are correctly paired.  It will raise a ValueError if the
+// input is outside it's specs.
+//
+// Most of the work is parsing the primitives (null, false, true, numbers,
+// strings).  It does 1 pass over the input stream.  It tries to be fast and
+// small in code size, while not using more RAM than necessary.
+
+typedef struct _ujson_stream_t {
+    mp_obj_t stream_obj;
+    mp_uint_t (*read)(mp_obj_t obj, void *buf, mp_uint_t size, int *errcode);
+    int errcode;
+    byte cur;
+} ujson_stream_t;
+
+#define S_EOF (0) // null is not allowed in json stream so is ok as EOF marker
+#define S_END(s) ((s).cur == S_EOF)
+#define S_CUR(s) ((s).cur)
+#define S_NEXT(s) (ujson_stream_next(&(s)))
+
+STATIC byte ujson_stream_next(ujson_stream_t *s) {
+    mp_uint_t ret = s->read(s->stream_obj, &s->cur, 1, &s->errcode);
+    if (s->errcode != 0) {
+        mp_raise_OSError(s->errcode);
+    }
+    if (ret == 0) {
+        s->cur = S_EOF;
+    }
+    return s->cur;
+}
+
+STATIC mp_obj_t mod_ujson_load(mp_obj_t stream_obj) {
+    const mp_stream_p_t *stream_p = mp_get_stream_raise(stream_obj, MP_STREAM_OP_READ);
+    ujson_stream_t s = {stream_obj, stream_p->read, 0, 0};
+    vstr_t vstr;
+    vstr_init(&vstr, 8);
+    mp_obj_list_t stack; // we use a list as a simple stack for nested JSON
+    stack.len = 0;
+    stack.items = NULL;
+    mp_obj_t stack_top = MP_OBJ_NULL;
+    mp_obj_type_t *stack_top_type = NULL;
+    mp_obj_t stack_key = MP_OBJ_NULL;
+    S_NEXT(s);
+    for (;;) {
+        cont:
+        if (S_END(s)) {
+            break;
+        }
+        mp_obj_t next = MP_OBJ_NULL;
+        bool enter = false;
+        byte cur = S_CUR(s);
+        S_NEXT(s);
+        switch (cur) {
+            case ',':
+            case ':':
+            case ' ':
+            case '\t':
+            case '\n':
+            case '\r':
+                goto cont;
+            case 'n':
+                if (S_CUR(s) == 'u' && S_NEXT(s) == 'l' && S_NEXT(s) == 'l') {
+                    S_NEXT(s);
+                    next = mp_const_none;
+                } else {
+                    goto fail;
+                }
+                break;
+            case 'f':
+                if (S_CUR(s) == 'a' && S_NEXT(s) == 'l' && S_NEXT(s) == 's' && S_NEXT(s) == 'e') {
+                    S_NEXT(s);
+                    next = mp_const_false;
+                } else {
+                    goto fail;
+                }
+                break;
+            case 't':
+                if (S_CUR(s) == 'r' && S_NEXT(s) == 'u' && S_NEXT(s) == 'e') {
+                    S_NEXT(s);
+                    next = mp_const_true;
+                } else {
+                    goto fail;
+                }
+                break;
+            case '"':
+                vstr_reset(&vstr);
+                for (; !S_END(s) && S_CUR(s) != '"';) {
+                    byte c = S_CUR(s);
+                    if (c == '\\') {
+                        c = S_NEXT(s);
+                        switch (c) {
+                            case 'b': c = 0x08; break;
+                            case 'f': c = 0x0c; break;
+                            case 'n': c = 0x0a; break;
+                            case 'r': c = 0x0d; break;
+                            case 't': c = 0x09; break;
+                            case 'u': {
+                                mp_uint_t num = 0;
+                                for (int i = 0; i < 4; i++) {
+                                    c = (S_NEXT(s) | 0x20) - '0';
+                                    if (c > 9) {
+                                        c -= ('a' - ('9' + 1));
+                                    }
+                                    num = (num << 4) | c;
+                                }
+                                vstr_add_char(&vstr, num);
+                                goto str_cont;
+                            }
+                        }
+                    }
+                    vstr_add_byte(&vstr, c);
+                str_cont:
+                    S_NEXT(s);
+                }
+                if (S_END(s)) {
+                    goto fail;
+                }
+                S_NEXT(s);
+                next = mp_obj_new_str(vstr.buf, vstr.len, false);
+                break;
+            case '-':
+            case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': {
+                bool flt = false;
+                vstr_reset(&vstr);
+                for (;;) {
+                    vstr_add_byte(&vstr, cur);
+                    cur = S_CUR(s);
+                    if (cur == '.' || cur == 'E' || cur == 'e') {
+                        flt = true;
+                    } else if (cur == '-' || unichar_isdigit(cur)) {
+                        // pass
+                    } else {
+                        break;
+                    }
+                    S_NEXT(s);
+                }
+                if (flt) {
+                    next = mp_parse_num_decimal(vstr.buf, vstr.len, false, false, NULL);
+                } else {
+                    next = mp_parse_num_integer(vstr.buf, vstr.len, 10, NULL);
+                }
+                break;
+            }
+            case '[':
+                next = mp_obj_new_list(0, NULL);
+                enter = true;
+                break;
+            case '{':
+                next = mp_obj_new_dict(0);
+                enter = true;
+                break;
+            case '}':
+            case ']': {
+                if (stack_top == MP_OBJ_NULL) {
+                    // no object at all
+                    goto fail;
+                }
+                if (stack.len == 0) {
+                    // finished; compound object
+                    goto success;
+                }
+                stack.len -= 1;
+                stack_top = stack.items[stack.len];
+                stack_top_type = mp_obj_get_type(stack_top);
+                goto cont;
+            }
+            default:
+                goto fail;
+        }
+        if (stack_top == MP_OBJ_NULL) {
+            stack_top = next;
+            stack_top_type = mp_obj_get_type(stack_top);
+            if (!enter) {
+                // finished; single primitive only
+                goto success;
+            }
+        } else {
+            // append to list or dict
+            if (stack_top_type == &mp_type_list) {
+                mp_obj_list_append(stack_top, next);
+            } else {
+                if (stack_key == MP_OBJ_NULL) {
+                    stack_key = next;
+                    if (enter) {
+                        goto fail;
+                    }
+                } else {
+                    mp_obj_dict_store(stack_top, stack_key, next);
+                    stack_key = MP_OBJ_NULL;
+                }
+            }
+            if (enter) {
+                if (stack.items == NULL) {
+                    mp_obj_list_init(&stack, 1);
+                    stack.items[0] = stack_top;
+                } else {
+                    mp_obj_list_append(MP_OBJ_FROM_PTR(&stack), stack_top);
+                }
+                stack_top = next;
+                stack_top_type = mp_obj_get_type(stack_top);
+            }
+        }
+    }
+    success:
+    // eat trailing whitespace
+    while (unichar_isspace(S_CUR(s))) {
+        S_NEXT(s);
+    }
+    if (!S_END(s)) {
+        // unexpected chars
+        goto fail;
+    }
+    if (stack_top == MP_OBJ_NULL || stack.len != 0) {
+        // not exactly 1 object
+        goto fail;
+    }
+    vstr_clear(&vstr);
+    return stack_top;
+
+    fail:
+    mp_raise_ValueError("syntax error in JSON");
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_ujson_load_obj, mod_ujson_load);
+
+STATIC mp_obj_t mod_ujson_loads(mp_obj_t obj) {
+    size_t len;
+    const char *buf = mp_obj_str_get_data(obj, &len);
+    vstr_t vstr = {len, len, (char*)buf, true};
+    mp_obj_stringio_t sio = {{&mp_type_stringio}, &vstr, 0, MP_OBJ_NULL};
+    return mod_ujson_load(MP_OBJ_FROM_PTR(&sio));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_ujson_loads_obj, mod_ujson_loads);
+
+STATIC const mp_rom_map_elem_t mp_module_ujson_globals_table[] = {
+    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ujson) },
+    { MP_ROM_QSTR(MP_QSTR_dumps), MP_ROM_PTR(&mod_ujson_dumps_obj) },
+    { MP_ROM_QSTR(MP_QSTR_load), MP_ROM_PTR(&mod_ujson_load_obj) },
+    { MP_ROM_QSTR(MP_QSTR_loads), MP_ROM_PTR(&mod_ujson_loads_obj) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(mp_module_ujson_globals, mp_module_ujson_globals_table);
+
+const mp_obj_module_t mp_module_ujson = {
+    .base = { &mp_type_module },
+    .globals = (mp_obj_dict_t*)&mp_module_ujson_globals,
+};
+
+#endif //MICROPY_PY_UJSON

+ 225 - 0
extmod/modurandom.c

@@ -0,0 +1,225 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Paul Sokolovsky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include "py/runtime.h"
+
+#if MICROPY_PY_URANDOM
+
+// Yasmarang random number generator
+// by Ilya Levin
+// http://www.literatecode.com/yasmarang
+// Public Domain
+
+STATIC uint32_t yasmarang_pad = 0xeda4baba, yasmarang_n = 69, yasmarang_d = 233;
+STATIC uint8_t yasmarang_dat = 0;
+
+STATIC uint32_t yasmarang(void)
+{
+   yasmarang_pad += yasmarang_dat + yasmarang_d * yasmarang_n;
+   yasmarang_pad = (yasmarang_pad<<3) + (yasmarang_pad>>29);
+   yasmarang_n = yasmarang_pad | 2;
+   yasmarang_d ^= (yasmarang_pad<<31) + (yasmarang_pad>>1);
+   yasmarang_dat ^= (char) yasmarang_pad ^ (yasmarang_d>>8) ^ 1;
+
+   return (yasmarang_pad^(yasmarang_d<<5)^(yasmarang_pad>>18)^(yasmarang_dat<<1));
+}  /* yasmarang */
+
+// End of Yasmarang
+
+#if MICROPY_PY_URANDOM_EXTRA_FUNCS
+
+// returns an unsigned integer below the given argument
+// n must not be zero
+STATIC uint32_t yasmarang_randbelow(uint32_t n) {
+    uint32_t mask = 1;
+    while ((n & mask) < n) {
+        mask = (mask << 1) | 1;
+    }
+    uint32_t r;
+    do {
+        r = yasmarang() & mask;
+    } while (r >= n);
+    return r;
+}
+
+#endif
+
+STATIC mp_obj_t mod_urandom_getrandbits(mp_obj_t num_in) {
+    int n = mp_obj_get_int(num_in);
+    if (n > 32 || n == 0) {
+        mp_raise_ValueError(NULL);
+    }
+    uint32_t mask = ~0;
+    // Beware of C undefined behavior when shifting by >= than bit size
+    mask >>= (32 - n);
+    return mp_obj_new_int_from_uint(yasmarang() & mask);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_urandom_getrandbits_obj, mod_urandom_getrandbits);
+
+STATIC mp_obj_t mod_urandom_seed(mp_obj_t seed_in) {
+    mp_uint_t seed = mp_obj_get_int_truncated(seed_in);
+    yasmarang_pad = seed;
+    yasmarang_n = 69;
+    yasmarang_d = 233;
+    yasmarang_dat = 0;
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_urandom_seed_obj, mod_urandom_seed);
+
+#if MICROPY_PY_URANDOM_EXTRA_FUNCS
+
+STATIC mp_obj_t mod_urandom_randrange(size_t n_args, const mp_obj_t *args) {
+    mp_int_t start = mp_obj_get_int(args[0]);
+    if (n_args == 1) {
+        // range(stop)
+        if (start > 0) {
+            return mp_obj_new_int(yasmarang_randbelow(start));
+        } else {
+            goto error;
+        }
+    } else {
+        mp_int_t stop = mp_obj_get_int(args[1]);
+        if (n_args == 2) {
+            // range(start, stop)
+            if (start < stop) {
+                return mp_obj_new_int(start + yasmarang_randbelow(stop - start));
+            } else {
+                goto error;
+            }
+        } else {
+            // range(start, stop, step)
+            mp_int_t step = mp_obj_get_int(args[2]);
+            mp_int_t n;
+            if (step > 0) {
+                n = (stop - start + step - 1) / step;
+            } else if (step < 0) {
+                n = (stop - start + step + 1) / step;
+            } else {
+                goto error;
+            }
+            if (n > 0) {
+                return mp_obj_new_int(start + step * yasmarang_randbelow(n));
+            } else {
+                goto error;
+            }
+        }
+    }
+
+error:
+    mp_raise_ValueError(NULL);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_urandom_randrange_obj, 1, 3, mod_urandom_randrange);
+
+STATIC mp_obj_t mod_urandom_randint(mp_obj_t a_in, mp_obj_t b_in) {
+    mp_int_t a = mp_obj_get_int(a_in);
+    mp_int_t b = mp_obj_get_int(b_in);
+    if (a <= b) {
+        return mp_obj_new_int(a + yasmarang_randbelow(b - a + 1));
+    } else {
+        mp_raise_ValueError(NULL);
+    }
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_urandom_randint_obj, mod_urandom_randint);
+
+STATIC mp_obj_t mod_urandom_choice(mp_obj_t seq) {
+    mp_int_t len = mp_obj_get_int(mp_obj_len(seq));
+    if (len > 0) {
+        return mp_obj_subscr(seq, mp_obj_new_int(yasmarang_randbelow(len)), MP_OBJ_SENTINEL);
+    } else {
+        nlr_raise(mp_obj_new_exception(&mp_type_IndexError));
+    }
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_urandom_choice_obj, mod_urandom_choice);
+
+#if MICROPY_PY_BUILTINS_FLOAT
+
+// returns a number in the range [0..1) using Yasmarang to fill in the fraction bits
+STATIC mp_float_t yasmarang_float(void) {
+    #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
+    typedef uint64_t mp_float_int_t;
+    #elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
+    typedef uint32_t mp_float_int_t;
+    #endif
+    union {
+        mp_float_t f;
+        #if MP_ENDIANNESS_LITTLE
+        struct { mp_float_int_t frc:MP_FLOAT_FRAC_BITS, exp:MP_FLOAT_EXP_BITS, sgn:1; } p;
+        #else
+        struct { mp_float_int_t sgn:1, exp:MP_FLOAT_EXP_BITS, frc:MP_FLOAT_FRAC_BITS; } p;
+        #endif
+    } u;
+    u.p.sgn = 0;
+    u.p.exp = (1 << (MP_FLOAT_EXP_BITS - 1)) - 1;
+    if (MP_FLOAT_FRAC_BITS <= 32) {
+        u.p.frc = yasmarang();
+    } else {
+        u.p.frc = ((uint64_t)yasmarang() << 32) | (uint64_t)yasmarang();
+    }
+    return u.f - 1;
+}
+
+STATIC mp_obj_t mod_urandom_random(void) {
+    return mp_obj_new_float(yasmarang_float());
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_urandom_random_obj, mod_urandom_random);
+
+STATIC mp_obj_t mod_urandom_uniform(mp_obj_t a_in, mp_obj_t b_in) {
+    mp_float_t a = mp_obj_get_float(a_in);
+    mp_float_t b = mp_obj_get_float(b_in);
+    return mp_obj_new_float(a + (b - a) * yasmarang_float());
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_urandom_uniform_obj, mod_urandom_uniform);
+
+#endif
+
+#endif // MICROPY_PY_URANDOM_EXTRA_FUNCS
+
+STATIC const mp_rom_map_elem_t mp_module_urandom_globals_table[] = {
+    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_urandom) },
+    { MP_ROM_QSTR(MP_QSTR_getrandbits), MP_ROM_PTR(&mod_urandom_getrandbits_obj) },
+    { MP_ROM_QSTR(MP_QSTR_seed), MP_ROM_PTR(&mod_urandom_seed_obj) },
+    #if MICROPY_PY_URANDOM_EXTRA_FUNCS
+    { MP_ROM_QSTR(MP_QSTR_randrange), MP_ROM_PTR(&mod_urandom_randrange_obj) },
+    { MP_ROM_QSTR(MP_QSTR_randint), MP_ROM_PTR(&mod_urandom_randint_obj) },
+    { MP_ROM_QSTR(MP_QSTR_choice), MP_ROM_PTR(&mod_urandom_choice_obj) },
+    #if MICROPY_PY_BUILTINS_FLOAT
+    { MP_ROM_QSTR(MP_QSTR_random), MP_ROM_PTR(&mod_urandom_random_obj) },
+    { MP_ROM_QSTR(MP_QSTR_uniform), MP_ROM_PTR(&mod_urandom_uniform_obj) },
+    #endif
+    #endif
+};
+
+STATIC MP_DEFINE_CONST_DICT(mp_module_urandom_globals, mp_module_urandom_globals_table);
+
+const mp_obj_module_t mp_module_urandom = {
+    .base = { &mp_type_module },
+    .globals = (mp_obj_dict_t*)&mp_module_urandom_globals,
+};
+
+#endif //MICROPY_PY_URANDOM

+ 257 - 0
extmod/modure.c

@@ -0,0 +1,257 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014 Paul Sokolovsky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+#include "py/runtime.h"
+#include "py/binary.h"
+#include "py/objstr.h"
+#include "py/stackctrl.h"
+
+#if MICROPY_PY_URE
+
+#define re1_5_stack_chk() MP_STACK_CHECK()
+
+#include "re1.5/re1.5.h"
+
+#define FLAG_DEBUG 0x1000
+
+typedef struct _mp_obj_re_t {
+    mp_obj_base_t base;
+    ByteProg re;
+} mp_obj_re_t;
+
+typedef struct _mp_obj_match_t {
+    mp_obj_base_t base;
+    int num_matches;
+    mp_obj_t str;
+    const char *caps[0];
+} mp_obj_match_t;
+
+
+STATIC void match_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
+    (void)kind;
+    mp_obj_match_t *self = MP_OBJ_TO_PTR(self_in);
+    mp_printf(print, "<match num=%d>", self->num_matches);
+}
+
+STATIC mp_obj_t match_group(mp_obj_t self_in, mp_obj_t no_in) {
+    mp_obj_match_t *self = MP_OBJ_TO_PTR(self_in);
+    mp_int_t no = mp_obj_get_int(no_in);
+    if (no < 0 || no >= self->num_matches) {
+        nlr_raise(mp_obj_new_exception_arg1(&mp_type_IndexError, no_in));
+    }
+
+    const char *start = self->caps[no * 2];
+    if (start == NULL) {
+        // no match for this group
+        return mp_const_none;
+    }
+    return mp_obj_new_str_of_type(mp_obj_get_type(self->str),
+        (const byte*)start, self->caps[no * 2 + 1] - start);
+}
+MP_DEFINE_CONST_FUN_OBJ_2(match_group_obj, match_group);
+
+STATIC const mp_rom_map_elem_t match_locals_dict_table[] = {
+    { MP_ROM_QSTR(MP_QSTR_group), MP_ROM_PTR(&match_group_obj) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(match_locals_dict, match_locals_dict_table);
+
+STATIC const mp_obj_type_t match_type = {
+    { &mp_type_type },
+    .name = MP_QSTR_match,
+    .print = match_print,
+    .locals_dict = (void*)&match_locals_dict,
+};
+
+STATIC void re_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
+    (void)kind;
+    mp_obj_re_t *self = MP_OBJ_TO_PTR(self_in);
+    mp_printf(print, "<re %p>", self);
+}
+
+STATIC mp_obj_t ure_exec(bool is_anchored, uint n_args, const mp_obj_t *args) {
+    (void)n_args;
+    mp_obj_re_t *self = MP_OBJ_TO_PTR(args[0]);
+    Subject subj;
+    size_t len;
+    subj.begin = mp_obj_str_get_data(args[1], &len);
+    subj.end = subj.begin + len;
+    int caps_num = (self->re.sub + 1) * 2;
+    mp_obj_match_t *match = m_new_obj_var(mp_obj_match_t, char*, caps_num);
+    // cast is a workaround for a bug in msvc: it treats const char** as a const pointer instead of a pointer to pointer to const char
+    memset((char*)match->caps, 0, caps_num * sizeof(char*));
+    int res = re1_5_recursiveloopprog(&self->re, &subj, match->caps, caps_num, is_anchored);
+    if (res == 0) {
+        m_del_var(mp_obj_match_t, char*, caps_num, match);
+        return mp_const_none;
+    }
+
+    match->base.type = &match_type;
+    match->num_matches = caps_num / 2; // caps_num counts start and end pointers
+    match->str = args[1];
+    return MP_OBJ_FROM_PTR(match);
+}
+
+STATIC mp_obj_t re_match(size_t n_args, const mp_obj_t *args) {
+    return ure_exec(true, n_args, args);
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(re_match_obj, 2, 4, re_match);
+
+STATIC mp_obj_t re_search(size_t n_args, const mp_obj_t *args) {
+    return ure_exec(false, n_args, args);
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(re_search_obj, 2, 4, re_search);
+
+STATIC mp_obj_t re_split(size_t n_args, const mp_obj_t *args) {
+    mp_obj_re_t *self = MP_OBJ_TO_PTR(args[0]);
+    Subject subj;
+    size_t len;
+    const mp_obj_type_t *str_type = mp_obj_get_type(args[1]);
+    subj.begin = mp_obj_str_get_data(args[1], &len);
+    subj.end = subj.begin + len;
+    int caps_num = (self->re.sub + 1) * 2;
+
+    int maxsplit = 0;
+    if (n_args > 2) {
+        maxsplit = mp_obj_get_int(args[2]);
+    }
+
+    mp_obj_t retval = mp_obj_new_list(0, NULL);
+    const char **caps = alloca(caps_num * sizeof(char*));
+    while (true) {
+        // cast is a workaround for a bug in msvc: it treats const char** as a const pointer instead of a pointer to pointer to const char
+        memset((char**)caps, 0, caps_num * sizeof(char*));
+        int res = re1_5_recursiveloopprog(&self->re, &subj, caps, caps_num, false);
+
+        // if we didn't have a match, or had an empty match, it's time to stop
+        if (!res || caps[0] == caps[1]) {
+            break;
+        }
+
+        mp_obj_t s = mp_obj_new_str_of_type(str_type, (const byte*)subj.begin, caps[0] - subj.begin);
+        mp_obj_list_append(retval, s);
+        if (self->re.sub > 0) {
+            mp_raise_NotImplementedError("Splitting with sub-captures");
+        }
+        subj.begin = caps[1];
+        if (maxsplit > 0 && --maxsplit == 0) {
+            break;
+        }
+    }
+
+    mp_obj_t s = mp_obj_new_str_of_type(str_type, (const byte*)subj.begin, subj.end - subj.begin);
+    mp_obj_list_append(retval, s);
+    return retval;
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(re_split_obj, 2, 3, re_split);
+
+STATIC const mp_rom_map_elem_t re_locals_dict_table[] = {
+    { MP_ROM_QSTR(MP_QSTR_match), MP_ROM_PTR(&re_match_obj) },
+    { MP_ROM_QSTR(MP_QSTR_search), MP_ROM_PTR(&re_search_obj) },
+    { MP_ROM_QSTR(MP_QSTR_split), MP_ROM_PTR(&re_split_obj) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(re_locals_dict, re_locals_dict_table);
+
+STATIC const mp_obj_type_t re_type = {
+    { &mp_type_type },
+    .name = MP_QSTR_ure,
+    .print = re_print,
+    .locals_dict = (void*)&re_locals_dict,
+};
+
+STATIC mp_obj_t mod_re_compile(size_t n_args, const mp_obj_t *args) {
+    const char *re_str = mp_obj_str_get_str(args[0]);
+    int size = re1_5_sizecode(re_str);
+    if (size == -1) {
+        goto error;
+    }
+    mp_obj_re_t *o = m_new_obj_var(mp_obj_re_t, char, size);
+    o->base.type = &re_type;
+    int flags = 0;
+    if (n_args > 1) {
+        flags = mp_obj_get_int(args[1]);
+    }
+    int error = re1_5_compilecode(&o->re, re_str);
+    if (error != 0) {
+error:
+        mp_raise_ValueError("Error in regex");
+    }
+    if (flags & FLAG_DEBUG) {
+        re1_5_dumpcode(&o->re);
+    }
+    return MP_OBJ_FROM_PTR(o);
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_re_compile_obj, 1, 2, mod_re_compile);
+
+STATIC mp_obj_t mod_re_exec(bool is_anchored, uint n_args, const mp_obj_t *args) {
+    (void)n_args;
+    mp_obj_t self = mod_re_compile(1, args);
+
+    const mp_obj_t args2[] = {self, args[1]};
+    mp_obj_t match = ure_exec(is_anchored, 2, args2);
+    return match;
+}
+
+STATIC mp_obj_t mod_re_match(size_t n_args, const mp_obj_t *args) {
+    return mod_re_exec(true, n_args, args);
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_re_match_obj, 2, 4, mod_re_match);
+
+STATIC mp_obj_t mod_re_search(size_t n_args, const mp_obj_t *args) {
+    return mod_re_exec(false, n_args, args);
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_re_search_obj, 2, 4, mod_re_search);
+
+STATIC const mp_rom_map_elem_t mp_module_re_globals_table[] = {
+    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ure) },
+    { MP_ROM_QSTR(MP_QSTR_compile), MP_ROM_PTR(&mod_re_compile_obj) },
+    { MP_ROM_QSTR(MP_QSTR_match), MP_ROM_PTR(&mod_re_match_obj) },
+    { MP_ROM_QSTR(MP_QSTR_search), MP_ROM_PTR(&mod_re_search_obj) },
+    { MP_ROM_QSTR(MP_QSTR_DEBUG), MP_ROM_INT(FLAG_DEBUG) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(mp_module_re_globals, mp_module_re_globals_table);
+
+const mp_obj_module_t mp_module_ure = {
+    .base = { &mp_type_module },
+    .globals = (mp_obj_dict_t*)&mp_module_re_globals,
+};
+
+// Source files #include'd here to make sure they're compiled in
+// only if module is enabled by config setting.
+
+#define re1_5_fatal(x) assert(!x)
+#include "re1.5/compilecode.c"
+#include "re1.5/dumpcode.c"
+#include "re1.5/recursiveloop.c"
+#include "re1.5/charclass.c"
+
+#endif //MICROPY_PY_URE

+ 378 - 0
extmod/moduselect.c

@@ -0,0 +1,378 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/mpconfig.h"
+#if MICROPY_PY_USELECT
+
+#include <stdio.h>
+
+#include "py/runtime.h"
+#include "py/obj.h"
+#include "py/objlist.h"
+#include "py/stream.h"
+#include "py/mperrno.h"
+#include "py/mphal.h"
+
+// Flags for poll()
+#define FLAG_ONESHOT (1)
+
+/// \module select - Provides select function to wait for events on a stream
+///
+/// This module provides the select function.
+
+typedef struct _poll_obj_t {
+    mp_obj_t obj;
+    mp_uint_t (*ioctl)(mp_obj_t obj, mp_uint_t request, mp_uint_t arg, int *errcode);
+    mp_uint_t flags;
+    mp_uint_t flags_ret;
+} poll_obj_t;
+
+STATIC void poll_map_add(mp_map_t *poll_map, const mp_obj_t *obj, mp_uint_t obj_len, mp_uint_t flags, bool or_flags) {
+    for (mp_uint_t i = 0; i < obj_len; i++) {
+        mp_map_elem_t *elem = mp_map_lookup(poll_map, mp_obj_id(obj[i]), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
+        if (elem->value == NULL) {
+            // object not found; get its ioctl and add it to the poll list
+            const mp_stream_p_t *stream_p = mp_get_stream_raise(obj[i], MP_STREAM_OP_IOCTL);
+            poll_obj_t *poll_obj = m_new_obj(poll_obj_t);
+            poll_obj->obj = obj[i];
+            poll_obj->ioctl = stream_p->ioctl;
+            poll_obj->flags = flags;
+            poll_obj->flags_ret = 0;
+            elem->value = poll_obj;
+        } else {
+            // object exists; update its flags
+            if (or_flags) {
+                ((poll_obj_t*)elem->value)->flags |= flags;
+            } else {
+                ((poll_obj_t*)elem->value)->flags = flags;
+            }
+        }
+    }
+}
+
+// poll each object in the map
+STATIC mp_uint_t poll_map_poll(mp_map_t *poll_map, mp_uint_t *rwx_num) {
+    mp_uint_t n_ready = 0;
+    for (mp_uint_t i = 0; i < poll_map->alloc; ++i) {
+        if (!MP_MAP_SLOT_IS_FILLED(poll_map, i)) {
+            continue;
+        }
+
+        poll_obj_t *poll_obj = (poll_obj_t*)poll_map->table[i].value;
+        int errcode;
+        mp_int_t ret = poll_obj->ioctl(poll_obj->obj, MP_STREAM_POLL, poll_obj->flags, &errcode);
+        poll_obj->flags_ret = ret;
+
+        if (ret == -1) {
+            // error doing ioctl
+            mp_raise_OSError(errcode);
+        }
+
+        if (ret != 0) {
+            // object is ready
+            n_ready += 1;
+            if (rwx_num != NULL) {
+                if (ret & MP_STREAM_POLL_RD) {
+                    rwx_num[0] += 1;
+                }
+                if (ret & MP_STREAM_POLL_WR) {
+                    rwx_num[1] += 1;
+                }
+                if ((ret & ~(MP_STREAM_POLL_RD | MP_STREAM_POLL_WR)) != 0) {
+                    rwx_num[2] += 1;
+                }
+            }
+        }
+    }
+    return n_ready;
+}
+
+/// \function select(rlist, wlist, xlist[, timeout])
+STATIC mp_obj_t select_select(uint n_args, const mp_obj_t *args) {
+    // get array data from tuple/list arguments
+    size_t rwx_len[3];
+    mp_obj_t *r_array, *w_array, *x_array;
+    mp_obj_get_array(args[0], &rwx_len[0], &r_array);
+    mp_obj_get_array(args[1], &rwx_len[1], &w_array);
+    mp_obj_get_array(args[2], &rwx_len[2], &x_array);
+
+    // get timeout
+    mp_uint_t timeout = -1;
+    if (n_args == 4) {
+        if (args[3] != mp_const_none) {
+            #if MICROPY_PY_BUILTINS_FLOAT
+            float timeout_f = mp_obj_get_float(args[3]);
+            if (timeout_f >= 0) {
+                timeout = (mp_uint_t)(timeout_f * 1000);
+            }
+            #else
+            timeout = mp_obj_get_int(args[3]) * 1000;
+            #endif
+        }
+    }
+
+    // merge separate lists and get the ioctl function for each object
+    mp_map_t poll_map;
+    mp_map_init(&poll_map, rwx_len[0] + rwx_len[1] + rwx_len[2]);
+    poll_map_add(&poll_map, r_array, rwx_len[0], MP_STREAM_POLL_RD, true);
+    poll_map_add(&poll_map, w_array, rwx_len[1], MP_STREAM_POLL_WR, true);
+    poll_map_add(&poll_map, x_array, rwx_len[2], MP_STREAM_POLL_ERR | MP_STREAM_POLL_HUP, true);
+
+    mp_uint_t start_tick = mp_hal_ticks_ms();
+    rwx_len[0] = rwx_len[1] = rwx_len[2] = 0;
+    for (;;) {
+        // poll the objects
+        mp_uint_t n_ready = poll_map_poll(&poll_map, rwx_len);
+
+        if (n_ready > 0 || (timeout != -1 && mp_hal_ticks_ms() - start_tick >= timeout)) {
+            // one or more objects are ready, or we had a timeout
+            mp_obj_t list_array[3];
+            list_array[0] = mp_obj_new_list(rwx_len[0], NULL);
+            list_array[1] = mp_obj_new_list(rwx_len[1], NULL);
+            list_array[2] = mp_obj_new_list(rwx_len[2], NULL);
+            rwx_len[0] = rwx_len[1] = rwx_len[2] = 0;
+            for (mp_uint_t i = 0; i < poll_map.alloc; ++i) {
+                if (!MP_MAP_SLOT_IS_FILLED(&poll_map, i)) {
+                    continue;
+                }
+                poll_obj_t *poll_obj = (poll_obj_t*)poll_map.table[i].value;
+                if (poll_obj->flags_ret & MP_STREAM_POLL_RD) {
+                    ((mp_obj_list_t*)list_array[0])->items[rwx_len[0]++] = poll_obj->obj;
+                }
+                if (poll_obj->flags_ret & MP_STREAM_POLL_WR) {
+                    ((mp_obj_list_t*)list_array[1])->items[rwx_len[1]++] = poll_obj->obj;
+                }
+                if ((poll_obj->flags_ret & ~(MP_STREAM_POLL_RD | MP_STREAM_POLL_WR)) != 0) {
+                    ((mp_obj_list_t*)list_array[2])->items[rwx_len[2]++] = poll_obj->obj;
+                }
+            }
+            mp_map_deinit(&poll_map);
+            return mp_obj_new_tuple(3, list_array);
+        }
+        MICROPY_EVENT_POLL_HOOK
+    }
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_select_select_obj, 3, 4, select_select);
+
+/// \class Poll - poll class
+
+typedef struct _mp_obj_poll_t {
+    mp_obj_base_t base;
+    mp_map_t poll_map;
+    short iter_cnt;
+    short iter_idx;
+    int flags;
+    // callee-owned tuple
+    mp_obj_t ret_tuple;
+} mp_obj_poll_t;
+
+/// \method register(obj[, eventmask])
+STATIC mp_obj_t poll_register(uint n_args, const mp_obj_t *args) {
+    mp_obj_poll_t *self = args[0];
+    mp_uint_t flags;
+    if (n_args == 3) {
+        flags = mp_obj_get_int(args[2]);
+    } else {
+        flags = MP_STREAM_POLL_RD | MP_STREAM_POLL_WR;
+    }
+    poll_map_add(&self->poll_map, &args[1], 1, flags, false);
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_register_obj, 2, 3, poll_register);
+
+/// \method unregister(obj)
+STATIC mp_obj_t poll_unregister(mp_obj_t self_in, mp_obj_t obj_in) {
+    mp_obj_poll_t *self = self_in;
+    mp_map_lookup(&self->poll_map, mp_obj_id(obj_in), MP_MAP_LOOKUP_REMOVE_IF_FOUND);
+    // TODO raise KeyError if obj didn't exist in map
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_2(poll_unregister_obj, poll_unregister);
+
+/// \method modify(obj, eventmask)
+STATIC mp_obj_t poll_modify(mp_obj_t self_in, mp_obj_t obj_in, mp_obj_t eventmask_in) {
+    mp_obj_poll_t *self = self_in;
+    mp_map_elem_t *elem = mp_map_lookup(&self->poll_map, mp_obj_id(obj_in), MP_MAP_LOOKUP);
+    if (elem == NULL) {
+        mp_raise_OSError(MP_ENOENT);
+    }
+    ((poll_obj_t*)elem->value)->flags = mp_obj_get_int(eventmask_in);
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_3(poll_modify_obj, poll_modify);
+
+STATIC mp_uint_t poll_poll_internal(uint n_args, const mp_obj_t *args) {
+    mp_obj_poll_t *self = args[0];
+
+    // work out timeout (its given already in ms)
+    mp_uint_t timeout = -1;
+    int flags = 0;
+    if (n_args >= 2) {
+        if (args[1] != mp_const_none) {
+            mp_int_t timeout_i = mp_obj_get_int(args[1]);
+            if (timeout_i >= 0) {
+                timeout = timeout_i;
+            }
+        }
+        if (n_args >= 3) {
+            flags = mp_obj_get_int(args[2]);
+        }
+    }
+
+    self->flags = flags;
+
+    mp_uint_t start_tick = mp_hal_ticks_ms();
+    mp_uint_t n_ready;
+    for (;;) {
+        // poll the objects
+        n_ready = poll_map_poll(&self->poll_map, NULL);
+        if (n_ready > 0 || (timeout != -1 && mp_hal_ticks_ms() - start_tick >= timeout)) {
+            break;
+        }
+        MICROPY_EVENT_POLL_HOOK
+    }
+
+    return n_ready;
+}
+
+STATIC mp_obj_t poll_poll(uint n_args, const mp_obj_t *args) {
+    mp_obj_poll_t *self = args[0];
+    mp_uint_t n_ready = poll_poll_internal(n_args, args);
+
+    // one or more objects are ready, or we had a timeout
+    mp_obj_list_t *ret_list = mp_obj_new_list(n_ready, NULL);
+    n_ready = 0;
+    for (mp_uint_t i = 0; i < self->poll_map.alloc; ++i) {
+        if (!MP_MAP_SLOT_IS_FILLED(&self->poll_map, i)) {
+            continue;
+        }
+        poll_obj_t *poll_obj = (poll_obj_t*)self->poll_map.table[i].value;
+        if (poll_obj->flags_ret != 0) {
+            mp_obj_t tuple[2] = {poll_obj->obj, MP_OBJ_NEW_SMALL_INT(poll_obj->flags_ret)};
+            ret_list->items[n_ready++] = mp_obj_new_tuple(2, tuple);
+            if (self->flags & FLAG_ONESHOT) {
+                // Don't poll next time, until new event flags will be set explicitly
+                poll_obj->flags = 0;
+            }
+        }
+    }
+    return ret_list;
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_poll_obj, 1, 3, poll_poll);
+
+STATIC mp_obj_t poll_ipoll(size_t n_args, const mp_obj_t *args) {
+    mp_obj_poll_t *self = MP_OBJ_TO_PTR(args[0]);
+
+    if (self->ret_tuple == MP_OBJ_NULL) {
+        self->ret_tuple = mp_obj_new_tuple(2, NULL);
+    }
+
+    int n_ready = poll_poll_internal(n_args, args);
+    self->iter_cnt = n_ready;
+    self->iter_idx = 0;
+
+    return args[0];
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_ipoll_obj, 1, 3, poll_ipoll);
+
+STATIC mp_obj_t poll_iternext(mp_obj_t self_in) {
+    mp_obj_poll_t *self = MP_OBJ_TO_PTR(self_in);
+
+    if (self->iter_cnt == 0) {
+        return MP_OBJ_STOP_ITERATION;
+    }
+
+    self->iter_cnt--;
+
+    for (mp_uint_t i = self->iter_idx; i < self->poll_map.alloc; ++i) {
+        self->iter_idx++;
+        if (!MP_MAP_SLOT_IS_FILLED(&self->poll_map, i)) {
+            continue;
+        }
+        poll_obj_t *poll_obj = (poll_obj_t*)self->poll_map.table[i].value;
+        if (poll_obj->flags_ret != 0) {
+            mp_obj_tuple_t *t = MP_OBJ_TO_PTR(self->ret_tuple);
+            t->items[0] = poll_obj->obj;
+            t->items[1] = MP_OBJ_NEW_SMALL_INT(poll_obj->flags_ret);
+            if (self->flags & FLAG_ONESHOT) {
+                // Don't poll next time, until new event flags will be set explicitly
+                poll_obj->flags = 0;
+            }
+            return MP_OBJ_FROM_PTR(t);
+        }
+    }
+
+    assert(!"inconsistent number of poll active entries");
+    self->iter_cnt = 0;
+    return MP_OBJ_STOP_ITERATION;
+}
+
+STATIC const mp_rom_map_elem_t poll_locals_dict_table[] = {
+    { MP_ROM_QSTR(MP_QSTR_register), MP_ROM_PTR(&poll_register_obj) },
+    { MP_ROM_QSTR(MP_QSTR_unregister), MP_ROM_PTR(&poll_unregister_obj) },
+    { MP_ROM_QSTR(MP_QSTR_modify), MP_ROM_PTR(&poll_modify_obj) },
+    { MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&poll_poll_obj) },
+    { MP_ROM_QSTR(MP_QSTR_ipoll), MP_ROM_PTR(&poll_ipoll_obj) },
+};
+STATIC MP_DEFINE_CONST_DICT(poll_locals_dict, poll_locals_dict_table);
+
+STATIC const mp_obj_type_t mp_type_poll = {
+    { &mp_type_type },
+    .name = MP_QSTR_poll,
+    .getiter = mp_identity_getiter,
+    .iternext = poll_iternext,
+    .locals_dict = (void*)&poll_locals_dict,
+};
+
+/// \function poll()
+STATIC mp_obj_t select_poll(void) {
+    mp_obj_poll_t *poll = m_new_obj(mp_obj_poll_t);
+    poll->base.type = &mp_type_poll;
+    mp_map_init(&poll->poll_map, 0);
+    poll->iter_cnt = 0;
+    poll->ret_tuple = MP_OBJ_NULL;
+    return poll;
+}
+MP_DEFINE_CONST_FUN_OBJ_0(mp_select_poll_obj, select_poll);
+
+STATIC const mp_rom_map_elem_t mp_module_select_globals_table[] = {
+    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uselect) },
+    { MP_ROM_QSTR(MP_QSTR_select), MP_ROM_PTR(&mp_select_select_obj) },
+    { MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&mp_select_poll_obj) },
+    { MP_ROM_QSTR(MP_QSTR_POLLIN), MP_ROM_INT(MP_STREAM_POLL_RD) },
+    { MP_ROM_QSTR(MP_QSTR_POLLOUT), MP_ROM_INT(MP_STREAM_POLL_WR) },
+    { MP_ROM_QSTR(MP_QSTR_POLLERR), MP_ROM_INT(MP_STREAM_POLL_ERR) },
+    { MP_ROM_QSTR(MP_QSTR_POLLHUP), MP_ROM_INT(MP_STREAM_POLL_HUP) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(mp_module_select_globals, mp_module_select_globals_table);
+
+const mp_obj_module_t mp_module_uselect = {
+    .base = { &mp_type_module },
+    .globals = (mp_obj_dict_t*)&mp_module_select_globals,
+};
+
+#endif // MICROPY_PY_USELECT

+ 238 - 0
extmod/modussl_axtls.c

@@ -0,0 +1,238 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015-2017 Paul Sokolovsky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "py/runtime.h"
+#include "py/stream.h"
+
+#if MICROPY_PY_USSL && MICROPY_SSL_AXTLS
+
+#include "ssl.h"
+
+typedef struct _mp_obj_ssl_socket_t {
+    mp_obj_base_t base;
+    mp_obj_t sock;
+    SSL_CTX *ssl_ctx;
+    SSL *ssl_sock;
+    byte *buf;
+    uint32_t bytes_left;
+} mp_obj_ssl_socket_t;
+
+struct ssl_args {
+    mp_arg_val_t server_side;
+    mp_arg_val_t server_hostname;
+};
+
+STATIC const mp_obj_type_t ussl_socket_type;
+
+STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) {
+#if MICROPY_PY_USSL_FINALISER
+    mp_obj_ssl_socket_t *o = m_new_obj_with_finaliser(mp_obj_ssl_socket_t);
+#else
+    mp_obj_ssl_socket_t *o = m_new_obj(mp_obj_ssl_socket_t);
+#endif
+    o->base.type = &ussl_socket_type;
+    o->buf = NULL;
+    o->bytes_left = 0;
+    o->sock = sock;
+
+    uint32_t options = SSL_SERVER_VERIFY_LATER;
+    if ((o->ssl_ctx = ssl_ctx_new(options, SSL_DEFAULT_CLNT_SESS)) == NULL) {
+        mp_raise_OSError(MP_EINVAL);
+    }
+
+    if (args->server_side.u_bool) {
+        o->ssl_sock = ssl_server_new(o->ssl_ctx, (long)sock);
+    } else {
+        SSL_EXTENSIONS *ext = ssl_ext_new();
+
+        if (args->server_hostname.u_obj != mp_const_none) {
+            ext->host_name = (char*)mp_obj_str_get_str(args->server_hostname.u_obj);
+        }
+
+        o->ssl_sock = ssl_client_new(o->ssl_ctx, (long)sock, NULL, 0, ext);
+
+        int res = ssl_handshake_status(o->ssl_sock);
+        // Pointer to SSL_EXTENSIONS as being passed to ssl_client_new()
+        // is saved in ssl_sock->extensions.
+        // As of axTLS 2.1.3, extensions aren't used beyond the initial
+        // handshake, and that's pretty much how it's expected to be. So
+        // we allocate them on stack and reset the pointer after handshake.
+
+        if (res != SSL_OK) {
+            printf("ssl_handshake_status: %d\n", res);
+            ssl_display_error(res);
+            mp_raise_OSError(MP_EIO);
+        }
+
+    }
+
+    return o;
+}
+
+STATIC void socket_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
+    (void)kind;
+    mp_obj_ssl_socket_t *self = MP_OBJ_TO_PTR(self_in);
+    mp_printf(print, "<_SSLSocket %p>", self->ssl_sock);
+}
+
+STATIC mp_uint_t socket_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) {
+    mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(o_in);
+
+    if (o->ssl_sock == NULL) {
+        *errcode = EBADF;
+        return MP_STREAM_ERROR;
+    }
+
+    while (o->bytes_left == 0) {
+        mp_int_t r = ssl_read(o->ssl_sock, &o->buf);
+        if (r == SSL_OK) {
+            // SSL_OK from ssl_read() means "everything is ok, but there's
+            // not user data yet. So, we just keep reading.
+            continue;
+        }
+        if (r < 0) {
+            if (r == SSL_CLOSE_NOTIFY || r == SSL_ERROR_CONN_LOST) {
+                // EOF
+                return 0;
+            }
+            *errcode = r;
+            return MP_STREAM_ERROR;
+        }
+        o->bytes_left = r;
+    }
+
+    if (size > o->bytes_left) {
+        size = o->bytes_left;
+    }
+    memcpy(buf, o->buf, size);
+    o->buf += size;
+    o->bytes_left -= size;
+    return size;
+}
+
+STATIC mp_uint_t socket_write(mp_obj_t o_in, const void *buf, mp_uint_t size, int *errcode) {
+    mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(o_in);
+
+    if (o->ssl_sock == NULL) {
+        *errcode = EBADF;
+        return MP_STREAM_ERROR;
+    }
+
+    mp_int_t r = ssl_write(o->ssl_sock, buf, size);
+    if (r < 0) {
+        *errcode = r;
+        return MP_STREAM_ERROR;
+    }
+    return r;
+}
+
+STATIC mp_obj_t socket_setblocking(mp_obj_t self_in, mp_obj_t flag_in) {
+    // Currently supports only blocking mode
+    (void)self_in;
+    if (!mp_obj_is_true(flag_in)) {
+        mp_raise_NotImplementedError(NULL);
+    }
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_setblocking_obj, socket_setblocking);
+
+STATIC mp_obj_t socket_close(mp_obj_t self_in) {
+    mp_obj_ssl_socket_t *self = MP_OBJ_TO_PTR(self_in);
+    if (self->ssl_sock != NULL) {
+        ssl_free(self->ssl_sock);
+        ssl_ctx_free(self->ssl_ctx);
+        self->ssl_sock = NULL;
+        return mp_stream_close(self->sock);
+    }
+
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(socket_close_obj, socket_close);
+
+STATIC const mp_rom_map_elem_t ussl_socket_locals_dict_table[] = {
+    { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
+    { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
+    { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) },
+    { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
+    { MP_ROM_QSTR(MP_QSTR_setblocking), MP_ROM_PTR(&socket_setblocking_obj) },
+    { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&socket_close_obj) },
+#if MICROPY_PY_USSL_FINALISER
+    { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&socket_close_obj) },
+#endif
+};
+
+STATIC MP_DEFINE_CONST_DICT(ussl_socket_locals_dict, ussl_socket_locals_dict_table);
+
+STATIC const mp_stream_p_t ussl_socket_stream_p = {
+    .read = socket_read,
+    .write = socket_write,
+};
+
+STATIC const mp_obj_type_t ussl_socket_type = {
+    { &mp_type_type },
+    // Save on qstr's, reuse same as for module
+    .name = MP_QSTR_ussl,
+    .print = socket_print,
+    .getiter = NULL,
+    .iternext = NULL,
+    .protocol = &ussl_socket_stream_p,
+    .locals_dict = (void*)&ussl_socket_locals_dict,
+};
+
+STATIC mp_obj_t mod_ssl_wrap_socket(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+    // TODO: Implement more args
+    static const mp_arg_t allowed_args[] = {
+        { MP_QSTR_server_side, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
+        { MP_QSTR_server_hostname, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
+    };
+
+    // TODO: Check that sock implements stream protocol
+    mp_obj_t sock = pos_args[0];
+
+    struct ssl_args args;
+    mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args,
+        MP_ARRAY_SIZE(allowed_args), allowed_args, (mp_arg_val_t*)&args);
+
+    return MP_OBJ_FROM_PTR(socket_new(sock, &args));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_ssl_wrap_socket_obj, 1, mod_ssl_wrap_socket);
+
+STATIC const mp_rom_map_elem_t mp_module_ssl_globals_table[] = {
+    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ussl) },
+    { MP_ROM_QSTR(MP_QSTR_wrap_socket), MP_ROM_PTR(&mod_ssl_wrap_socket_obj) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(mp_module_ssl_globals, mp_module_ssl_globals_table);
+
+const mp_obj_module_t mp_module_ussl = {
+    .base = { &mp_type_module },
+    .globals = (mp_obj_dict_t*)&mp_module_ssl_globals,
+};
+
+#endif // MICROPY_PY_USSL

+ 342 - 0
extmod/modussl_mbedtls.c

@@ -0,0 +1,342 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Linaro Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/mpconfig.h"
+#if MICROPY_PY_USSL && MICROPY_SSL_MBEDTLS
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h> // needed because mp_is_nonblocking_error uses system error codes
+
+#include "py/runtime.h"
+#include "py/stream.h"
+
+// mbedtls_time_t
+#include "mbedtls/platform.h"
+#include "mbedtls/net.h"
+#include "mbedtls/ssl.h"
+#include "mbedtls/x509_crt.h"
+#include "mbedtls/pk.h"
+#include "mbedtls/entropy.h"
+#include "mbedtls/ctr_drbg.h"
+#include "mbedtls/debug.h"
+
+typedef struct _mp_obj_ssl_socket_t {
+    mp_obj_base_t base;
+    mp_obj_t sock;
+    mbedtls_entropy_context entropy;
+    mbedtls_ctr_drbg_context ctr_drbg;
+    mbedtls_ssl_context ssl;
+    mbedtls_ssl_config conf;
+    mbedtls_x509_crt cacert;
+    mbedtls_x509_crt cert;
+    mbedtls_pk_context pkey;
+} mp_obj_ssl_socket_t;
+
+struct ssl_args {
+    mp_arg_val_t key;
+    mp_arg_val_t cert;
+    mp_arg_val_t server_side;
+    mp_arg_val_t server_hostname;
+};
+
+STATIC const mp_obj_type_t ussl_socket_type;
+
+#ifdef MBEDTLS_DEBUG_C
+STATIC void mbedtls_debug(void *ctx, int level, const char *file, int line, const char *str) {
+    (void)ctx;
+    (void)level;
+    printf("DBG:%s:%04d: %s\n", file, line, str);
+}
+#endif
+
+// TODO: FIXME!
+STATIC int null_entropy_func(void *data, unsigned char *output, size_t len) {
+    (void)data;
+    (void)output;
+    (void)len;
+    // enjoy random bytes
+    return 0;
+}
+
+STATIC int _mbedtls_ssl_send(void *ctx, const byte *buf, size_t len) {
+    mp_obj_t sock = *(mp_obj_t*)ctx;
+
+    const mp_stream_p_t *sock_stream = mp_get_stream_raise(sock, MP_STREAM_OP_WRITE);
+    int err;
+
+    mp_uint_t out_sz = sock_stream->write(sock, buf, len, &err);
+    if (out_sz == MP_STREAM_ERROR) {
+        if (mp_is_nonblocking_error(err)) {
+            return MBEDTLS_ERR_SSL_WANT_WRITE;
+        }
+        return -err;
+    } else {
+        return out_sz;
+    }
+}
+
+STATIC int _mbedtls_ssl_recv(void *ctx, byte *buf, size_t len) {
+    mp_obj_t sock = *(mp_obj_t*)ctx;
+
+    const mp_stream_p_t *sock_stream = mp_get_stream_raise(sock, MP_STREAM_OP_READ);
+    int err;
+
+    mp_uint_t out_sz = sock_stream->read(sock, buf, len, &err);
+    if (out_sz == MP_STREAM_ERROR) {
+        if (mp_is_nonblocking_error(err)) {
+            return MBEDTLS_ERR_SSL_WANT_READ;
+        }
+        return -err;
+    } else {
+        return out_sz;
+    }
+}
+
+
+STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) {
+#if MICROPY_PY_USSL_FINALISER
+    mp_obj_ssl_socket_t *o = m_new_obj_with_finaliser(mp_obj_ssl_socket_t);
+#else
+    mp_obj_ssl_socket_t *o = m_new_obj(mp_obj_ssl_socket_t);
+#endif
+    o->base.type = &ussl_socket_type;
+
+    int ret;
+    mbedtls_ssl_init(&o->ssl);
+    mbedtls_ssl_config_init(&o->conf);
+    mbedtls_x509_crt_init(&o->cacert);
+    mbedtls_x509_crt_init(&o->cert);
+    mbedtls_pk_init(&o->pkey);
+    mbedtls_ctr_drbg_init(&o->ctr_drbg);
+    #ifdef MBEDTLS_DEBUG_C
+    // Debug level (0-4)
+    mbedtls_debug_set_threshold(0);
+    #endif
+
+    mbedtls_entropy_init(&o->entropy);
+    const byte seed[] = "upy";
+    ret = mbedtls_ctr_drbg_seed(&o->ctr_drbg, null_entropy_func/*mbedtls_entropy_func*/, &o->entropy, seed, sizeof(seed));
+    if (ret != 0) {
+        printf("ret=%d\n", ret);
+        assert(0);
+    }
+
+    ret = mbedtls_ssl_config_defaults(&o->conf,
+                    args->server_side.u_bool ? MBEDTLS_SSL_IS_SERVER : MBEDTLS_SSL_IS_CLIENT,
+                    MBEDTLS_SSL_TRANSPORT_STREAM,
+                    MBEDTLS_SSL_PRESET_DEFAULT);
+    if (ret != 0) {
+        assert(0);
+    }
+
+    mbedtls_ssl_conf_authmode(&o->conf, MBEDTLS_SSL_VERIFY_NONE);
+    mbedtls_ssl_conf_rng(&o->conf, mbedtls_ctr_drbg_random, &o->ctr_drbg);
+    #ifdef MBEDTLS_DEBUG_C
+    mbedtls_ssl_conf_dbg(&o->conf, mbedtls_debug, NULL);
+    #endif
+
+    ret = mbedtls_ssl_setup(&o->ssl, &o->conf);
+    if (ret != 0) {
+        assert(0);
+    }
+
+    if (args->server_hostname.u_obj != mp_const_none) {
+        const char *sni = mp_obj_str_get_str(args->server_hostname.u_obj);
+        ret = mbedtls_ssl_set_hostname(&o->ssl, sni);
+        if (ret != 0) {
+            assert(0);
+        }
+    }
+
+    o->sock = sock;
+    mbedtls_ssl_set_bio(&o->ssl, &o->sock, _mbedtls_ssl_send, _mbedtls_ssl_recv, NULL);
+
+    if (args->key.u_obj != MP_OBJ_NULL) {
+        size_t key_len;
+        const byte *key = (const byte*)mp_obj_str_get_data(args->key.u_obj, &key_len);
+        // len should include terminating null
+        ret = mbedtls_pk_parse_key(&o->pkey, key, key_len + 1, NULL, 0);
+        assert(ret == 0);
+
+        size_t cert_len;
+        const byte *cert = (const byte*)mp_obj_str_get_data(args->cert.u_obj, &cert_len);
+        // len should include terminating null
+        ret = mbedtls_x509_crt_parse(&o->cert, cert, cert_len + 1);
+        assert(ret == 0);
+
+        ret = mbedtls_ssl_conf_own_cert(&o->conf, &o->cert, &o->pkey);
+        assert(ret == 0);
+    }
+
+    while ((ret = mbedtls_ssl_handshake(&o->ssl)) != 0) {
+        if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
+            //assert(0);
+            printf("mbedtls_ssl_handshake error: -%x\n", -ret);
+            mp_raise_OSError(MP_EIO);
+        }
+    }
+
+    return o;
+}
+
+STATIC mp_obj_t mod_ssl_getpeercert(mp_obj_t o_in, mp_obj_t binary_form) {
+    mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(o_in);
+    if (!mp_obj_is_true(binary_form)) {
+        mp_raise_NotImplementedError(NULL);
+    }
+    const mbedtls_x509_crt* peer_cert = mbedtls_ssl_get_peer_cert(&o->ssl);
+    return mp_obj_new_bytes(peer_cert->raw.p, peer_cert->raw.len);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_ssl_getpeercert_obj, mod_ssl_getpeercert);
+
+STATIC void socket_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
+    (void)kind;
+    mp_obj_ssl_socket_t *self = MP_OBJ_TO_PTR(self_in);
+    mp_printf(print, "<_SSLSocket %p>", self);
+}
+
+STATIC mp_uint_t socket_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) {
+    mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(o_in);
+
+    int ret = mbedtls_ssl_read(&o->ssl, buf, size);
+    if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
+        // end of stream
+        return 0;
+    }
+    if (ret >= 0) {
+        return ret;
+    }
+    if (ret == MBEDTLS_ERR_SSL_WANT_READ) {
+        ret = MP_EWOULDBLOCK;
+    }
+    *errcode = ret;
+    return MP_STREAM_ERROR;
+}
+
+STATIC mp_uint_t socket_write(mp_obj_t o_in, const void *buf, mp_uint_t size, int *errcode) {
+    mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(o_in);
+
+    int ret = mbedtls_ssl_write(&o->ssl, buf, size);
+    if (ret >= 0) {
+        return ret;
+    }
+    if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
+        ret = MP_EWOULDBLOCK;
+    }
+    *errcode = ret;
+    return MP_STREAM_ERROR;
+}
+
+STATIC mp_obj_t socket_setblocking(mp_obj_t self_in, mp_obj_t flag_in) {
+    mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(self_in);
+    mp_obj_t sock = o->sock;
+    mp_obj_t dest[3];
+    mp_load_method(sock, MP_QSTR_setblocking, dest);
+    dest[2] = flag_in;
+    return mp_call_method_n_kw(1, 0, dest);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_setblocking_obj, socket_setblocking);
+
+STATIC mp_obj_t socket_close(mp_obj_t self_in) {
+    mp_obj_ssl_socket_t *self = MP_OBJ_TO_PTR(self_in);
+
+    mbedtls_pk_free(&self->pkey);
+    mbedtls_x509_crt_free(&self->cert);
+    mbedtls_x509_crt_free(&self->cacert);
+    mbedtls_ssl_free(&self->ssl);
+    mbedtls_ssl_config_free(&self->conf);
+    mbedtls_ctr_drbg_free(&self->ctr_drbg);
+    mbedtls_entropy_free(&self->entropy);
+
+    return mp_stream_close(self->sock);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(socket_close_obj, socket_close);
+
+STATIC const mp_rom_map_elem_t ussl_socket_locals_dict_table[] = {
+    { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
+    { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
+    { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) },
+    { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
+    { MP_ROM_QSTR(MP_QSTR_setblocking), MP_ROM_PTR(&socket_setblocking_obj) },
+    { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&socket_close_obj) },
+#if MICROPY_PY_USSL_FINALISER
+    { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&socket_close_obj) },
+#endif
+    { MP_ROM_QSTR(MP_QSTR_getpeercert), MP_ROM_PTR(&mod_ssl_getpeercert_obj) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(ussl_socket_locals_dict, ussl_socket_locals_dict_table);
+
+STATIC const mp_stream_p_t ussl_socket_stream_p = {
+    .read = socket_read,
+    .write = socket_write,
+};
+
+STATIC const mp_obj_type_t ussl_socket_type = {
+    { &mp_type_type },
+    // Save on qstr's, reuse same as for module
+    .name = MP_QSTR_ussl,
+    .print = socket_print,
+    .getiter = NULL,
+    .iternext = NULL,
+    .protocol = &ussl_socket_stream_p,
+    .locals_dict = (void*)&ussl_socket_locals_dict,
+};
+
+STATIC mp_obj_t mod_ssl_wrap_socket(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+    // TODO: Implement more args
+    static const mp_arg_t allowed_args[] = {
+        { MP_QSTR_key, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
+        { MP_QSTR_cert, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
+        { MP_QSTR_server_side, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
+        { MP_QSTR_server_hostname, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
+    };
+
+    // TODO: Check that sock implements stream protocol
+    mp_obj_t sock = pos_args[0];
+
+    struct ssl_args args;
+    mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args,
+        MP_ARRAY_SIZE(allowed_args), allowed_args, (mp_arg_val_t*)&args);
+
+    return MP_OBJ_FROM_PTR(socket_new(sock, &args));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_ssl_wrap_socket_obj, 1, mod_ssl_wrap_socket);
+
+STATIC const mp_rom_map_elem_t mp_module_ssl_globals_table[] = {
+    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ussl) },
+    { MP_ROM_QSTR(MP_QSTR_wrap_socket), MP_ROM_PTR(&mod_ssl_wrap_socket_obj) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(mp_module_ssl_globals, mp_module_ssl_globals_table);
+
+const mp_obj_module_t mp_module_ussl = {
+    .base = { &mp_type_module },
+    .globals = (mp_obj_dict_t*)&mp_module_ssl_globals,
+};
+
+#endif // MICROPY_PY_USSL

+ 230 - 0
extmod/modutimeq.c

@@ -0,0 +1,230 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014 Damien P. George
+ * Copyright (c) 2016-2017 Paul Sokolovsky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <string.h>
+
+#include "py/objlist.h"
+#include "py/runtime.h"
+#include "py/smallint.h"
+
+#if MICROPY_PY_UTIMEQ
+
+#define MODULO MICROPY_PY_UTIME_TICKS_PERIOD
+
+#define DEBUG 0
+
+// the algorithm here is modelled on CPython's heapq.py
+
+struct qentry {
+    mp_uint_t time;
+    mp_uint_t id;
+    mp_obj_t callback;
+    mp_obj_t args;
+};
+
+typedef struct _mp_obj_utimeq_t {
+    mp_obj_base_t base;
+    mp_uint_t alloc;
+    mp_uint_t len;
+    struct qentry items[];
+} mp_obj_utimeq_t;
+
+STATIC mp_uint_t utimeq_id;
+
+STATIC mp_obj_utimeq_t *get_heap(mp_obj_t heap_in) {
+    return MP_OBJ_TO_PTR(heap_in);
+}
+
+STATIC bool time_less_than(struct qentry *item, struct qentry *parent) {
+    mp_uint_t item_tm = item->time;
+    mp_uint_t parent_tm = parent->time;
+    mp_uint_t res = parent_tm - item_tm;
+    if (res == 0) {
+        // TODO: This actually should use the same "ring" logic
+        // as for time, to avoid artifacts when id's overflow.
+        return item->id < parent->id;
+    }
+    if ((mp_int_t)res < 0) {
+        res += MODULO;
+    }
+    return res && res < (MODULO / 2);
+}
+
+STATIC mp_obj_t utimeq_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+    mp_arg_check_num(n_args, n_kw, 1, 1, false);
+    mp_uint_t alloc = mp_obj_get_int(args[0]);
+    mp_obj_utimeq_t *o = m_new_obj_var(mp_obj_utimeq_t, struct qentry, alloc);
+    o->base.type = type;
+    memset(o->items, 0, sizeof(*o->items) * alloc);
+    o->alloc = alloc;
+    o->len = 0;
+    return MP_OBJ_FROM_PTR(o);
+}
+
+STATIC void heap_siftdown(mp_obj_utimeq_t *heap, mp_uint_t start_pos, mp_uint_t pos) {
+    struct qentry item = heap->items[pos];
+    while (pos > start_pos) {
+        mp_uint_t parent_pos = (pos - 1) >> 1;
+        struct qentry *parent = &heap->items[parent_pos];
+        bool lessthan = time_less_than(&item, parent);
+        if (lessthan) {
+            heap->items[pos] = *parent;
+            pos = parent_pos;
+        } else {
+            break;
+        }
+    }
+    heap->items[pos] = item;
+}
+
+STATIC void heap_siftup(mp_obj_utimeq_t *heap, mp_uint_t pos) {
+    mp_uint_t start_pos = pos;
+    mp_uint_t end_pos = heap->len;
+    struct qentry item = heap->items[pos];
+    for (mp_uint_t child_pos = 2 * pos + 1; child_pos < end_pos; child_pos = 2 * pos + 1) {
+        // choose right child if it's <= left child
+        if (child_pos + 1 < end_pos) {
+            bool lessthan = time_less_than(&heap->items[child_pos], &heap->items[child_pos + 1]);
+            if (!lessthan) {
+                child_pos += 1;
+            }
+        }
+        // bubble up the smaller child
+        heap->items[pos] = heap->items[child_pos];
+        pos = child_pos;
+    }
+    heap->items[pos] = item;
+    heap_siftdown(heap, start_pos, pos);
+}
+
+STATIC mp_obj_t mod_utimeq_heappush(size_t n_args, const mp_obj_t *args) {
+    (void)n_args;
+    mp_obj_t heap_in = args[0];
+    mp_obj_utimeq_t *heap = get_heap(heap_in);
+    if (heap->len == heap->alloc) {
+        mp_raise_msg(&mp_type_IndexError, "queue overflow");
+    }
+    mp_uint_t l = heap->len;
+    heap->items[l].time = MP_OBJ_SMALL_INT_VALUE(args[1]);
+    heap->items[l].id = utimeq_id++;
+    heap->items[l].callback = args[2];
+    heap->items[l].args = args[3];
+    heap_siftdown(heap, 0, heap->len);
+    heap->len++;
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_utimeq_heappush_obj, 4, 4, mod_utimeq_heappush);
+
+STATIC mp_obj_t mod_utimeq_heappop(mp_obj_t heap_in, mp_obj_t list_ref) {
+    mp_obj_utimeq_t *heap = get_heap(heap_in);
+    if (heap->len == 0) {
+        nlr_raise(mp_obj_new_exception_msg(&mp_type_IndexError, "empty heap"));
+    }
+    mp_obj_list_t *ret = MP_OBJ_TO_PTR(list_ref);
+    if (!MP_OBJ_IS_TYPE(list_ref, &mp_type_list) || ret->len < 3) {
+        mp_raise_TypeError(NULL);
+    }
+
+    struct qentry *item = &heap->items[0];
+    ret->items[0] = MP_OBJ_NEW_SMALL_INT(item->time);
+    ret->items[1] = item->callback;
+    ret->items[2] = item->args;
+    heap->len -= 1;
+    heap->items[0] = heap->items[heap->len];
+    heap->items[heap->len].callback = MP_OBJ_NULL; // so we don't retain a pointer
+    heap->items[heap->len].args = MP_OBJ_NULL;
+    if (heap->len) {
+        heap_siftup(heap, 0);
+    }
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_utimeq_heappop_obj, mod_utimeq_heappop);
+
+STATIC mp_obj_t mod_utimeq_peektime(mp_obj_t heap_in) {
+    mp_obj_utimeq_t *heap = get_heap(heap_in);
+    if (heap->len == 0) {
+        nlr_raise(mp_obj_new_exception_msg(&mp_type_IndexError, "empty heap"));
+    }
+
+    struct qentry *item = &heap->items[0];
+    return MP_OBJ_NEW_SMALL_INT(item->time);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_utimeq_peektime_obj, mod_utimeq_peektime);
+
+#if DEBUG
+STATIC mp_obj_t mod_utimeq_dump(mp_obj_t heap_in) {
+    mp_obj_utimeq_t *heap = get_heap(heap_in);
+    for (int i = 0; i < heap->len; i++) {
+        printf(UINT_FMT "\t%p\t%p\n", heap->items[i].time,
+            MP_OBJ_TO_PTR(heap->items[i].callback), MP_OBJ_TO_PTR(heap->items[i].args));
+    }
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_utimeq_dump_obj, mod_utimeq_dump);
+#endif
+
+STATIC mp_obj_t utimeq_unary_op(mp_unary_op_t op, mp_obj_t self_in) {
+    mp_obj_utimeq_t *self = MP_OBJ_TO_PTR(self_in);
+    switch (op) {
+        case MP_UNARY_OP_BOOL: return mp_obj_new_bool(self->len != 0);
+        case MP_UNARY_OP_LEN: return MP_OBJ_NEW_SMALL_INT(self->len);
+        default: return MP_OBJ_NULL; // op not supported
+    }
+}
+
+STATIC const mp_rom_map_elem_t utimeq_locals_dict_table[] = {
+    { MP_ROM_QSTR(MP_QSTR_push), MP_ROM_PTR(&mod_utimeq_heappush_obj) },
+    { MP_ROM_QSTR(MP_QSTR_pop), MP_ROM_PTR(&mod_utimeq_heappop_obj) },
+    { MP_ROM_QSTR(MP_QSTR_peektime), MP_ROM_PTR(&mod_utimeq_peektime_obj) },
+    #if DEBUG
+    { MP_ROM_QSTR(MP_QSTR_dump), MP_ROM_PTR(&mod_utimeq_dump_obj) },
+    #endif
+};
+
+STATIC MP_DEFINE_CONST_DICT(utimeq_locals_dict, utimeq_locals_dict_table);
+
+STATIC const mp_obj_type_t utimeq_type = {
+    { &mp_type_type },
+    .name = MP_QSTR_utimeq,
+    .make_new = utimeq_make_new,
+    .unary_op = utimeq_unary_op,
+    .locals_dict = (void*)&utimeq_locals_dict,
+};
+
+STATIC const mp_rom_map_elem_t mp_module_utimeq_globals_table[] = {
+    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_utimeq) },
+    { MP_ROM_QSTR(MP_QSTR_utimeq), MP_ROM_PTR(&utimeq_type) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(mp_module_utimeq_globals, mp_module_utimeq_globals_table);
+
+const mp_obj_module_t mp_module_utimeq = {
+    .base = { &mp_type_module },
+    .globals = (mp_obj_dict_t*)&mp_module_utimeq_globals,
+};
+
+#endif //MICROPY_PY_UTIMEQ

+ 224 - 0
extmod/moduzlib.c

@@ -0,0 +1,224 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014-2016 Paul Sokolovsky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "py/runtime.h"
+#include "py/stream.h"
+#include "py/mperrno.h"
+
+#if MICROPY_PY_UZLIB
+
+#include "uzlib/tinf.h"
+
+#if 0 // print debugging info
+#define DEBUG_printf DEBUG_printf
+#else // don't print debugging info
+#define DEBUG_printf(...) (void)0
+#endif
+
+typedef struct _mp_obj_decompio_t {
+    mp_obj_base_t base;
+    mp_obj_t src_stream;
+    TINF_DATA decomp;
+    bool eof;
+} mp_obj_decompio_t;
+
+STATIC unsigned char read_src_stream(TINF_DATA *data) {
+    byte *p = (void*)data;
+    p -= offsetof(mp_obj_decompio_t, decomp);
+    mp_obj_decompio_t *self = (mp_obj_decompio_t*)p;
+
+    const mp_stream_p_t *stream = mp_get_stream_raise(self->src_stream, MP_STREAM_OP_READ);
+    int err;
+    byte c;
+    mp_uint_t out_sz = stream->read(self->src_stream, &c, 1, &err);
+    if (out_sz == MP_STREAM_ERROR) {
+        mp_raise_OSError(err);
+    }
+    if (out_sz == 0) {
+        nlr_raise(mp_obj_new_exception(&mp_type_EOFError));
+    }
+    return c;
+}
+
+STATIC mp_obj_t decompio_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+    mp_arg_check_num(n_args, n_kw, 1, 2, false);
+    mp_obj_decompio_t *o = m_new_obj(mp_obj_decompio_t);
+    o->base.type = type;
+    memset(&o->decomp, 0, sizeof(o->decomp));
+    o->decomp.readSource = read_src_stream;
+    o->src_stream = args[0];
+    o->eof = false;
+
+    mp_int_t dict_opt = 0;
+    int dict_sz;
+    if (n_args > 1) {
+        dict_opt = mp_obj_get_int(args[1]);
+    }
+
+    if (dict_opt >= 16) {
+        int st = uzlib_gzip_parse_header(&o->decomp);
+        if (st != TINF_OK) {
+            goto header_error;
+        }
+        dict_sz = 1 << (dict_opt - 16);
+    } else if (dict_opt >= 0) {
+        dict_opt = uzlib_zlib_parse_header(&o->decomp);
+        if (dict_opt < 0) {
+header_error:
+            mp_raise_ValueError("compression header");
+        }
+        dict_sz = 1 << dict_opt;
+    } else {
+        dict_sz = 1 << -dict_opt;
+    }
+
+    uzlib_uncompress_init(&o->decomp, m_new(byte, dict_sz), dict_sz);
+    return MP_OBJ_FROM_PTR(o);
+}
+
+STATIC mp_uint_t decompio_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) {
+    mp_obj_decompio_t *o = MP_OBJ_TO_PTR(o_in);
+    if (o->eof) {
+        return 0;
+    }
+
+    o->decomp.dest = buf;
+    o->decomp.destSize = size;
+    int st = uzlib_uncompress_chksum(&o->decomp);
+    if (st == TINF_DONE) {
+        o->eof = true;
+    }
+    if (st < 0) {
+        *errcode = MP_EINVAL;
+        return MP_STREAM_ERROR;
+    }
+    return o->decomp.dest - (byte*)buf;
+}
+
+STATIC const mp_rom_map_elem_t decompio_locals_dict_table[] = {
+    { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
+    { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
+    { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(decompio_locals_dict, decompio_locals_dict_table);
+
+STATIC const mp_stream_p_t decompio_stream_p = {
+    .read = decompio_read,
+};
+
+STATIC const mp_obj_type_t decompio_type = {
+    { &mp_type_type },
+    .name = MP_QSTR_DecompIO,
+    .make_new = decompio_make_new,
+    .protocol = &decompio_stream_p,
+    .locals_dict = (void*)&decompio_locals_dict,
+};
+
+STATIC mp_obj_t mod_uzlib_decompress(size_t n_args, const mp_obj_t *args) {
+    mp_obj_t data = args[0];
+    mp_buffer_info_t bufinfo;
+    mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ);
+
+    TINF_DATA *decomp = m_new_obj(TINF_DATA);
+    memset(decomp, 0, sizeof(*decomp));
+    DEBUG_printf("sizeof(TINF_DATA)=" UINT_FMT "\n", sizeof(*decomp));
+    uzlib_uncompress_init(decomp, NULL, 0);
+    mp_uint_t dest_buf_size = (bufinfo.len + 15) & ~15;
+    byte *dest_buf = m_new(byte, dest_buf_size);
+
+    decomp->dest = dest_buf;
+    decomp->destSize = dest_buf_size;
+    DEBUG_printf("uzlib: Initial out buffer: " UINT_FMT " bytes\n", decomp->destSize);
+    decomp->source = bufinfo.buf;
+
+    int st;
+    bool is_zlib = true;
+
+    if (n_args > 1 && MP_OBJ_SMALL_INT_VALUE(args[1]) < 0) {
+        is_zlib = false;
+    }
+
+    if (is_zlib) {
+        st = uzlib_zlib_parse_header(decomp);
+        if (st < 0) {
+            goto error;
+        }
+    }
+
+    while (1) {
+        st = uzlib_uncompress_chksum(decomp);
+        if (st < 0) {
+            goto error;
+        }
+        if (st == TINF_DONE) {
+            break;
+        }
+        size_t offset = decomp->dest - dest_buf;
+        dest_buf = m_renew(byte, dest_buf, dest_buf_size, dest_buf_size + 256);
+        dest_buf_size += 256;
+        decomp->dest = dest_buf + offset;
+        decomp->destSize = 256;
+    }
+
+    mp_uint_t final_sz = decomp->dest - dest_buf;
+    DEBUG_printf("uzlib: Resizing from " UINT_FMT " to final size: " UINT_FMT " bytes\n", dest_buf_size, final_sz);
+    dest_buf = (byte*)m_renew(byte, dest_buf, dest_buf_size, final_sz);
+    mp_obj_t res = mp_obj_new_bytearray_by_ref(final_sz, dest_buf);
+    m_del_obj(TINF_DATA, decomp);
+    return res;
+
+error:
+        nlr_raise(mp_obj_new_exception_arg1(&mp_type_ValueError, MP_OBJ_NEW_SMALL_INT(st)));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_uzlib_decompress_obj, 1, 3, mod_uzlib_decompress);
+
+STATIC const mp_rom_map_elem_t mp_module_uzlib_globals_table[] = {
+    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uzlib) },
+    { MP_ROM_QSTR(MP_QSTR_decompress), MP_ROM_PTR(&mod_uzlib_decompress_obj) },
+    { MP_ROM_QSTR(MP_QSTR_DecompIO), MP_ROM_PTR(&decompio_type) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(mp_module_uzlib_globals, mp_module_uzlib_globals_table);
+
+const mp_obj_module_t mp_module_uzlib = {
+    .base = { &mp_type_module },
+    .globals = (mp_obj_dict_t*)&mp_module_uzlib_globals,
+};
+
+// Source files #include'd here to make sure they're compiled in
+// only if module is enabled by config setting.
+
+#include "uzlib/tinflate.c"
+#include "uzlib/tinfzlib.c"
+#include "uzlib/tinfgzip.c"
+#include "uzlib/adler32.c"
+#include "uzlib/crc32.c"
+
+#endif // MICROPY_PY_UZLIB

+ 352 - 0
extmod/modwebrepl.c

@@ -0,0 +1,352 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Paul Sokolovsky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "py/runtime.h"
+#include "py/stream.h"
+#include "py/builtin.h"
+#ifdef MICROPY_PY_WEBREPL_DELAY
+#include "py/mphal.h"
+#endif
+#include "extmod/modwebsocket.h"
+#include "genhdr/mpversion.h"
+
+#if MICROPY_PY_WEBREPL
+
+#if 0 // print debugging info
+#define DEBUG_printf DEBUG_printf
+#else // don't print debugging info
+#define DEBUG_printf(...) (void)0
+#endif
+
+struct webrepl_file {
+    char sig[2];
+    char type;
+    char flags;
+    uint64_t offset;
+    uint32_t size;
+    uint16_t fname_len;
+    char fname[64];
+} __attribute__((packed));
+
+enum { PUT_FILE = 1, GET_FILE, GET_VER };
+enum { STATE_PASSWD, STATE_NORMAL };
+
+typedef struct _mp_obj_webrepl_t {
+    mp_obj_base_t base;
+    mp_obj_t sock;
+    byte state;
+    byte hdr_to_recv;
+    uint32_t data_to_recv;
+    struct webrepl_file hdr;
+    mp_obj_t cur_file;
+} mp_obj_webrepl_t;
+
+// These get passed to functions which aren't force-l32, so can't be const
+STATIC char passwd_prompt[] = "Password: ";
+STATIC char connected_prompt[] = "\r\nWebREPL connected\r\n>>> ";
+STATIC char denied_prompt[] = "\r\nAccess denied\r\n";
+
+STATIC char webrepl_passwd[10];
+
+STATIC void write_webrepl(mp_obj_t websock, const void *buf, size_t len) {
+    const mp_stream_p_t *sock_stream = mp_get_stream_raise(websock, MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL);
+    int err;
+    int old_opts = sock_stream->ioctl(websock, MP_STREAM_SET_DATA_OPTS, FRAME_BIN, &err);
+    sock_stream->write(websock, buf, len, &err);
+    sock_stream->ioctl(websock, MP_STREAM_SET_DATA_OPTS, old_opts, &err);
+}
+
+#define SSTR(s) s, sizeof(s) - 1
+STATIC void write_webrepl_str(mp_obj_t websock, const char *str, int sz) {
+    int err;
+    const mp_stream_p_t *sock_stream = mp_get_stream_raise(websock, MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL);
+    sock_stream->write(websock, str, sz, &err);
+}
+
+STATIC void write_webrepl_resp(mp_obj_t websock, uint16_t code) {
+    char buf[4] = {'W', 'B', code & 0xff, code >> 8};
+    write_webrepl(websock, buf, sizeof(buf));
+}
+
+STATIC mp_obj_t webrepl_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+    mp_arg_check_num(n_args, n_kw, 1, 2, false);
+    mp_get_stream_raise(args[0], MP_STREAM_OP_READ | MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL);
+    DEBUG_printf("sizeof(struct webrepl_file) = %lu\n", sizeof(struct webrepl_file));
+    mp_obj_webrepl_t *o = m_new_obj(mp_obj_webrepl_t);
+    o->base.type = type;
+    o->sock = args[0];
+    o->hdr_to_recv = sizeof(struct webrepl_file);
+    o->data_to_recv = 0;
+    o->state = STATE_PASSWD;
+    write_webrepl_str(args[0], SSTR(passwd_prompt));
+    return o;
+}
+
+STATIC int write_file_chunk(mp_obj_webrepl_t *self) {
+    const mp_stream_p_t *file_stream =
+        mp_get_stream_raise(self->cur_file, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL);
+    byte readbuf[2 + 256];
+    int err;
+    mp_uint_t out_sz = file_stream->read(self->cur_file, readbuf + 2, sizeof(readbuf) - 2, &err);
+    if (out_sz == MP_STREAM_ERROR) {
+        return out_sz;
+    }
+    readbuf[0] = out_sz;
+    readbuf[1] = out_sz >> 8;
+    DEBUG_printf("webrepl: Sending %d bytes of file\n", out_sz);
+    write_webrepl(self->sock, readbuf, 2 + out_sz);
+    return out_sz;
+}
+
+STATIC void handle_op(mp_obj_webrepl_t *self) {
+
+    // Handle operations not requiring opened file
+
+    switch (self->hdr.type) {
+        case GET_VER: {
+            static char ver[] = {MICROPY_VERSION_MAJOR, MICROPY_VERSION_MINOR, MICROPY_VERSION_MICRO};
+            write_webrepl(self->sock, ver, sizeof(ver));
+            self->hdr_to_recv = sizeof(struct webrepl_file);
+            return;
+        }
+    }
+
+    // Handle operations requiring opened file
+
+    mp_obj_t open_args[2] = {
+        mp_obj_new_str(self->hdr.fname, strlen(self->hdr.fname), false),
+        MP_OBJ_NEW_QSTR(MP_QSTR_rb)
+    };
+
+    if (self->hdr.type == PUT_FILE) {
+        open_args[1] = MP_OBJ_NEW_QSTR(MP_QSTR_wb);
+    }
+
+    self->cur_file = mp_builtin_open(2, open_args, (mp_map_t*)&mp_const_empty_map);
+
+    #if 0
+    struct mp_stream_seek_t seek = { .offset = self->hdr.offset, .whence = 0 };
+    int err;
+    mp_uint_t res = file_stream->ioctl(self->cur_file, MP_STREAM_SEEK, (uintptr_t)&seek, &err);
+    assert(res != MP_STREAM_ERROR);
+    #endif
+
+    write_webrepl_resp(self->sock, 0);
+
+    if (self->hdr.type == PUT_FILE) {
+        self->data_to_recv = self->hdr.size;
+    } else if (self->hdr.type == GET_FILE) {
+        self->data_to_recv = 1;
+    }
+}
+
+STATIC mp_uint_t _webrepl_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode);
+
+STATIC mp_uint_t webrepl_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) {
+    mp_uint_t out_sz;
+    do {
+        out_sz = _webrepl_read(self_in, buf, size, errcode);
+    } while (out_sz == -2);
+    return out_sz;
+}
+
+STATIC mp_uint_t _webrepl_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) {
+    // We know that os.dupterm always calls with size = 1
+    assert(size == 1);
+    mp_obj_webrepl_t *self = self_in;
+    const mp_stream_p_t *sock_stream = mp_get_stream_raise(self->sock, MP_STREAM_OP_READ);
+    mp_uint_t out_sz = sock_stream->read(self->sock, buf, size, errcode);
+    //DEBUG_printf("webrepl: Read %d initial bytes from websocket\n", out_sz);
+    if (out_sz == 0 || out_sz == MP_STREAM_ERROR) {
+        return out_sz;
+    }
+
+    if (self->state == STATE_PASSWD) {
+        char c = *(char*)buf;
+        if (c == '\r' || c == '\n') {
+            self->hdr.fname[self->data_to_recv] = 0;
+            DEBUG_printf("webrepl: entered password: %s\n", self->hdr.fname);
+
+            if (strcmp(self->hdr.fname, webrepl_passwd) != 0) {
+                write_webrepl_str(self->sock, SSTR(denied_prompt));
+                return 0;
+            }
+
+            self->state = STATE_NORMAL;
+            self->data_to_recv = 0;
+            write_webrepl_str(self->sock, SSTR(connected_prompt));
+        } else if (self->data_to_recv < 10) {
+            self->hdr.fname[self->data_to_recv++] = c;
+        }
+        return -2;
+    }
+
+    // If last read data belonged to text record (== REPL)
+    int err;
+    if (sock_stream->ioctl(self->sock, MP_STREAM_GET_DATA_OPTS, 0, &err) == 1) {
+        return out_sz;
+    }
+
+    DEBUG_printf("webrepl: received bin data, hdr_to_recv: %d, data_to_recv=%d\n", self->hdr_to_recv, self->data_to_recv);
+
+    if (self->hdr_to_recv != 0) {
+        char *p = (char*)&self->hdr + sizeof(self->hdr) - self->hdr_to_recv;
+        *p++ = *(char*)buf;
+        if (--self->hdr_to_recv != 0) {
+            mp_uint_t hdr_sz = sock_stream->read(self->sock, p, self->hdr_to_recv, errcode);
+            if (hdr_sz == MP_STREAM_ERROR) {
+                return hdr_sz;
+            }
+            self->hdr_to_recv -= hdr_sz;
+            if (self->hdr_to_recv != 0) {
+                return -2;
+            }
+        }
+
+        DEBUG_printf("webrepl: op: %d, file: %s, chunk @%x, sz=%d\n", self->hdr.type, self->hdr.fname, (uint32_t)self->hdr.offset, self->hdr.size);
+
+        handle_op(self);
+
+        return -2;
+    }
+
+    if (self->data_to_recv != 0) {
+        static byte filebuf[512];
+        filebuf[0] = *(byte*)buf;
+        mp_uint_t buf_sz = 1;
+        if (--self->data_to_recv != 0) {
+            size_t to_read = MIN(sizeof(filebuf) - 1, self->data_to_recv);
+            mp_uint_t sz = sock_stream->read(self->sock, filebuf + 1, to_read, errcode);
+            if (sz == MP_STREAM_ERROR) {
+                return sz;
+            }
+            self->data_to_recv -= sz;
+            buf_sz += sz;
+        }
+
+        if (self->hdr.type == PUT_FILE) {
+            DEBUG_printf("webrepl: Writing %lu bytes to file\n", buf_sz);
+            int err;
+            mp_uint_t res = mp_stream_write_exactly(self->cur_file, filebuf, buf_sz, &err);
+            if (err != 0 || res != buf_sz) {
+                assert(0);
+            }
+        } else if (self->hdr.type == GET_FILE) {
+            assert(buf_sz == 1);
+            assert(self->data_to_recv == 0);
+            assert(filebuf[0] == 0);
+            mp_uint_t out_sz = write_file_chunk(self);
+            if (out_sz != 0) {
+                self->data_to_recv = 1;
+            }
+        }
+
+        if (self->data_to_recv == 0) {
+            mp_stream_close(self->cur_file);
+            self->hdr_to_recv = sizeof(struct webrepl_file);
+            DEBUG_printf("webrepl: Finished file operation %d\n", self->hdr.type);
+            write_webrepl_resp(self->sock, 0);
+        }
+
+        #ifdef MICROPY_PY_WEBREPL_DELAY
+        // Some platforms may have broken drivers and easily gets
+        // overloaded with modest traffic WebREPL file transfers
+        // generate. The basic workaround is a crude rate control
+        // done in such way.
+        mp_hal_delay_ms(MICROPY_PY_WEBREPL_DELAY);
+        #endif
+    }
+
+    return -2;
+}
+
+STATIC mp_uint_t webrepl_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) {
+    mp_obj_webrepl_t *self = self_in;
+    if (self->state == STATE_PASSWD) {
+        // Don't forward output until passwd is entered
+        return size;
+    }
+    const mp_stream_p_t *stream_p = mp_get_stream_raise(self->sock, MP_STREAM_OP_WRITE);
+    return stream_p->write(self->sock, buf, size, errcode);
+}
+
+STATIC mp_obj_t webrepl_close(mp_obj_t self_in) {
+    mp_obj_webrepl_t *self = MP_OBJ_TO_PTR(self_in);
+    // TODO: This is a place to do cleanup
+    return mp_stream_close(self->sock);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(webrepl_close_obj, webrepl_close);
+
+STATIC mp_obj_t webrepl_set_password(mp_obj_t passwd_in) {
+    size_t len;
+    const char *passwd = mp_obj_str_get_data(passwd_in, &len);
+    if (len > sizeof(webrepl_passwd) - 1) {
+        mp_raise_ValueError(NULL);
+    }
+    strcpy(webrepl_passwd, passwd);
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(webrepl_set_password_obj, webrepl_set_password);
+
+STATIC const mp_rom_map_elem_t webrepl_locals_dict_table[] = {
+    { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
+    { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
+    { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
+    { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&webrepl_close_obj) },
+};
+STATIC MP_DEFINE_CONST_DICT(webrepl_locals_dict, webrepl_locals_dict_table);
+
+STATIC const mp_stream_p_t webrepl_stream_p = {
+    .read = webrepl_read,
+    .write = webrepl_write,
+};
+
+STATIC const mp_obj_type_t webrepl_type = {
+    { &mp_type_type },
+    .name = MP_QSTR__webrepl,
+    .make_new = webrepl_make_new,
+    .protocol = &webrepl_stream_p,
+    .locals_dict = (mp_obj_dict_t*)&webrepl_locals_dict,
+};
+
+STATIC const mp_rom_map_elem_t webrepl_module_globals_table[] = {
+    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR__webrepl) },
+    { MP_ROM_QSTR(MP_QSTR__webrepl), MP_ROM_PTR(&webrepl_type) },
+    { MP_ROM_QSTR(MP_QSTR_password), MP_ROM_PTR(&webrepl_set_password_obj) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(webrepl_module_globals, webrepl_module_globals_table);
+
+const mp_obj_module_t mp_module_webrepl = {
+    .base = { &mp_type_module },
+    .globals = (mp_obj_dict_t*)&webrepl_module_globals,
+};
+
+#endif // MICROPY_PY_WEBREPL

+ 316 - 0
extmod/modwebsocket.c

@@ -0,0 +1,316 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Paul Sokolovsky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "py/runtime.h"
+#include "py/stream.h"
+#include "extmod/modwebsocket.h"
+
+#if MICROPY_PY_WEBSOCKET
+
+enum { FRAME_HEADER, FRAME_OPT, PAYLOAD, CONTROL };
+
+enum { BLOCKING_WRITE = 0x80 };
+
+typedef struct _mp_obj_websocket_t {
+    mp_obj_base_t base;
+    mp_obj_t sock;
+    uint32_t msg_sz;
+    byte mask[4];
+    byte state;
+    byte to_recv;
+    byte mask_pos;
+    byte buf_pos;
+    byte buf[6];
+    byte opts;
+    // Copy of last data frame flags
+    byte ws_flags;
+    // Copy of current frame flags
+    byte last_flags;
+} mp_obj_websocket_t;
+
+STATIC mp_uint_t websocket_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode);
+
+STATIC mp_obj_t websocket_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+    mp_arg_check_num(n_args, n_kw, 1, 2, false);
+    mp_obj_websocket_t *o = m_new_obj(mp_obj_websocket_t);
+    o->base.type = type;
+    o->sock = args[0];
+    o->state = FRAME_HEADER;
+    o->to_recv = 2;
+    o->mask_pos = 0;
+    o->buf_pos = 0;
+    o->opts = FRAME_TXT;
+    if (n_args > 1 && args[1] == mp_const_true) {
+        o->opts |= BLOCKING_WRITE;
+    }
+    return  MP_OBJ_FROM_PTR(o);
+}
+
+STATIC mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) {
+    mp_obj_websocket_t *self =  MP_OBJ_TO_PTR(self_in);
+    const mp_stream_p_t *stream_p = mp_get_stream_raise(self->sock, MP_STREAM_OP_READ);
+    while (1) {
+        if (self->to_recv != 0) {
+            mp_uint_t out_sz = stream_p->read(self->sock, self->buf + self->buf_pos, self->to_recv, errcode);
+            if (out_sz == 0 || out_sz == MP_STREAM_ERROR) {
+                return out_sz;
+            }
+            self->buf_pos += out_sz;
+            self->to_recv -= out_sz;
+            if (self->to_recv != 0) {
+                *errcode = MP_EAGAIN;
+                return MP_STREAM_ERROR;
+            }
+        }
+
+        switch (self->state) {
+            case FRAME_HEADER: {
+                // TODO: Split frame handling below is untested so far, so conservatively disable it
+                assert(self->buf[0] & 0x80);
+
+                // "Control frames MAY be injected in the middle of a fragmented message."
+                // So, they must be processed before data frames (and not alter
+                // self->ws_flags)
+                byte frame_type = self->buf[0];
+                self->last_flags = frame_type;
+                frame_type &= FRAME_OPCODE_MASK;
+
+                if ((self->buf[0] & FRAME_OPCODE_MASK) == FRAME_CONT) {
+                    // Preserve previous frame type
+                    self->ws_flags = (self->ws_flags & FRAME_OPCODE_MASK) | (self->buf[0] & ~FRAME_OPCODE_MASK);
+                } else {
+                    self->ws_flags = self->buf[0];
+                }
+
+                // Reset mask in case someone will use "simplified" protocol
+                // without masks.
+                memset(self->mask, 0, sizeof(self->mask));
+
+                int to_recv = 0;
+                size_t sz = self->buf[1] & 0x7f;
+                if (sz == 126) {
+                    // Msg size is next 2 bytes
+                    to_recv += 2;
+                } else if (sz == 127) {
+                    // Msg size is next 8 bytes
+                    assert(0);
+                }
+                if (self->buf[1] & 0x80) {
+                    // Next 4 bytes is mask
+                    to_recv += 4;
+                }
+
+                self->buf_pos = 0;
+                self->to_recv = to_recv;
+                self->msg_sz = sz; // May be overridden by FRAME_OPT
+                if (to_recv != 0) {
+                    self->state = FRAME_OPT;
+                } else {
+                    if (frame_type >= FRAME_CLOSE) {
+                        self->state = CONTROL;
+                    } else {
+                        self->state = PAYLOAD;
+                    }
+                }
+                continue;
+            }
+
+            case FRAME_OPT: {
+                if ((self->buf_pos & 3) == 2) {
+                    // First two bytes are message length
+                    self->msg_sz = (self->buf[0] << 8) | self->buf[1];
+                }
+                if (self->buf_pos >= 4) {
+                    // Last 4 bytes is mask
+                    memcpy(self->mask, self->buf + self->buf_pos - 4, 4);
+                }
+                self->buf_pos = 0;
+                if ((self->last_flags & FRAME_OPCODE_MASK) >= FRAME_CLOSE) {
+                    self->state = CONTROL;
+                } else {
+                    self->state = PAYLOAD;
+                }
+                continue;
+            }
+
+            case PAYLOAD:
+            case CONTROL: {
+                mp_uint_t out_sz = 0;
+                if (self->msg_sz == 0) {
+                    // In case message had zero payload
+                    goto no_payload;
+                }
+
+                size_t sz = MIN(size, self->msg_sz);
+                out_sz = stream_p->read(self->sock, buf, sz, errcode);
+                if (out_sz == 0 || out_sz == MP_STREAM_ERROR) {
+                    return out_sz;
+                }
+
+                sz = out_sz;
+                for (byte *p = buf; sz--; p++) {
+                    *p ^= self->mask[self->mask_pos++ & 3];
+                }
+
+                self->msg_sz -= out_sz;
+                if (self->msg_sz == 0) {
+                    byte last_state;
+no_payload:
+                    last_state = self->state;
+                    self->state = FRAME_HEADER;
+                    self->to_recv = 2;
+                    self->mask_pos = 0;
+                    self->buf_pos = 0;
+
+                    // Handle control frame
+                    if (last_state == CONTROL) {
+                        byte frame_type = self->last_flags & FRAME_OPCODE_MASK;
+                        if (frame_type == FRAME_CLOSE) {
+                            static char close_resp[2] = {0x88, 0};
+                            int err;
+                            websocket_write(self_in, close_resp, sizeof(close_resp), &err);
+                            return 0;
+                        }
+
+                        //DEBUG_printf("Finished receiving ctrl message %x, ignoring\n", self->last_flags);
+                        continue;
+                    }
+                }
+
+                if (out_sz != 0) {
+                    return out_sz;
+                }
+                // Empty (data) frame received is not EOF
+                continue;
+            }
+
+        }
+    }
+}
+
+STATIC mp_uint_t websocket_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) {
+    mp_obj_websocket_t *self =  MP_OBJ_TO_PTR(self_in);
+    assert(size < 0x10000);
+    byte header[4] = {0x80 | (self->opts & FRAME_OPCODE_MASK)};
+    int hdr_sz;
+    if (size < 126) {
+        header[1] = size;
+        hdr_sz = 2;
+    } else {
+        header[1] = 126;
+        header[2] = size >> 8;
+        header[3] = size & 0xff;
+        hdr_sz = 4;
+    }
+
+    mp_obj_t dest[3];
+    if (self->opts & BLOCKING_WRITE) {
+        mp_load_method(self->sock, MP_QSTR_setblocking, dest);
+        dest[2] = mp_const_true;
+        mp_call_method_n_kw(1, 0, dest);
+    }
+
+    mp_uint_t out_sz = mp_stream_write_exactly(self->sock, header, hdr_sz, errcode);
+    if (*errcode == 0) {
+        out_sz = mp_stream_write_exactly(self->sock, buf, size, errcode);
+    }
+
+    if (self->opts & BLOCKING_WRITE) {
+        dest[2] = mp_const_false;
+        mp_call_method_n_kw(1, 0, dest);
+    }
+
+    if (*errcode != 0) {
+        return MP_STREAM_ERROR;
+    }
+    return out_sz;
+}
+
+STATIC mp_uint_t websocket_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) {
+    mp_obj_websocket_t *self = MP_OBJ_TO_PTR(self_in);
+    switch (request) {
+        case MP_STREAM_GET_DATA_OPTS:
+            return self->ws_flags & FRAME_OPCODE_MASK;
+        case MP_STREAM_SET_DATA_OPTS: {
+            int cur = self->opts & FRAME_OPCODE_MASK;
+            self->opts = (self->opts & ~FRAME_OPCODE_MASK) | (arg & FRAME_OPCODE_MASK);
+            return cur;
+        }
+        default:
+            *errcode = MP_EINVAL;
+            return MP_STREAM_ERROR;
+    }
+}
+
+STATIC mp_obj_t websocket_close(mp_obj_t self_in) {
+    mp_obj_websocket_t *self = MP_OBJ_TO_PTR(self_in);
+    // TODO: Send close signaling to the other side, otherwise it's
+    // abrupt close (connection abort).
+    return mp_stream_close(self->sock);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(websocket_close_obj, websocket_close);
+
+STATIC const mp_rom_map_elem_t websocket_locals_dict_table[] = {
+    { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
+    { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
+    { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) },
+    { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
+    { MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&mp_stream_ioctl_obj) },
+    { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&websocket_close_obj) },
+};
+STATIC MP_DEFINE_CONST_DICT(websocket_locals_dict, websocket_locals_dict_table);
+
+STATIC const mp_stream_p_t websocket_stream_p = {
+    .read = websocket_read,
+    .write = websocket_write,
+    .ioctl = websocket_ioctl,
+};
+
+STATIC const mp_obj_type_t websocket_type = {
+    { &mp_type_type },
+    .name = MP_QSTR_websocket,
+    .make_new = websocket_make_new,
+    .protocol = &websocket_stream_p,
+    .locals_dict = (void*)&websocket_locals_dict,
+};
+
+STATIC const mp_rom_map_elem_t websocket_module_globals_table[] = {
+    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_websocket) },
+    { MP_ROM_QSTR(MP_QSTR_websocket), MP_ROM_PTR(&websocket_type) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(websocket_module_globals, websocket_module_globals_table);
+
+const mp_obj_module_t mp_module_websocket = {
+    .base = { &mp_type_module },
+    .globals = (mp_obj_dict_t*)&websocket_module_globals,
+};
+
+#endif // MICROPY_PY_WEBSOCKET

+ 10 - 0
extmod/modwebsocket.h

@@ -0,0 +1,10 @@
+#ifndef MICROPY_INCLUDED_EXTMOD_MODWEBSOCKET_H
+#define MICROPY_INCLUDED_EXTMOD_MODWEBSOCKET_H
+
+#define FRAME_OPCODE_MASK 0x0f
+enum {
+    FRAME_CONT, FRAME_TXT, FRAME_BIN,
+    FRAME_CLOSE = 0x8, FRAME_PING, FRAME_PONG
+};
+
+#endif // MICROPY_INCLUDED_EXTMOD_MODWEBSOCKET_H

+ 145 - 0
extmod/uos_dupterm.c

@@ -0,0 +1,145 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Paul Sokolovsky
+ * Copyright (c) 2017 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <string.h>
+#include "py/mpconfig.h"
+
+#include "py/runtime.h"
+#include "py/objtuple.h"
+#include "py/objarray.h"
+#include "py/stream.h"
+#include "lib/utils/interrupt_char.h"
+
+#if MICROPY_PY_OS_DUPTERM
+
+void mp_uos_deactivate(size_t dupterm_idx, const char *msg, mp_obj_t exc) {
+    mp_obj_t term = MP_STATE_VM(dupterm_objs[dupterm_idx]);
+    MP_STATE_VM(dupterm_objs[dupterm_idx]) = MP_OBJ_NULL;
+    mp_printf(&mp_plat_print, msg);
+    if (exc != MP_OBJ_NULL) {
+        mp_obj_print_exception(&mp_plat_print, exc);
+    }
+    nlr_buf_t nlr;
+    if (nlr_push(&nlr) == 0) {
+        mp_stream_close(term);
+        nlr_pop();
+    } else {
+        // Ignore any errors during stream closing
+    }
+}
+
+int mp_uos_dupterm_rx_chr(void) {
+    for (size_t idx = 0; idx < MICROPY_PY_OS_DUPTERM; ++idx) {
+        if (MP_STATE_VM(dupterm_objs[idx]) == MP_OBJ_NULL) {
+            continue;
+        }
+
+        nlr_buf_t nlr;
+        if (nlr_push(&nlr) == 0) {
+            mp_obj_t readinto_m[3];
+            mp_load_method(MP_STATE_VM(dupterm_objs[idx]), MP_QSTR_readinto, readinto_m);
+            readinto_m[2] = MP_STATE_VM(dupterm_arr_obj);
+            mp_obj_t res = mp_call_method_n_kw(1, 0, readinto_m);
+            if (res == mp_const_none) {
+                nlr_pop();
+            } else if (res == MP_OBJ_NEW_SMALL_INT(0)) {
+                nlr_pop();
+                mp_uos_deactivate(idx, "dupterm: EOF received, deactivating\n", MP_OBJ_NULL);
+            } else {
+                mp_buffer_info_t bufinfo;
+                mp_get_buffer_raise(MP_STATE_VM(dupterm_arr_obj), &bufinfo, MP_BUFFER_READ);
+                nlr_pop();
+                if (*(byte*)bufinfo.buf == mp_interrupt_char) {
+                    // Signal keyboard interrupt to be raised as soon as the VM resumes
+                    mp_keyboard_interrupt();
+                    return -2;
+                }
+                return *(byte*)bufinfo.buf;
+            }
+        } else {
+            mp_uos_deactivate(idx, "dupterm: Exception in read() method, deactivating: ", nlr.ret_val);
+        }
+    }
+
+    // No chars available
+    return -1;
+}
+
+void mp_uos_dupterm_tx_strn(const char *str, size_t len) {
+    for (size_t idx = 0; idx < MICROPY_PY_OS_DUPTERM; ++idx) {
+        if (MP_STATE_VM(dupterm_objs[idx]) == MP_OBJ_NULL) {
+            continue;
+        }
+        nlr_buf_t nlr;
+        if (nlr_push(&nlr) == 0) {
+            mp_obj_t write_m[3];
+            mp_load_method(MP_STATE_VM(dupterm_objs[idx]), MP_QSTR_write, write_m);
+
+            mp_obj_array_t *arr = MP_OBJ_TO_PTR(MP_STATE_VM(dupterm_arr_obj));
+            void *org_items = arr->items;
+            arr->items = (void*)str;
+            arr->len = len;
+            write_m[2] = MP_STATE_VM(dupterm_arr_obj);
+            mp_call_method_n_kw(1, 0, write_m);
+            arr = MP_OBJ_TO_PTR(MP_STATE_VM(dupterm_arr_obj));
+            arr->items = org_items;
+            arr->len = 1;
+            nlr_pop();
+        } else {
+            mp_uos_deactivate(idx, "dupterm: Exception in write() method, deactivating: ", nlr.ret_val);
+        }
+    }
+}
+
+STATIC mp_obj_t mp_uos_dupterm(size_t n_args, const mp_obj_t *args) {
+    mp_int_t idx = 0;
+    if (n_args == 2) {
+        idx = mp_obj_get_int(args[1]);
+    }
+
+    if (idx < 0 || idx >= MICROPY_PY_OS_DUPTERM) {
+        mp_raise_ValueError("invalid dupterm index");
+    }
+
+    mp_obj_t previous_obj = MP_STATE_VM(dupterm_objs[idx]);
+    if (previous_obj == MP_OBJ_NULL) {
+        previous_obj = mp_const_none;
+    }
+    if (args[0] == mp_const_none) {
+        MP_STATE_VM(dupterm_objs[idx]) = MP_OBJ_NULL;
+    } else {
+        MP_STATE_VM(dupterm_objs[idx]) = args[0];
+        if (MP_STATE_VM(dupterm_arr_obj) == MP_OBJ_NULL) {
+            MP_STATE_VM(dupterm_arr_obj) = mp_obj_new_bytearray(1, "");
+        }
+    }
+
+    return previous_obj;
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_uos_dupterm_obj, 1, 2, mp_uos_dupterm);
+
+#endif

+ 102 - 0
extmod/utime_mphal.c

@@ -0,0 +1,102 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013-2016 Damien P. George
+ * Copyright (c) 2016 Paul Sokolovsky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/mpconfig.h"
+#if MICROPY_PY_UTIME_MP_HAL
+
+#include <string.h>
+
+#include "py/obj.h"
+#include "py/mphal.h"
+#include "py/smallint.h"
+#include "py/runtime.h"
+#include "extmod/utime_mphal.h"
+
+STATIC mp_obj_t time_sleep(mp_obj_t seconds_o) {
+    #if MICROPY_PY_BUILTINS_FLOAT
+    mp_hal_delay_ms((mp_uint_t)(1000 * mp_obj_get_float(seconds_o)));
+    #else
+    mp_hal_delay_ms(1000 * mp_obj_get_int(seconds_o));
+    #endif
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_1(mp_utime_sleep_obj, time_sleep);
+
+STATIC mp_obj_t time_sleep_ms(mp_obj_t arg) {
+    mp_int_t ms = mp_obj_get_int(arg);
+    if (ms > 0) {
+        mp_hal_delay_ms(ms);
+    }
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_1(mp_utime_sleep_ms_obj, time_sleep_ms);
+
+STATIC mp_obj_t time_sleep_us(mp_obj_t arg) {
+    mp_int_t us = mp_obj_get_int(arg);
+    if (us > 0) {
+        mp_hal_delay_us(us);
+    }
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_1(mp_utime_sleep_us_obj, time_sleep_us);
+
+STATIC mp_obj_t time_ticks_ms(void) {
+    return MP_OBJ_NEW_SMALL_INT(mp_hal_ticks_ms() & (MICROPY_PY_UTIME_TICKS_PERIOD - 1));
+}
+MP_DEFINE_CONST_FUN_OBJ_0(mp_utime_ticks_ms_obj, time_ticks_ms);
+
+STATIC mp_obj_t time_ticks_us(void) {
+    return MP_OBJ_NEW_SMALL_INT(mp_hal_ticks_us() & (MICROPY_PY_UTIME_TICKS_PERIOD - 1));
+}
+MP_DEFINE_CONST_FUN_OBJ_0(mp_utime_ticks_us_obj, time_ticks_us);
+
+STATIC mp_obj_t time_ticks_cpu(void) {
+    return MP_OBJ_NEW_SMALL_INT(mp_hal_ticks_cpu() & (MICROPY_PY_UTIME_TICKS_PERIOD - 1));
+}
+MP_DEFINE_CONST_FUN_OBJ_0(mp_utime_ticks_cpu_obj, time_ticks_cpu);
+
+STATIC mp_obj_t time_ticks_diff(mp_obj_t end_in, mp_obj_t start_in) {
+    // we assume that the arguments come from ticks_xx so are small ints
+    mp_uint_t start = MP_OBJ_SMALL_INT_VALUE(start_in);
+    mp_uint_t end = MP_OBJ_SMALL_INT_VALUE(end_in);
+    // Optimized formula avoiding if conditions. We adjust difference "forward",
+    // wrap it around and adjust back.
+    mp_int_t diff = ((end - start + MICROPY_PY_UTIME_TICKS_PERIOD / 2) & (MICROPY_PY_UTIME_TICKS_PERIOD - 1))
+                   - MICROPY_PY_UTIME_TICKS_PERIOD / 2;
+    return MP_OBJ_NEW_SMALL_INT(diff);
+}
+MP_DEFINE_CONST_FUN_OBJ_2(mp_utime_ticks_diff_obj, time_ticks_diff);
+
+STATIC mp_obj_t time_ticks_add(mp_obj_t ticks_in, mp_obj_t delta_in) {
+    // we assume that first argument come from ticks_xx so is small int
+    mp_uint_t ticks = MP_OBJ_SMALL_INT_VALUE(ticks_in);
+    mp_uint_t delta = mp_obj_get_int(delta_in);
+    return MP_OBJ_NEW_SMALL_INT((ticks + delta) & (MICROPY_PY_UTIME_TICKS_PERIOD - 1));
+}
+MP_DEFINE_CONST_FUN_OBJ_2(mp_utime_ticks_add_obj, time_ticks_add);
+
+#endif // MICROPY_PY_UTIME_MP_HAL

+ 41 - 0
extmod/utime_mphal.h

@@ -0,0 +1,41 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013-2016 Damien P. George
+ * Copyright (c) 2016 Paul Sokolovsky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_EXTMOD_UTIME_MPHAL_H
+#define MICROPY_INCLUDED_EXTMOD_UTIME_MPHAL_H
+
+#include "py/obj.h"
+
+MP_DECLARE_CONST_FUN_OBJ_1(mp_utime_sleep_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_utime_sleep_ms_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_utime_sleep_us_obj);
+MP_DECLARE_CONST_FUN_OBJ_0(mp_utime_ticks_ms_obj);
+MP_DECLARE_CONST_FUN_OBJ_0(mp_utime_ticks_us_obj);
+MP_DECLARE_CONST_FUN_OBJ_0(mp_utime_ticks_cpu_obj);
+MP_DECLARE_CONST_FUN_OBJ_2(mp_utime_ticks_diff_obj);
+MP_DECLARE_CONST_FUN_OBJ_2(mp_utime_ticks_add_obj);
+
+#endif // MICROPY_INCLUDED_EXTMOD_UTIME_MPHAL_H

+ 461 - 0
extmod/vfs.c

@@ -0,0 +1,461 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include "py/runtime.h"
+#include "py/objstr.h"
+#include "py/mperrno.h"
+#include "extmod/vfs.h"
+
+#if MICROPY_VFS
+
+#if MICROPY_VFS_FAT
+#include "extmod/vfs_fat.h"
+#endif
+
+// For mp_vfs_proxy_call, the maximum number of additional args that can be passed.
+// A fixed maximum size is used to avoid the need for a costly variable array.
+#define PROXY_MAX_ARGS (2)
+
+// path is the path to lookup and *path_out holds the path within the VFS
+// object (starts with / if an absolute path).
+// Returns MP_VFS_ROOT for root dir (and then path_out is undefined) and
+// MP_VFS_NONE for path not found.
+mp_vfs_mount_t *mp_vfs_lookup_path(const char *path, const char **path_out) {
+    if (*path == '/' || MP_STATE_VM(vfs_cur) == MP_VFS_ROOT) {
+        // an absolute path, or the current volume is root, so search root dir
+        bool is_abs = 0;
+        if (*path == '/') {
+            ++path;
+            is_abs = 1;
+        }
+        if (*path == '\0') {
+            // path is "" or "/" so return virtual root
+            return MP_VFS_ROOT;
+        }
+        for (mp_vfs_mount_t *vfs = MP_STATE_VM(vfs_mount_table); vfs != NULL; vfs = vfs->next) {
+            size_t len = vfs->len - 1;
+            if (len == 0) {
+                *path_out = path - is_abs;
+                return vfs;
+            }
+            if (strncmp(path, vfs->str + 1, len) == 0) {
+                if (path[len] == '/') {
+                    *path_out = path + len;
+                    return vfs;
+                } else if (path[len] == '\0') {
+                    *path_out = "/";
+                    return vfs;
+                }
+            }
+        }
+
+        // if we get here then there's nothing mounted on /
+
+        if (is_abs) {
+            // path began with / and was not found
+            return MP_VFS_NONE;
+        }
+    }
+
+    // a relative path within a mounted device
+    *path_out = path;
+    return MP_STATE_VM(vfs_cur);
+}
+
+// Version of mp_vfs_lookup_path that takes and returns uPy string objects.
+STATIC mp_vfs_mount_t *lookup_path(mp_obj_t path_in, mp_obj_t *path_out) {
+    const char *path = mp_obj_str_get_str(path_in);
+    const char *p_out;
+    mp_vfs_mount_t *vfs = mp_vfs_lookup_path(path, &p_out);
+    if (vfs != MP_VFS_NONE && vfs != MP_VFS_ROOT) {
+        *path_out = mp_obj_new_str_of_type(mp_obj_get_type(path_in),
+            (const byte*)p_out, strlen(p_out));
+    }
+    return vfs;
+}
+
+STATIC mp_obj_t mp_vfs_proxy_call(mp_vfs_mount_t *vfs, qstr meth_name, size_t n_args, const mp_obj_t *args) {
+    assert(n_args <= PROXY_MAX_ARGS);
+    if (vfs == MP_VFS_NONE) {
+        // mount point not found
+        mp_raise_OSError(MP_ENODEV);
+    }
+    if (vfs == MP_VFS_ROOT) {
+        // can't do operation on root dir
+        mp_raise_OSError(MP_EPERM);
+    }
+    mp_obj_t meth[2 + PROXY_MAX_ARGS];
+    mp_load_method(vfs->obj, meth_name, meth);
+    if (args != NULL) {
+        memcpy(meth + 2, args, n_args * sizeof(*args));
+    }
+    return mp_call_method_n_kw(n_args, 0, meth);
+}
+
+mp_import_stat_t mp_vfs_import_stat(const char *path) {
+    const char *path_out;
+    mp_vfs_mount_t *vfs = mp_vfs_lookup_path(path, &path_out);
+    if (vfs == MP_VFS_NONE || vfs == MP_VFS_ROOT) {
+        return MP_IMPORT_STAT_NO_EXIST;
+    }
+    #if MICROPY_VFS_FAT
+    // fast paths for known VFS types
+    if (mp_obj_get_type(vfs->obj) == &mp_fat_vfs_type) {
+        return fat_vfs_import_stat(MP_OBJ_TO_PTR(vfs->obj), path_out);
+    }
+    #endif
+    // TODO delegate to vfs.stat() method
+    return MP_IMPORT_STAT_NO_EXIST;
+}
+
+mp_obj_t mp_vfs_mount(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+    enum { ARG_readonly, ARG_mkfs };
+    static const mp_arg_t allowed_args[] = {
+        { MP_QSTR_readonly, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_false} },
+        { MP_QSTR_mkfs, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_false} },
+    };
+
+    // parse args
+    mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+    mp_arg_parse_all(n_args - 2, pos_args + 2, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+    // get the mount point
+    size_t mnt_len;
+    const char *mnt_str = mp_obj_str_get_data(pos_args[1], &mnt_len);
+
+    // see if we need to auto-detect and create the filesystem
+    mp_obj_t vfs_obj = pos_args[0];
+    mp_obj_t dest[2];
+    mp_load_method_maybe(vfs_obj, MP_QSTR_mount, dest);
+    if (dest[0] == MP_OBJ_NULL) {
+        // Input object has no mount method, assume it's a block device and try to
+        // auto-detect the filesystem and create the corresponding VFS entity.
+        // (At the moment we only support FAT filesystems.)
+        #if MICROPY_VFS_FAT
+        vfs_obj = mp_fat_vfs_type.make_new(&mp_fat_vfs_type, 1, 0, &vfs_obj);
+        #endif
+    }
+
+    // create new object
+    mp_vfs_mount_t *vfs = m_new_obj(mp_vfs_mount_t);
+    vfs->str = mnt_str;
+    vfs->len = mnt_len;
+    vfs->obj = vfs_obj;
+    vfs->next = NULL;
+
+    // call the underlying object to do any mounting operation
+    mp_vfs_proxy_call(vfs, MP_QSTR_mount, 2, (mp_obj_t*)&args);
+
+    // check that the destination mount point is unused
+    const char *path_out;
+    mp_vfs_mount_t *existing_mount = mp_vfs_lookup_path(mp_obj_str_get_str(pos_args[1]), &path_out);
+    if (existing_mount != MP_VFS_NONE && existing_mount != MP_VFS_ROOT) {
+        if (vfs->len != 1 && existing_mount->len == 1) {
+            // if root dir is mounted, still allow to mount something within a subdir of root
+        } else {
+            // mount point in use
+            mp_raise_OSError(MP_EPERM);
+        }
+    }
+
+    // insert the vfs into the mount table
+    mp_vfs_mount_t **vfsp = &MP_STATE_VM(vfs_mount_table);
+    while (*vfsp != NULL) {
+        if ((*vfsp)->len == 1) {
+            // make sure anything mounted at the root stays at the end of the list
+            vfs->next = *vfsp;
+            break;
+        }
+        vfsp = &(*vfsp)->next;
+    }
+    *vfsp = vfs;
+
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_KW(mp_vfs_mount_obj, 2, mp_vfs_mount);
+
+mp_obj_t mp_vfs_umount(mp_obj_t mnt_in) {
+    // remove vfs from the mount table
+    mp_vfs_mount_t *vfs = NULL;
+    size_t mnt_len;
+    const char *mnt_str = NULL;
+    if (MP_OBJ_IS_STR(mnt_in)) {
+        mnt_str = mp_obj_str_get_data(mnt_in, &mnt_len);
+    }
+    for (mp_vfs_mount_t **vfsp = &MP_STATE_VM(vfs_mount_table); *vfsp != NULL; vfsp = &(*vfsp)->next) {
+        if ((mnt_str != NULL && !memcmp(mnt_str, (*vfsp)->str, mnt_len + 1)) || (*vfsp)->obj == mnt_in) {
+            vfs = *vfsp;
+            *vfsp = (*vfsp)->next;
+            break;
+        }
+    }
+
+    if (vfs == NULL) {
+        mp_raise_OSError(MP_EINVAL);
+    }
+
+    // if we unmounted the current device then set current to root
+    if (MP_STATE_VM(vfs_cur) == vfs) {
+        MP_STATE_VM(vfs_cur) = MP_VFS_ROOT;
+    }
+
+    // call the underlying object to do any unmounting operation
+    mp_vfs_proxy_call(vfs, MP_QSTR_umount, 0, NULL);
+
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_umount_obj, mp_vfs_umount);
+
+// Note: buffering and encoding args are currently ignored
+mp_obj_t mp_vfs_open(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+    enum { ARG_file, ARG_mode, ARG_encoding };
+    static const mp_arg_t allowed_args[] = {
+        { MP_QSTR_file, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },
+        { MP_QSTR_mode, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_QSTR(MP_QSTR_r)} },
+        { MP_QSTR_buffering, MP_ARG_INT, {.u_int = -1} },
+        { MP_QSTR_encoding, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },
+    };
+
+    // parse args
+    mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+    mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+    mp_vfs_mount_t *vfs = lookup_path((mp_obj_t)args[ARG_file].u_rom_obj, &args[ARG_file].u_obj);
+    return mp_vfs_proxy_call(vfs, MP_QSTR_open, 2, (mp_obj_t*)&args);
+}
+MP_DEFINE_CONST_FUN_OBJ_KW(mp_vfs_open_obj, 0, mp_vfs_open);
+
+mp_obj_t mp_vfs_chdir(mp_obj_t path_in) {
+    mp_obj_t path_out;
+    mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out);
+    MP_STATE_VM(vfs_cur) = vfs;
+    if (vfs == MP_VFS_ROOT) {
+        // If we change to the root dir and a VFS is mounted at the root then
+        // we must change that VFS's current dir to the root dir so that any
+        // subsequent relative paths begin at the root of that VFS.
+        for (vfs = MP_STATE_VM(vfs_mount_table); vfs != NULL; vfs = vfs->next) {
+            if (vfs->len == 1) {
+                mp_obj_t root = mp_obj_new_str("/", 1, false);
+                mp_vfs_proxy_call(vfs, MP_QSTR_chdir, 1, &root);
+                break;
+            }
+        }
+    } else {
+        mp_vfs_proxy_call(vfs, MP_QSTR_chdir, 1, &path_out);
+    }
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_chdir_obj, mp_vfs_chdir);
+
+mp_obj_t mp_vfs_getcwd(void) {
+    if (MP_STATE_VM(vfs_cur) == MP_VFS_ROOT) {
+        return MP_OBJ_NEW_QSTR(MP_QSTR__slash_);
+    }
+    mp_obj_t cwd_o = mp_vfs_proxy_call(MP_STATE_VM(vfs_cur), MP_QSTR_getcwd, 0, NULL);
+    if (MP_STATE_VM(vfs_cur)->len == 1) {
+        // don't prepend "/" for vfs mounted at root
+        return cwd_o;
+    }
+    const char *cwd = mp_obj_str_get_str(cwd_o);
+    vstr_t vstr;
+    vstr_init(&vstr, MP_STATE_VM(vfs_cur)->len + strlen(cwd) + 1);
+    vstr_add_strn(&vstr, MP_STATE_VM(vfs_cur)->str, MP_STATE_VM(vfs_cur)->len);
+    if (!(cwd[0] == '/' && cwd[1] == 0)) {
+        vstr_add_str(&vstr, cwd);
+    }
+    return mp_obj_new_str_from_vstr(&mp_type_str, &vstr);
+}
+MP_DEFINE_CONST_FUN_OBJ_0(mp_vfs_getcwd_obj, mp_vfs_getcwd);
+
+typedef struct _mp_vfs_ilistdir_it_t {
+    mp_obj_base_t base;
+    mp_fun_1_t iternext;
+    union {
+        mp_vfs_mount_t *vfs;
+        mp_obj_t iter;
+    } cur;
+    bool is_str;
+    bool is_iter;
+} mp_vfs_ilistdir_it_t;
+
+STATIC mp_obj_t mp_vfs_ilistdir_it_iternext(mp_obj_t self_in) {
+    mp_vfs_ilistdir_it_t *self = MP_OBJ_TO_PTR(self_in);
+    if (self->is_iter) {
+        // continue delegating to root dir
+        return mp_iternext(self->cur.iter);
+    } else if (self->cur.vfs == NULL) {
+        // finished iterating mount points and no root dir is mounted
+        return MP_OBJ_STOP_ITERATION;
+    } else {
+        // continue iterating mount points
+        mp_vfs_mount_t *vfs = self->cur.vfs;
+        self->cur.vfs = vfs->next;
+        if (vfs->len == 1) {
+            // vfs is mounted at root dir, delegate to it
+            mp_obj_t root = mp_obj_new_str("/", 1, false);
+            self->is_iter = true;
+            self->cur.iter = mp_vfs_proxy_call(vfs, MP_QSTR_ilistdir, 1, &root);
+            return mp_iternext(self->cur.iter);
+        } else {
+            // a mounted directory
+            mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL));
+            t->items[0] = mp_obj_new_str_of_type(
+                self->is_str ? &mp_type_str : &mp_type_bytes,
+                (const byte*)vfs->str + 1, vfs->len - 1);
+            t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFDIR);
+            t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // no inode number
+            return MP_OBJ_FROM_PTR(t);
+        }
+    }
+}
+
+mp_obj_t mp_vfs_ilistdir(size_t n_args, const mp_obj_t *args) {
+    mp_obj_t path_in;
+    if (n_args == 1) {
+        path_in = args[0];
+    } else {
+        path_in = MP_OBJ_NEW_QSTR(MP_QSTR_);
+    }
+
+    mp_obj_t path_out;
+    mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out);
+
+    if (vfs == MP_VFS_ROOT) {
+        // list the root directory
+        mp_vfs_ilistdir_it_t *iter = m_new_obj(mp_vfs_ilistdir_it_t);
+        iter->base.type = &mp_type_polymorph_iter;
+        iter->iternext = mp_vfs_ilistdir_it_iternext;
+        iter->cur.vfs = MP_STATE_VM(vfs_mount_table);
+        iter->is_str = mp_obj_get_type(path_in) == &mp_type_str;
+        iter->is_iter = false;
+        return MP_OBJ_FROM_PTR(iter);
+    }
+
+    return mp_vfs_proxy_call(vfs, MP_QSTR_ilistdir, 1, &path_out);
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_vfs_ilistdir_obj, 0, 1, mp_vfs_ilistdir);
+
+mp_obj_t mp_vfs_listdir(size_t n_args, const mp_obj_t *args) {
+    mp_obj_t iter = mp_vfs_ilistdir(n_args, args);
+    mp_obj_t dir_list = mp_obj_new_list(0, NULL);
+    mp_obj_t next;
+    while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
+        mp_obj_t *items;
+        mp_obj_get_array_fixed_n(next, 3, &items);
+        mp_obj_list_append(dir_list, items[0]);
+    }
+    return dir_list;
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_vfs_listdir_obj, 0, 1, mp_vfs_listdir);
+
+mp_obj_t mp_vfs_mkdir(mp_obj_t path_in) {
+    mp_obj_t path_out;
+    mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out);
+    if (vfs == MP_VFS_ROOT || (vfs != MP_VFS_NONE && !strcmp(mp_obj_str_get_str(path_out), "/"))) {
+        mp_raise_OSError(MP_EEXIST);
+    }
+    return mp_vfs_proxy_call(vfs, MP_QSTR_mkdir, 1, &path_out);
+}
+MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_mkdir_obj, mp_vfs_mkdir);
+
+mp_obj_t mp_vfs_remove(mp_obj_t path_in) {
+    mp_obj_t path_out;
+    mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out);
+    return mp_vfs_proxy_call(vfs, MP_QSTR_remove, 1, &path_out);
+}
+MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_remove_obj, mp_vfs_remove);
+
+mp_obj_t mp_vfs_rename(mp_obj_t old_path_in, mp_obj_t new_path_in) {
+    mp_obj_t args[2];
+    mp_vfs_mount_t *old_vfs = lookup_path(old_path_in, &args[0]);
+    mp_vfs_mount_t *new_vfs = lookup_path(new_path_in, &args[1]);
+    if (old_vfs != new_vfs) {
+        // can't rename across filesystems
+        mp_raise_OSError(MP_EPERM);
+    }
+    return mp_vfs_proxy_call(old_vfs, MP_QSTR_rename, 2, args);
+}
+MP_DEFINE_CONST_FUN_OBJ_2(mp_vfs_rename_obj, mp_vfs_rename);
+
+mp_obj_t mp_vfs_rmdir(mp_obj_t path_in) {
+    mp_obj_t path_out;
+    mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out);
+    return mp_vfs_proxy_call(vfs, MP_QSTR_rmdir, 1, &path_out);
+}
+MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_rmdir_obj, mp_vfs_rmdir);
+
+mp_obj_t mp_vfs_stat(mp_obj_t path_in) {
+    mp_obj_t path_out;
+    mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out);
+    if (vfs == MP_VFS_ROOT) {
+        mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
+        t->items[0] = MP_OBJ_NEW_SMALL_INT(MP_S_IFDIR); // st_mode
+        for (int i = 1; i <= 9; ++i) {
+            t->items[i] = MP_OBJ_NEW_SMALL_INT(0); // dev, nlink, uid, gid, size, atime, mtime, ctime
+        }
+        return MP_OBJ_FROM_PTR(t);
+    }
+    return mp_vfs_proxy_call(vfs, MP_QSTR_stat, 1, &path_out);
+}
+MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_stat_obj, mp_vfs_stat);
+
+mp_obj_t mp_vfs_statvfs(mp_obj_t path_in) {
+    mp_obj_t path_out;
+    mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out);
+    if (vfs == MP_VFS_ROOT) {
+        // statvfs called on the root directory, see if there's anything mounted there
+        for (vfs = MP_STATE_VM(vfs_mount_table); vfs != NULL; vfs = vfs->next) {
+            if (vfs->len == 1) {
+                break;
+            }
+        }
+
+        // If there's nothing mounted at root then return a mostly-empty tuple
+        if (vfs == NULL) {
+            mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
+
+            // fill in: bsize, frsize, blocks, bfree, bavail, files, ffree, favail, flags
+            for (int i = 0; i <= 8; ++i) {
+                t->items[i] = MP_OBJ_NEW_SMALL_INT(0);
+            }
+
+            // Put something sensible in f_namemax
+            t->items[9] = MP_OBJ_NEW_SMALL_INT(MICROPY_ALLOC_PATH_MAX);
+
+            return MP_OBJ_FROM_PTR(t);
+        }
+
+        // VFS mounted at root so delegate the call to it
+        path_out = MP_OBJ_NEW_QSTR(MP_QSTR__slash_);
+    }
+    return mp_vfs_proxy_call(vfs, MP_QSTR_statvfs, 1, &path_out);
+}
+MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_statvfs_obj, mp_vfs_statvfs);
+
+#endif // MICROPY_VFS

+ 85 - 0
extmod/vfs.h

@@ -0,0 +1,85 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_EXTMOD_VFS_H
+#define MICROPY_INCLUDED_EXTMOD_VFS_H
+
+#include "py/lexer.h"
+#include "py/obj.h"
+
+// return values of mp_vfs_lookup_path
+// ROOT is 0 so that the default current directory is the root directory
+#define MP_VFS_NONE ((mp_vfs_mount_t*)1)
+#define MP_VFS_ROOT ((mp_vfs_mount_t*)0)
+
+// MicroPython's port-standardized versions of stat constants
+#define MP_S_IFDIR (0x4000)
+#define MP_S_IFREG (0x8000)
+
+// constants for block protocol ioctl
+#define BP_IOCTL_INIT           (1)
+#define BP_IOCTL_DEINIT         (2)
+#define BP_IOCTL_SYNC           (3)
+#define BP_IOCTL_SEC_COUNT      (4)
+#define BP_IOCTL_SEC_SIZE       (5)
+
+typedef struct _mp_vfs_mount_t {
+    const char *str; // mount point with leading /
+    size_t len;
+    mp_obj_t obj;
+    struct _mp_vfs_mount_t *next;
+} mp_vfs_mount_t;
+
+mp_vfs_mount_t *mp_vfs_lookup_path(const char *path, const char **path_out);
+mp_import_stat_t mp_vfs_import_stat(const char *path);
+mp_obj_t mp_vfs_mount(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
+mp_obj_t mp_vfs_umount(mp_obj_t mnt_in);
+mp_obj_t mp_vfs_open(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
+mp_obj_t mp_vfs_chdir(mp_obj_t path_in);
+mp_obj_t mp_vfs_getcwd(void);
+mp_obj_t mp_vfs_ilistdir(size_t n_args, const mp_obj_t *args);
+mp_obj_t mp_vfs_listdir(size_t n_args, const mp_obj_t *args);
+mp_obj_t mp_vfs_mkdir(mp_obj_t path_in);
+mp_obj_t mp_vfs_remove(mp_obj_t path_in);
+mp_obj_t mp_vfs_rename(mp_obj_t old_path_in, mp_obj_t new_path_in);
+mp_obj_t mp_vfs_rmdir(mp_obj_t path_in);
+mp_obj_t mp_vfs_stat(mp_obj_t path_in);
+mp_obj_t mp_vfs_statvfs(mp_obj_t path_in);
+
+MP_DECLARE_CONST_FUN_OBJ_KW(mp_vfs_mount_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_umount_obj);
+MP_DECLARE_CONST_FUN_OBJ_KW(mp_vfs_open_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_chdir_obj);
+MP_DECLARE_CONST_FUN_OBJ_0(mp_vfs_getcwd_obj);
+MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_vfs_ilistdir_obj);
+MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_vfs_listdir_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_mkdir_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_remove_obj);
+MP_DECLARE_CONST_FUN_OBJ_2(mp_vfs_rename_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_rmdir_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_stat_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_statvfs_obj);
+
+#endif // MICROPY_INCLUDED_EXTMOD_VFS_H

+ 344 - 0
extmod/vfs_fat.c

@@ -0,0 +1,344 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014 Damien P. George
+ * Copyright (c) 2016 Paul Sokolovsky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/mpconfig.h"
+#if MICROPY_VFS_FAT
+
+#if !MICROPY_VFS
+#error "with MICROPY_VFS_FAT enabled, must also enable MICROPY_VFS"
+#endif
+
+#include <string.h>
+#include "py/runtime.h"
+#include "py/mperrno.h"
+#include "lib/oofatfs/ff.h"
+#include "extmod/vfs_fat.h"
+#include "lib/timeutils/timeutils.h"
+
+#if _MAX_SS == _MIN_SS
+#define SECSIZE(fs) (_MIN_SS)
+#else
+#define SECSIZE(fs) ((fs)->ssize)
+#endif
+
+#define mp_obj_fat_vfs_t fs_user_mount_t
+
+STATIC mp_obj_t fat_vfs_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+    mp_arg_check_num(n_args, n_kw, 1, 1, false);
+
+    // create new object
+    fs_user_mount_t *vfs = m_new_obj(fs_user_mount_t);
+    vfs->base.type = type;
+    vfs->flags = FSUSER_FREE_OBJ;
+    vfs->fatfs.drv = vfs;
+
+    // load block protocol methods
+    mp_load_method(args[0], MP_QSTR_readblocks, vfs->readblocks);
+    mp_load_method_maybe(args[0], MP_QSTR_writeblocks, vfs->writeblocks);
+    mp_load_method_maybe(args[0], MP_QSTR_ioctl, vfs->u.ioctl);
+    if (vfs->u.ioctl[0] != MP_OBJ_NULL) {
+        // device supports new block protocol, so indicate it
+        vfs->flags |= FSUSER_HAVE_IOCTL;
+    } else {
+        // no ioctl method, so assume the device uses the old block protocol
+        mp_load_method_maybe(args[0], MP_QSTR_sync, vfs->u.old.sync);
+        mp_load_method(args[0], MP_QSTR_count, vfs->u.old.count);
+    }
+
+    return MP_OBJ_FROM_PTR(vfs);
+}
+
+STATIC mp_obj_t fat_vfs_mkfs(mp_obj_t bdev_in) {
+    // create new object
+    fs_user_mount_t *vfs = MP_OBJ_TO_PTR(fat_vfs_make_new(&mp_fat_vfs_type, 1, 0, &bdev_in));
+
+    // make the filesystem
+    uint8_t working_buf[_MAX_SS];
+    FRESULT res = f_mkfs(&vfs->fatfs, FM_FAT | FM_SFD, 0, working_buf, sizeof(working_buf));
+    if (res != FR_OK) {
+        mp_raise_OSError(fresult_to_errno_table[res]);
+    }
+
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_mkfs_fun_obj, fat_vfs_mkfs);
+STATIC MP_DEFINE_CONST_STATICMETHOD_OBJ(fat_vfs_mkfs_obj, MP_ROM_PTR(&fat_vfs_mkfs_fun_obj));
+
+STATIC MP_DEFINE_CONST_FUN_OBJ_3(fat_vfs_open_obj, fatfs_builtin_open_self);
+
+STATIC mp_obj_t fat_vfs_ilistdir_func(size_t n_args, const mp_obj_t *args) {
+    mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(args[0]);
+    bool is_str_type = true;
+    const char *path;
+    if (n_args == 2) {
+        if (mp_obj_get_type(args[1]) == &mp_type_bytes) {
+            is_str_type = false;
+        }
+        path = mp_obj_str_get_str(args[1]);
+    } else {
+        path = "";
+    }
+
+    return fat_vfs_ilistdir2(self, path, is_str_type);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fat_vfs_ilistdir_obj, 1, 2, fat_vfs_ilistdir_func);
+
+STATIC mp_obj_t fat_vfs_remove_internal(mp_obj_t vfs_in, mp_obj_t path_in, mp_int_t attr) {
+    mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in);
+    const char *path = mp_obj_str_get_str(path_in);
+
+    FILINFO fno;
+    FRESULT res = f_stat(&self->fatfs, path, &fno);
+
+    if (res != FR_OK) {
+        mp_raise_OSError(fresult_to_errno_table[res]);
+    }
+
+    // check if path is a file or directory
+    if ((fno.fattrib & AM_DIR) == attr) {
+        res = f_unlink(&self->fatfs, path);
+
+        if (res != FR_OK) {
+            mp_raise_OSError(fresult_to_errno_table[res]);
+        }
+        return mp_const_none;
+    } else {
+        mp_raise_OSError(attr ? MP_ENOTDIR : MP_EISDIR);
+    }
+}
+
+STATIC mp_obj_t fat_vfs_remove(mp_obj_t vfs_in, mp_obj_t path_in) {
+    return fat_vfs_remove_internal(vfs_in, path_in, 0); // 0 == file attribute
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_remove_obj, fat_vfs_remove);
+
+STATIC mp_obj_t fat_vfs_rmdir(mp_obj_t vfs_in, mp_obj_t path_in) {
+    return fat_vfs_remove_internal(vfs_in, path_in, AM_DIR);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_rmdir_obj, fat_vfs_rmdir);
+
+STATIC mp_obj_t fat_vfs_rename(mp_obj_t vfs_in, mp_obj_t path_in, mp_obj_t path_out) {
+    mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in);
+    const char *old_path = mp_obj_str_get_str(path_in);
+    const char *new_path = mp_obj_str_get_str(path_out);
+    FRESULT res = f_rename(&self->fatfs, old_path, new_path);
+    if (res == FR_EXIST) {
+        // if new_path exists then try removing it (but only if it's a file)
+        fat_vfs_remove_internal(vfs_in, path_out, 0); // 0 == file attribute
+        // try to rename again
+        res = f_rename(&self->fatfs, old_path, new_path);
+    }
+    if (res == FR_OK) {
+        return mp_const_none;
+    } else {
+        mp_raise_OSError(fresult_to_errno_table[res]);
+    }
+
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_3(fat_vfs_rename_obj, fat_vfs_rename);
+
+STATIC mp_obj_t fat_vfs_mkdir(mp_obj_t vfs_in, mp_obj_t path_o) {
+    mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in);
+    const char *path = mp_obj_str_get_str(path_o);
+    FRESULT res = f_mkdir(&self->fatfs, path);
+    if (res == FR_OK) {
+        return mp_const_none;
+    } else {
+        mp_raise_OSError(fresult_to_errno_table[res]);
+    }
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_mkdir_obj, fat_vfs_mkdir);
+
+/// Change current directory.
+STATIC mp_obj_t fat_vfs_chdir(mp_obj_t vfs_in, mp_obj_t path_in) {
+    mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in);
+    const char *path;
+    path = mp_obj_str_get_str(path_in);
+
+    FRESULT res = f_chdir(&self->fatfs, path);
+
+    if (res != FR_OK) {
+        mp_raise_OSError(fresult_to_errno_table[res]);
+    }
+
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_chdir_obj, fat_vfs_chdir);
+
+/// Get the current directory.
+STATIC mp_obj_t fat_vfs_getcwd(mp_obj_t vfs_in) {
+    mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in);
+    char buf[MICROPY_ALLOC_PATH_MAX + 1];
+    FRESULT res = f_getcwd(&self->fatfs, buf, sizeof(buf));
+    if (res != FR_OK) {
+        mp_raise_OSError(fresult_to_errno_table[res]);
+    }
+    return mp_obj_new_str(buf, strlen(buf), false);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_getcwd_obj, fat_vfs_getcwd);
+
+/// \function stat(path)
+/// Get the status of a file or directory.
+STATIC mp_obj_t fat_vfs_stat(mp_obj_t vfs_in, mp_obj_t path_in) {
+    mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in);
+    const char *path = mp_obj_str_get_str(path_in);
+
+    FILINFO fno;
+    if (path[0] == 0 || (path[0] == '/' && path[1] == 0)) {
+        // stat root directory
+        fno.fsize = 0;
+        fno.fdate = 0x2821; // Jan 1, 2000
+        fno.ftime = 0;
+        fno.fattrib = AM_DIR;
+    } else {
+        FRESULT res = f_stat(&self->fatfs, path, &fno);
+        if (res != FR_OK) {
+            mp_raise_OSError(fresult_to_errno_table[res]);
+        }
+    }
+
+    mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
+    mp_int_t mode = 0;
+    if (fno.fattrib & AM_DIR) {
+        mode |= MP_S_IFDIR;
+    } else {
+        mode |= MP_S_IFREG;
+    }
+    mp_int_t seconds = timeutils_seconds_since_2000(
+        1980 + ((fno.fdate >> 9) & 0x7f),
+        (fno.fdate >> 5) & 0x0f,
+        fno.fdate & 0x1f,
+        (fno.ftime >> 11) & 0x1f,
+        (fno.ftime >> 5) & 0x3f,
+        2 * (fno.ftime & 0x1f)
+    );
+    t->items[0] = MP_OBJ_NEW_SMALL_INT(mode); // st_mode
+    t->items[1] = MP_OBJ_NEW_SMALL_INT(0); // st_ino
+    t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // st_dev
+    t->items[3] = MP_OBJ_NEW_SMALL_INT(0); // st_nlink
+    t->items[4] = MP_OBJ_NEW_SMALL_INT(0); // st_uid
+    t->items[5] = MP_OBJ_NEW_SMALL_INT(0); // st_gid
+    t->items[6] = mp_obj_new_int_from_uint(fno.fsize); // st_size
+    t->items[7] = MP_OBJ_NEW_SMALL_INT(seconds); // st_atime
+    t->items[8] = MP_OBJ_NEW_SMALL_INT(seconds); // st_mtime
+    t->items[9] = MP_OBJ_NEW_SMALL_INT(seconds); // st_ctime
+
+    return MP_OBJ_FROM_PTR(t);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_stat_obj, fat_vfs_stat);
+
+// Get the status of a VFS.
+STATIC mp_obj_t fat_vfs_statvfs(mp_obj_t vfs_in, mp_obj_t path_in) {
+    mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in);
+    (void)path_in;
+
+    DWORD nclst;
+    FATFS *fatfs = &self->fatfs;
+    FRESULT res = f_getfree(fatfs, &nclst);
+    if (FR_OK != res) {
+        mp_raise_OSError(fresult_to_errno_table[res]);
+    }
+
+    mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
+
+    t->items[0] = MP_OBJ_NEW_SMALL_INT(fatfs->csize * SECSIZE(fatfs)); // f_bsize
+    t->items[1] = t->items[0]; // f_frsize
+    t->items[2] = MP_OBJ_NEW_SMALL_INT((fatfs->n_fatent - 2)); // f_blocks
+    t->items[3] = MP_OBJ_NEW_SMALL_INT(nclst); // f_bfree
+    t->items[4] = t->items[3]; // f_bavail
+    t->items[5] = MP_OBJ_NEW_SMALL_INT(0); // f_files
+    t->items[6] = MP_OBJ_NEW_SMALL_INT(0); // f_ffree
+    t->items[7] = MP_OBJ_NEW_SMALL_INT(0); // f_favail
+    t->items[8] = MP_OBJ_NEW_SMALL_INT(0); // f_flags
+    t->items[9] = MP_OBJ_NEW_SMALL_INT(_MAX_LFN); // f_namemax
+
+    return MP_OBJ_FROM_PTR(t);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_statvfs_obj, fat_vfs_statvfs);
+
+STATIC mp_obj_t vfs_fat_mount(mp_obj_t self_in, mp_obj_t readonly, mp_obj_t mkfs) {
+    fs_user_mount_t *self = MP_OBJ_TO_PTR(self_in);
+
+    // Read-only device indicated by writeblocks[0] == MP_OBJ_NULL.
+    // User can specify read-only device by:
+    //  1. readonly=True keyword argument
+    //  2. nonexistent writeblocks method (then writeblocks[0] == MP_OBJ_NULL already)
+    if (mp_obj_is_true(readonly)) {
+        self->writeblocks[0] = MP_OBJ_NULL;
+    }
+
+    // mount the block device
+    FRESULT res = f_mount(&self->fatfs);
+
+    // check if we need to make the filesystem
+    if (res == FR_NO_FILESYSTEM && mp_obj_is_true(mkfs)) {
+        uint8_t working_buf[_MAX_SS];
+        res = f_mkfs(&self->fatfs, FM_FAT | FM_SFD, 0, working_buf, sizeof(working_buf));
+    }
+    if (res != FR_OK) {
+        mp_raise_OSError(fresult_to_errno_table[res]);
+    }
+
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_3(vfs_fat_mount_obj, vfs_fat_mount);
+
+STATIC mp_obj_t vfs_fat_umount(mp_obj_t self_in) {
+    fs_user_mount_t *self = MP_OBJ_TO_PTR(self_in);
+    FRESULT res = f_umount(&self->fatfs);
+    if (res != FR_OK) {
+        mp_raise_OSError(fresult_to_errno_table[res]);
+    }
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_umount_obj, vfs_fat_umount);
+
+STATIC const mp_rom_map_elem_t fat_vfs_locals_dict_table[] = {
+    { MP_ROM_QSTR(MP_QSTR_mkfs), MP_ROM_PTR(&fat_vfs_mkfs_obj) },
+    { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&fat_vfs_open_obj) },
+    { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&fat_vfs_ilistdir_obj) },
+    { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&fat_vfs_mkdir_obj) },
+    { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&fat_vfs_rmdir_obj) },
+    { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&fat_vfs_chdir_obj) },
+    { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&fat_vfs_getcwd_obj) },
+    { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&fat_vfs_remove_obj) },
+    { MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&fat_vfs_rename_obj) },
+    { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&fat_vfs_stat_obj) },
+    { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&fat_vfs_statvfs_obj) },
+    { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&vfs_fat_mount_obj) },
+    { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&fat_vfs_umount_obj) },
+};
+STATIC MP_DEFINE_CONST_DICT(fat_vfs_locals_dict, fat_vfs_locals_dict_table);
+
+const mp_obj_type_t mp_fat_vfs_type = {
+    { &mp_type_type },
+    .name = MP_QSTR_VfsFat,
+    .make_new = fat_vfs_make_new,
+    .locals_dict = (mp_obj_dict_t*)&fat_vfs_locals_dict,
+};
+
+#endif // MICROPY_VFS_FAT

+ 64 - 0
extmod/vfs_fat.h

@@ -0,0 +1,64 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_EXTMOD_VFS_FAT_H
+#define MICROPY_INCLUDED_EXTMOD_VFS_FAT_H
+
+#include "py/lexer.h"
+#include "py/obj.h"
+#include "lib/oofatfs/ff.h"
+#include "extmod/vfs.h"
+
+// these are the values for fs_user_mount_t.flags
+#define FSUSER_NATIVE       (0x0001) // readblocks[2]/writeblocks[2] contain native func
+#define FSUSER_FREE_OBJ     (0x0002) // fs_user_mount_t obj should be freed on umount
+#define FSUSER_HAVE_IOCTL   (0x0004) // new protocol with ioctl
+
+typedef struct _fs_user_mount_t {
+    mp_obj_base_t base;
+    uint16_t flags;
+    mp_obj_t readblocks[4];
+    mp_obj_t writeblocks[4];
+    // new protocol uses just ioctl, old uses sync (optional) and count
+    union {
+        mp_obj_t ioctl[4];
+        struct {
+            mp_obj_t sync[2];
+            mp_obj_t count[2];
+        } old;
+    } u;
+    FATFS fatfs;
+} fs_user_mount_t;
+
+extern const byte fresult_to_errno_table[20];
+extern const mp_obj_type_t mp_fat_vfs_type;
+
+mp_import_stat_t fat_vfs_import_stat(struct _fs_user_mount_t *vfs, const char *path);
+mp_obj_t fatfs_builtin_open_self(mp_obj_t self_in, mp_obj_t path, mp_obj_t mode);
+MP_DECLARE_CONST_FUN_OBJ_KW(mp_builtin_open_obj);
+
+mp_obj_t fat_vfs_ilistdir2(struct _fs_user_mount_t *vfs, const char *path, bool is_str_type);
+
+#endif // MICROPY_INCLUDED_EXTMOD_VFS_FAT_H

+ 280 - 0
extmod/vfs_fat_diskio.c

@@ -0,0 +1,280 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * Original template for this file comes from:
+ * Low level disk I/O module skeleton for FatFs, (C)ChaN, 2013
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/mpconfig.h"
+#if MICROPY_VFS && MICROPY_VFS_FAT
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include "py/mphal.h"
+
+#include "py/runtime.h"
+#include "lib/oofatfs/ff.h"
+#include "lib/oofatfs/diskio.h"
+#include "extmod/vfs_fat.h"
+
+#if _MAX_SS == _MIN_SS
+#define SECSIZE(fs) (_MIN_SS)
+#else
+#define SECSIZE(fs) ((fs)->ssize)
+#endif
+
+typedef void *bdev_t;
+STATIC fs_user_mount_t *disk_get_device(void *bdev) {
+    return (fs_user_mount_t*)bdev;
+}
+
+/*-----------------------------------------------------------------------*/
+/* Initialize a Drive                                                    */
+/*-----------------------------------------------------------------------*/
+
+STATIC
+DSTATUS disk_initialize (
+    bdev_t pdrv              /* Physical drive nmuber (0..) */
+)
+{
+    fs_user_mount_t *vfs = disk_get_device(pdrv);
+    if (vfs == NULL) {
+        return STA_NOINIT;
+    }
+
+    if (vfs->flags & FSUSER_HAVE_IOCTL) {
+        // new protocol with ioctl; call ioctl(INIT, 0)
+        vfs->u.ioctl[2] = MP_OBJ_NEW_SMALL_INT(BP_IOCTL_INIT);
+        vfs->u.ioctl[3] = MP_OBJ_NEW_SMALL_INT(0); // unused
+        mp_obj_t ret = mp_call_method_n_kw(2, 0, vfs->u.ioctl);
+        if (ret != mp_const_none && MP_OBJ_SMALL_INT_VALUE(ret) != 0) {
+            // error initialising
+            return STA_NOINIT;
+        }
+    }
+
+    if (vfs->writeblocks[0] == MP_OBJ_NULL) {
+        return STA_PROTECT;
+    } else {
+        return 0;
+    }
+}
+
+/*-----------------------------------------------------------------------*/
+/* Get Disk Status                                                       */
+/*-----------------------------------------------------------------------*/
+
+STATIC
+DSTATUS disk_status (
+    bdev_t pdrv      /* Physical drive nmuber (0..) */
+)
+{
+    fs_user_mount_t *vfs = disk_get_device(pdrv);
+    if (vfs == NULL) {
+        return STA_NOINIT;
+    }
+
+    if (vfs->writeblocks[0] == MP_OBJ_NULL) {
+        return STA_PROTECT;
+    } else {
+        return 0;
+    }
+}
+
+/*-----------------------------------------------------------------------*/
+/* Read Sector(s)                                                        */
+/*-----------------------------------------------------------------------*/
+
+DRESULT disk_read (
+    bdev_t pdrv,      /* Physical drive nmuber (0..) */
+    BYTE *buff,        /* Data buffer to store read data */
+    DWORD sector,    /* Sector address (LBA) */
+    UINT count        /* Number of sectors to read (1..128) */
+)
+{
+    fs_user_mount_t *vfs = disk_get_device(pdrv);
+    if (vfs == NULL) {
+        return RES_PARERR;
+    }
+
+    if (vfs->flags & FSUSER_NATIVE) {
+        mp_uint_t (*f)(uint8_t*, uint32_t, uint32_t) = (void*)(uintptr_t)vfs->readblocks[2];
+        if (f(buff, sector, count) != 0) {
+            return RES_ERROR;
+        }
+    } else {
+        vfs->readblocks[2] = MP_OBJ_NEW_SMALL_INT(sector);
+        vfs->readblocks[3] = mp_obj_new_bytearray_by_ref(count * SECSIZE(&vfs->fatfs), buff);
+        mp_call_method_n_kw(2, 0, vfs->readblocks);
+        // TODO handle error return
+    }
+
+    return RES_OK;
+}
+
+/*-----------------------------------------------------------------------*/
+/* Write Sector(s)                                                       */
+/*-----------------------------------------------------------------------*/
+
+DRESULT disk_write (
+    bdev_t pdrv,          /* Physical drive nmuber (0..) */
+    const BYTE *buff,    /* Data to be written */
+    DWORD sector,        /* Sector address (LBA) */
+    UINT count            /* Number of sectors to write (1..128) */
+)
+{
+    fs_user_mount_t *vfs = disk_get_device(pdrv);
+    if (vfs == NULL) {
+        return RES_PARERR;
+    }
+
+    if (vfs->writeblocks[0] == MP_OBJ_NULL) {
+        // read-only block device
+        return RES_WRPRT;
+    }
+
+    if (vfs->flags & FSUSER_NATIVE) {
+        mp_uint_t (*f)(const uint8_t*, uint32_t, uint32_t) = (void*)(uintptr_t)vfs->writeblocks[2];
+        if (f(buff, sector, count) != 0) {
+            return RES_ERROR;
+        }
+    } else {
+        vfs->writeblocks[2] = MP_OBJ_NEW_SMALL_INT(sector);
+        vfs->writeblocks[3] = mp_obj_new_bytearray_by_ref(count * SECSIZE(&vfs->fatfs), (void*)buff);
+        mp_call_method_n_kw(2, 0, vfs->writeblocks);
+        // TODO handle error return
+    }
+
+    return RES_OK;
+}
+
+
+/*-----------------------------------------------------------------------*/
+/* Miscellaneous Functions                                               */
+/*-----------------------------------------------------------------------*/
+
+DRESULT disk_ioctl (
+    bdev_t pdrv,      /* Physical drive nmuber (0..) */
+    BYTE cmd,        /* Control code */
+    void *buff        /* Buffer to send/receive control data */
+)
+{
+    fs_user_mount_t *vfs = disk_get_device(pdrv);
+    if (vfs == NULL) {
+        return RES_PARERR;
+    }
+
+    if (vfs->flags & FSUSER_HAVE_IOCTL) {
+        // new protocol with ioctl
+        switch (cmd) {
+            case CTRL_SYNC:
+                vfs->u.ioctl[2] = MP_OBJ_NEW_SMALL_INT(BP_IOCTL_SYNC);
+                vfs->u.ioctl[3] = MP_OBJ_NEW_SMALL_INT(0); // unused
+                mp_call_method_n_kw(2, 0, vfs->u.ioctl);
+                return RES_OK;
+
+            case GET_SECTOR_COUNT: {
+                vfs->u.ioctl[2] = MP_OBJ_NEW_SMALL_INT(BP_IOCTL_SEC_COUNT);
+                vfs->u.ioctl[3] = MP_OBJ_NEW_SMALL_INT(0); // unused
+                mp_obj_t ret = mp_call_method_n_kw(2, 0, vfs->u.ioctl);
+                *((DWORD*)buff) = mp_obj_get_int(ret);
+                return RES_OK;
+            }
+
+            case GET_SECTOR_SIZE: {
+                vfs->u.ioctl[2] = MP_OBJ_NEW_SMALL_INT(BP_IOCTL_SEC_SIZE);
+                vfs->u.ioctl[3] = MP_OBJ_NEW_SMALL_INT(0); // unused
+                mp_obj_t ret = mp_call_method_n_kw(2, 0, vfs->u.ioctl);
+                if (ret == mp_const_none) {
+                    // Default sector size
+                    *((WORD*)buff) = 512;
+                } else {
+                    *((WORD*)buff) = mp_obj_get_int(ret);
+                }
+                #if _MAX_SS != _MIN_SS
+                // need to store ssize because we use it in disk_read/disk_write
+                vfs->fatfs.ssize = *((WORD*)buff);
+                #endif
+                return RES_OK;
+            }
+
+            case GET_BLOCK_SIZE:
+                *((DWORD*)buff) = 1; // erase block size in units of sector size
+                return RES_OK;
+
+            case IOCTL_INIT:
+                *((DSTATUS*)buff) = disk_initialize(pdrv);
+                return RES_OK;
+
+            case IOCTL_STATUS:
+                *((DSTATUS*)buff) = disk_status(pdrv);
+                return RES_OK;
+
+            default:
+                return RES_PARERR;
+        }
+    } else {
+        // old protocol with sync and count
+        switch (cmd) {
+            case CTRL_SYNC:
+                if (vfs->u.old.sync[0] != MP_OBJ_NULL) {
+                    mp_call_method_n_kw(0, 0, vfs->u.old.sync);
+                }
+                return RES_OK;
+
+            case GET_SECTOR_COUNT: {
+                mp_obj_t ret = mp_call_method_n_kw(0, 0, vfs->u.old.count);
+                *((DWORD*)buff) = mp_obj_get_int(ret);
+                return RES_OK;
+            }
+
+            case GET_SECTOR_SIZE:
+                *((WORD*)buff) = 512; // old protocol had fixed sector size
+                #if _MAX_SS != _MIN_SS
+                // need to store ssize because we use it in disk_read/disk_write
+                vfs->fatfs.ssize = 512;
+                #endif
+                return RES_OK;
+
+            case GET_BLOCK_SIZE:
+                *((DWORD*)buff) = 1; // erase block size in units of sector size
+                return RES_OK;
+
+            case IOCTL_INIT:
+                *((DSTATUS*)buff) = disk_initialize(pdrv);
+                return RES_OK;
+
+            case IOCTL_STATUS:
+                *((DSTATUS*)buff) = disk_status(pdrv);
+                return RES_OK;
+
+            default:
+                return RES_PARERR;
+        }
+    }
+}
+
+#endif // MICROPY_VFS && MICROPY_VFS_FAT

+ 299 - 0
extmod/vfs_fat_file.c

@@ -0,0 +1,299 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/mpconfig.h"
+#if MICROPY_VFS && MICROPY_VFS_FAT
+
+#include <stdio.h>
+
+#include "py/runtime.h"
+#include "py/stream.h"
+#include "py/mperrno.h"
+#include "lib/oofatfs/ff.h"
+#include "extmod/vfs_fat.h"
+
+#define mp_type_fileio fatfs_type_fileio
+#define mp_type_textio fatfs_type_textio
+
+extern const mp_obj_type_t mp_type_fileio;
+extern const mp_obj_type_t mp_type_textio;
+
+// this table converts from FRESULT to POSIX errno
+const byte fresult_to_errno_table[20] = {
+    [FR_OK] = 0,
+    [FR_DISK_ERR] = MP_EIO,
+    [FR_INT_ERR] = MP_EIO,
+    [FR_NOT_READY] = MP_EBUSY,
+    [FR_NO_FILE] = MP_ENOENT,
+    [FR_NO_PATH] = MP_ENOENT,
+    [FR_INVALID_NAME] = MP_EINVAL,
+    [FR_DENIED] = MP_EACCES,
+    [FR_EXIST] = MP_EEXIST,
+    [FR_INVALID_OBJECT] = MP_EINVAL,
+    [FR_WRITE_PROTECTED] = MP_EROFS,
+    [FR_INVALID_DRIVE] = MP_ENODEV,
+    [FR_NOT_ENABLED] = MP_ENODEV,
+    [FR_NO_FILESYSTEM] = MP_ENODEV,
+    [FR_MKFS_ABORTED] = MP_EIO,
+    [FR_TIMEOUT] = MP_EIO,
+    [FR_LOCKED] = MP_EIO,
+    [FR_NOT_ENOUGH_CORE] = MP_ENOMEM,
+    [FR_TOO_MANY_OPEN_FILES] = MP_EMFILE,
+    [FR_INVALID_PARAMETER] = MP_EINVAL,
+};
+
+typedef struct _pyb_file_obj_t {
+    mp_obj_base_t base;
+    FIL fp;
+} pyb_file_obj_t;
+
+STATIC void file_obj_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
+    (void)kind;
+    mp_printf(print, "<io.%s %p>", mp_obj_get_type_str(self_in), MP_OBJ_TO_PTR(self_in));
+}
+
+STATIC mp_uint_t file_obj_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) {
+    pyb_file_obj_t *self = MP_OBJ_TO_PTR(self_in);
+    UINT sz_out;
+    FRESULT res = f_read(&self->fp, buf, size, &sz_out);
+    if (res != FR_OK) {
+        *errcode = fresult_to_errno_table[res];
+        return MP_STREAM_ERROR;
+    }
+    return sz_out;
+}
+
+STATIC mp_uint_t file_obj_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) {
+    pyb_file_obj_t *self = MP_OBJ_TO_PTR(self_in);
+    UINT sz_out;
+    FRESULT res = f_write(&self->fp, buf, size, &sz_out);
+    if (res != FR_OK) {
+        *errcode = fresult_to_errno_table[res];
+        return MP_STREAM_ERROR;
+    }
+    if (sz_out != size) {
+        // The FatFS documentation says that this means disk full.
+        *errcode = MP_ENOSPC;
+        return MP_STREAM_ERROR;
+    }
+    return sz_out;
+}
+
+
+STATIC mp_obj_t file_obj_close(mp_obj_t self_in) {
+    pyb_file_obj_t *self = MP_OBJ_TO_PTR(self_in);
+    // if fs==NULL then the file is closed and in that case this method is a no-op
+    if (self->fp.obj.fs != NULL) {
+        FRESULT res = f_close(&self->fp);
+        if (res != FR_OK) {
+            mp_raise_OSError(fresult_to_errno_table[res]);
+        }
+    }
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(file_obj_close_obj, file_obj_close);
+
+STATIC mp_obj_t file_obj___exit__(size_t n_args, const mp_obj_t *args) {
+    (void)n_args;
+    return file_obj_close(args[0]);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(file_obj___exit___obj, 4, 4, file_obj___exit__);
+
+STATIC mp_uint_t file_obj_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) {
+    pyb_file_obj_t *self = MP_OBJ_TO_PTR(o_in);
+
+    if (request == MP_STREAM_SEEK) {
+        struct mp_stream_seek_t *s = (struct mp_stream_seek_t*)(uintptr_t)arg;
+
+        switch (s->whence) {
+            case 0: // SEEK_SET
+                f_lseek(&self->fp, s->offset);
+                break;
+
+            case 1: // SEEK_CUR
+                if (s->offset != 0) {
+                    *errcode = MP_EOPNOTSUPP;
+                    return MP_STREAM_ERROR;
+                }
+                // no-operation
+                break;
+
+            case 2: // SEEK_END
+                f_lseek(&self->fp, f_size(&self->fp) + s->offset);
+                break;
+        }
+
+        s->offset = f_tell(&self->fp);
+        return 0;
+
+    } else if (request == MP_STREAM_FLUSH) {
+        FRESULT res = f_sync(&self->fp);
+        if (res != FR_OK) {
+            *errcode = fresult_to_errno_table[res];
+            return MP_STREAM_ERROR;
+        }
+        return 0;
+
+    } else {
+        *errcode = MP_EINVAL;
+        return MP_STREAM_ERROR;
+    }
+}
+
+// Note: encoding is ignored for now; it's also not a valid kwarg for CPython's FileIO,
+// but by adding it here we can use one single mp_arg_t array for open() and FileIO's constructor
+STATIC const mp_arg_t file_open_args[] = {
+    { MP_QSTR_file, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },
+    { MP_QSTR_mode, MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_QSTR(MP_QSTR_r)} },
+    { MP_QSTR_encoding, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },
+};
+#define FILE_OPEN_NUM_ARGS MP_ARRAY_SIZE(file_open_args)
+
+STATIC mp_obj_t file_open(fs_user_mount_t *vfs, const mp_obj_type_t *type, mp_arg_val_t *args) {
+    int mode = 0;
+    const char *mode_s = mp_obj_str_get_str(args[1].u_obj);
+    // TODO make sure only one of r, w, x, a, and b, t are specified
+    while (*mode_s) {
+        switch (*mode_s++) {
+            case 'r':
+                mode |= FA_READ;
+                break;
+            case 'w':
+                mode |= FA_WRITE | FA_CREATE_ALWAYS;
+                break;
+            case 'x':
+                mode |= FA_WRITE | FA_CREATE_NEW;
+                break;
+            case 'a':
+                mode |= FA_WRITE | FA_OPEN_ALWAYS;
+                break;
+            case '+':
+                mode |= FA_READ | FA_WRITE;
+                break;
+            #if MICROPY_PY_IO_FILEIO
+            case 'b':
+                type = &mp_type_fileio;
+                break;
+            #endif
+            case 't':
+                type = &mp_type_textio;
+                break;
+        }
+    }
+
+    pyb_file_obj_t *o = m_new_obj_with_finaliser(pyb_file_obj_t);
+    o->base.type = type;
+
+    const char *fname = mp_obj_str_get_str(args[0].u_obj);
+    assert(vfs != NULL);
+    FRESULT res = f_open(&vfs->fatfs, &o->fp, fname, mode);
+    if (res != FR_OK) {
+        m_del_obj(pyb_file_obj_t, o);
+        mp_raise_OSError(fresult_to_errno_table[res]);
+    }
+
+    // for 'a' mode, we must begin at the end of the file
+    if ((mode & FA_OPEN_ALWAYS) != 0) {
+        f_lseek(&o->fp, f_size(&o->fp));
+    }
+
+    return MP_OBJ_FROM_PTR(o);
+}
+
+STATIC mp_obj_t file_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+    mp_arg_val_t arg_vals[FILE_OPEN_NUM_ARGS];
+    mp_arg_parse_all_kw_array(n_args, n_kw, args, FILE_OPEN_NUM_ARGS, file_open_args, arg_vals);
+    return file_open(NULL, type, arg_vals);
+}
+
+// TODO gc hook to close the file if not already closed
+
+STATIC const mp_rom_map_elem_t rawfile_locals_dict_table[] = {
+    { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
+    { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
+    { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) },
+    { MP_ROM_QSTR(MP_QSTR_readlines), MP_ROM_PTR(&mp_stream_unbuffered_readlines_obj) },
+    { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
+    { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&mp_stream_flush_obj) },
+    { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&file_obj_close_obj) },
+    { MP_ROM_QSTR(MP_QSTR_seek), MP_ROM_PTR(&mp_stream_seek_obj) },
+    { MP_ROM_QSTR(MP_QSTR_tell), MP_ROM_PTR(&mp_stream_tell_obj) },
+    { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&file_obj_close_obj) },
+    { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) },
+    { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&file_obj___exit___obj) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(rawfile_locals_dict, rawfile_locals_dict_table);
+
+#if MICROPY_PY_IO_FILEIO
+STATIC const mp_stream_p_t fileio_stream_p = {
+    .read = file_obj_read,
+    .write = file_obj_write,
+    .ioctl = file_obj_ioctl,
+};
+
+const mp_obj_type_t mp_type_fileio = {
+    { &mp_type_type },
+    .name = MP_QSTR_FileIO,
+    .print = file_obj_print,
+    .make_new = file_obj_make_new,
+    .getiter = mp_identity_getiter,
+    .iternext = mp_stream_unbuffered_iter,
+    .protocol = &fileio_stream_p,
+    .locals_dict = (mp_obj_dict_t*)&rawfile_locals_dict,
+};
+#endif
+
+STATIC const mp_stream_p_t textio_stream_p = {
+    .read = file_obj_read,
+    .write = file_obj_write,
+    .ioctl = file_obj_ioctl,
+    .is_text = true,
+};
+
+const mp_obj_type_t mp_type_textio = {
+    { &mp_type_type },
+    .name = MP_QSTR_TextIOWrapper,
+    .print = file_obj_print,
+    .make_new = file_obj_make_new,
+    .getiter = mp_identity_getiter,
+    .iternext = mp_stream_unbuffered_iter,
+    .protocol = &textio_stream_p,
+    .locals_dict = (mp_obj_dict_t*)&rawfile_locals_dict,
+};
+
+// Factory function for I/O stream classes
+mp_obj_t fatfs_builtin_open_self(mp_obj_t self_in, mp_obj_t path, mp_obj_t mode) {
+    // TODO: analyze buffering args and instantiate appropriate type
+    fs_user_mount_t *self = MP_OBJ_TO_PTR(self_in);
+    mp_arg_val_t arg_vals[FILE_OPEN_NUM_ARGS];
+    arg_vals[0].u_obj = path;
+    arg_vals[1].u_obj = mode;
+    arg_vals[2].u_obj = mp_const_none;
+    return file_open(self, &mp_type_textio, arg_vals);
+}
+
+#endif // MICROPY_VFS && MICROPY_VFS_FAT

+ 108 - 0
extmod/vfs_fat_misc.c

@@ -0,0 +1,108 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/mpconfig.h"
+#if MICROPY_VFS_FAT
+
+#include <string.h>
+#include "py/runtime.h"
+#include "lib/oofatfs/ff.h"
+#include "extmod/vfs_fat.h"
+#include "py/lexer.h"
+
+typedef struct _mp_vfs_fat_ilistdir_it_t {
+    mp_obj_base_t base;
+    mp_fun_1_t iternext;
+    bool is_str;
+    FF_DIR dir;
+} mp_vfs_fat_ilistdir_it_t;
+
+STATIC mp_obj_t mp_vfs_fat_ilistdir_it_iternext(mp_obj_t self_in) {
+    mp_vfs_fat_ilistdir_it_t *self = MP_OBJ_TO_PTR(self_in);
+
+    for (;;) {
+        FILINFO fno;
+        FRESULT res = f_readdir(&self->dir, &fno);
+        char *fn = fno.fname;
+        if (res != FR_OK || fn[0] == 0) {
+            // stop on error or end of dir
+            break;
+        }
+
+        // Note that FatFS already filters . and .., so we don't need to
+
+        // make 3-tuple with info about this entry
+        mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL));
+        if (self->is_str) {
+            t->items[0] = mp_obj_new_str(fn, strlen(fn), false);
+        } else {
+            t->items[0] = mp_obj_new_bytes((const byte*)fn, strlen(fn));
+        }
+        if (fno.fattrib & AM_DIR) {
+            // dir
+            t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFDIR);
+        } else {
+            // file
+            t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFREG);
+        }
+        t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // no inode number
+
+        return MP_OBJ_FROM_PTR(t);
+    }
+
+    // ignore error because we may be closing a second time
+    f_closedir(&self->dir);
+
+    return MP_OBJ_STOP_ITERATION;
+}
+
+mp_obj_t fat_vfs_ilistdir2(fs_user_mount_t *vfs, const char *path, bool is_str_type) {
+    mp_vfs_fat_ilistdir_it_t *iter = m_new_obj(mp_vfs_fat_ilistdir_it_t);
+    iter->base.type = &mp_type_polymorph_iter;
+    iter->iternext = mp_vfs_fat_ilistdir_it_iternext;
+    iter->is_str = is_str_type;
+    FRESULT res = f_opendir(&vfs->fatfs, &iter->dir, path);
+    if (res != FR_OK) {
+        mp_raise_OSError(fresult_to_errno_table[res]);
+    }
+    return MP_OBJ_FROM_PTR(iter);
+}
+
+mp_import_stat_t fat_vfs_import_stat(fs_user_mount_t *vfs, const char *path) {
+    FILINFO fno;
+    assert(vfs != NULL);
+    FRESULT res = f_stat(&vfs->fatfs, path, &fno);
+    if (res == FR_OK) {
+        if ((fno.fattrib & AM_DIR) != 0) {
+            return MP_IMPORT_STAT_DIR;
+        } else {
+            return MP_IMPORT_STAT_FILE;
+        }
+    }
+    return MP_IMPORT_STAT_NO_EXIST;
+}
+
+#endif // MICROPY_VFS_FAT

+ 87 - 0
extmod/vfs_reader.c

@@ -0,0 +1,87 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013-2017 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "py/runtime.h"
+#include "py/stream.h"
+#include "py/reader.h"
+#include "extmod/vfs.h"
+
+#if MICROPY_READER_VFS
+
+typedef struct _mp_reader_vfs_t {
+    mp_obj_t file;
+    uint16_t len;
+    uint16_t pos;
+    byte buf[24];
+} mp_reader_vfs_t;
+
+STATIC mp_uint_t mp_reader_vfs_readbyte(void *data) {
+    mp_reader_vfs_t *reader = (mp_reader_vfs_t*)data;
+    if (reader->pos >= reader->len) {
+        if (reader->len < sizeof(reader->buf)) {
+            return MP_READER_EOF;
+        } else {
+            int errcode;
+            reader->len = mp_stream_rw(reader->file, reader->buf, sizeof(reader->buf),
+                &errcode, MP_STREAM_RW_READ | MP_STREAM_RW_ONCE);
+            if (errcode != 0) {
+                // TODO handle errors properly
+                return MP_READER_EOF;
+            }
+            if (reader->len == 0) {
+                return MP_READER_EOF;
+            }
+            reader->pos = 0;
+        }
+    }
+    return reader->buf[reader->pos++];
+}
+
+STATIC void mp_reader_vfs_close(void *data) {
+    mp_reader_vfs_t *reader = (mp_reader_vfs_t*)data;
+    mp_stream_close(reader->file);
+    m_del_obj(mp_reader_vfs_t, reader);
+}
+
+void mp_reader_new_file(mp_reader_t *reader, const char *filename) {
+    mp_reader_vfs_t *rf = m_new_obj(mp_reader_vfs_t);
+    mp_obj_t arg = mp_obj_new_str(filename, strlen(filename), false);
+    rf->file = mp_vfs_open(1, &arg, (mp_map_t*)&mp_const_empty_map);
+    int errcode;
+    rf->len = mp_stream_rw(rf->file, rf->buf, sizeof(rf->buf), &errcode, MP_STREAM_RW_READ | MP_STREAM_RW_ONCE);
+    if (errcode != 0) {
+        mp_raise_OSError(errcode);
+    }
+    rf->pos = 0;
+    reader->data = rf;
+    reader->readbyte = mp_reader_vfs_readbyte;
+    reader->close = mp_reader_vfs_close;
+}
+
+#endif // MICROPY_READER_VFS

+ 39 - 0
extmod/virtpin.c

@@ -0,0 +1,39 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Paul Sokolovsky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "extmod/virtpin.h"
+
+int mp_virtual_pin_read(mp_obj_t pin) {
+    mp_obj_base_t* s = (mp_obj_base_t*)MP_OBJ_TO_PTR(pin);
+    mp_pin_p_t *pin_p = (mp_pin_p_t*)s->type->protocol;
+    return pin_p->ioctl(pin, MP_PIN_READ, 0, NULL);
+}
+
+void mp_virtual_pin_write(mp_obj_t pin, int value) {
+    mp_obj_base_t* s = (mp_obj_base_t*)MP_OBJ_TO_PTR(pin);
+    mp_pin_p_t *pin_p = (mp_pin_p_t*)s->type->protocol;
+    pin_p->ioctl(pin, MP_PIN_WRITE, value, NULL);
+}

+ 47 - 0
extmod/virtpin.h

@@ -0,0 +1,47 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Paul Sokolovsky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_EXTMOD_VIRTPIN_H
+#define MICROPY_INCLUDED_EXTMOD_VIRTPIN_H
+
+#include "py/obj.h"
+
+#define MP_PIN_READ   (1)
+#define MP_PIN_WRITE  (2)
+#define MP_PIN_INPUT  (3)
+#define MP_PIN_OUTPUT (4)
+
+// Pin protocol
+typedef struct _mp_pin_p_t {
+    mp_uint_t (*ioctl)(mp_obj_t obj, mp_uint_t request, uintptr_t arg, int *errcode);
+} mp_pin_p_t;
+
+int mp_virtual_pin_read(mp_obj_t pin);
+void mp_virtual_pin_write(mp_obj_t pin, int value);
+
+// If a port exposes a Pin object, it's constructor should be like this
+mp_obj_t mp_pin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args);
+
+#endif // MICROPY_INCLUDED_EXTMOD_VIRTPIN_H

+ 447 - 0
lib/mp-readline/readline.c

@@ -0,0 +1,447 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "py/mpstate.h"
+#include "py/repl.h"
+#include "py/mphal.h"
+#include "lib/mp-readline/readline.h"
+
+#if 0 // print debugging info
+#define DEBUG_PRINT (1)
+#define DEBUG_printf printf
+#else // don't print debugging info
+#define DEBUG_printf(...) (void)0
+#endif
+
+#define READLINE_HIST_SIZE (MP_ARRAY_SIZE(MP_STATE_PORT(readline_hist)))
+
+enum { ESEQ_NONE, ESEQ_ESC, ESEQ_ESC_BRACKET, ESEQ_ESC_BRACKET_DIGIT, ESEQ_ESC_O };
+
+void readline_init0(void) {
+    memset(MP_STATE_PORT(readline_hist), 0, READLINE_HIST_SIZE * sizeof(const char*));
+}
+
+STATIC char *str_dup_maybe(const char *str) {
+    uint32_t len = strlen(str);
+    char *s2 = m_new_maybe(char, len + 1);
+    if (s2 == NULL) {
+        return NULL;
+    }
+    memcpy(s2, str, len + 1);
+    return s2;
+}
+
+// By default assume terminal which implements VT100 commands...
+#ifndef MICROPY_HAL_HAS_VT100
+#define MICROPY_HAL_HAS_VT100 (1)
+#endif
+
+// ...and provide the implementation using them
+#if MICROPY_HAL_HAS_VT100
+STATIC void mp_hal_move_cursor_back(uint pos) {
+    if (pos <= 4) {
+        // fast path for most common case of 1 step back
+        mp_hal_stdout_tx_strn("\b\b\b\b", pos);
+    } else {
+        char vt100_command[6];
+        // snprintf needs space for the terminating null character
+        int n = snprintf(&vt100_command[0], sizeof(vt100_command), "\x1b[%u", pos);
+        if (n > 0) {
+            vt100_command[n] = 'D'; // replace null char
+            mp_hal_stdout_tx_strn(vt100_command, n + 1);
+        }
+    }
+}
+
+STATIC void mp_hal_erase_line_from_cursor(uint n_chars_to_erase) {
+    (void)n_chars_to_erase;
+    mp_hal_stdout_tx_strn("\x1b[K", 3);
+}
+#endif
+
+typedef struct _readline_t {
+    vstr_t *line;
+    size_t orig_line_len;
+    int escape_seq;
+    int hist_cur;
+    size_t cursor_pos;
+    char escape_seq_buf[1];
+    const char *prompt;
+} readline_t;
+
+STATIC readline_t rl;
+
+int readline_process_char(int c) {
+    size_t last_line_len = rl.line->len;
+    int redraw_step_back = 0;
+    bool redraw_from_cursor = false;
+    int redraw_step_forward = 0;
+    if (rl.escape_seq == ESEQ_NONE) {
+        if (CHAR_CTRL_A <= c && c <= CHAR_CTRL_E && vstr_len(rl.line) == rl.orig_line_len) {
+            // control character with empty line
+            return c;
+        } else if (c == CHAR_CTRL_A) {
+            // CTRL-A with non-empty line is go-to-start-of-line
+            goto home_key;
+        #if MICROPY_REPL_EMACS_KEYS
+        } else if (c == CHAR_CTRL_B) {
+            // CTRL-B with non-empty line is go-back-one-char
+            goto left_arrow_key;
+        #endif
+        } else if (c == CHAR_CTRL_C) {
+            // CTRL-C with non-empty line is cancel
+            return c;
+        #if MICROPY_REPL_EMACS_KEYS
+        } else if (c == CHAR_CTRL_D) {
+            // CTRL-D with non-empty line is delete-at-cursor
+            goto delete_key;
+        #endif
+        } else if (c == CHAR_CTRL_E) {
+            // CTRL-E is go-to-end-of-line
+            goto end_key;
+        #if MICROPY_REPL_EMACS_KEYS
+        } else if (c == CHAR_CTRL_F) {
+            // CTRL-F with non-empty line is go-forward-one-char
+            goto right_arrow_key;
+        } else if (c == CHAR_CTRL_K) {
+            // CTRL-K is kill from cursor to end-of-line, inclusive
+            vstr_cut_tail_bytes(rl.line, last_line_len - rl.cursor_pos);
+            // set redraw parameters
+            redraw_from_cursor = true;
+        } else if (c == CHAR_CTRL_N) {
+            // CTRL-N is go to next line in history
+            goto down_arrow_key;
+        } else if (c == CHAR_CTRL_P) {
+            // CTRL-P is go to previous line in history
+            goto up_arrow_key;
+        } else if (c == CHAR_CTRL_U) {
+            // CTRL-U is kill from beginning-of-line up to cursor
+            vstr_cut_out_bytes(rl.line, rl.orig_line_len, rl.cursor_pos - rl.orig_line_len);
+            // set redraw parameters
+            redraw_step_back = rl.cursor_pos - rl.orig_line_len;
+            redraw_from_cursor = true;
+        #endif
+        } else if (c == '\r') {
+            // newline
+            mp_hal_stdout_tx_str("\r\n");
+            readline_push_history(vstr_null_terminated_str(rl.line) + rl.orig_line_len);
+            return 0;
+        } else if (c == 27) {
+            // escape sequence
+            rl.escape_seq = ESEQ_ESC;
+        } else if (c == 8 || c == 127) {
+            // backspace/delete
+            if (rl.cursor_pos > rl.orig_line_len) {
+                // work out how many chars to backspace
+                #if MICROPY_REPL_AUTO_INDENT
+                int nspace = 0;
+                for (size_t i = rl.orig_line_len; i < rl.cursor_pos; i++) {
+                    if (rl.line->buf[i] != ' ') {
+                        nspace = 0;
+                        break;
+                    }
+                    nspace += 1;
+                }
+                if (nspace < 4) {
+                    nspace = 1;
+                } else {
+                    nspace = 4;
+                }
+                #else
+                int nspace = 1;
+                #endif
+
+                // do the backspace
+                vstr_cut_out_bytes(rl.line, rl.cursor_pos - nspace, nspace);
+                // set redraw parameters
+                redraw_step_back = nspace;
+                redraw_from_cursor = true;
+            }
+        #if MICROPY_HELPER_REPL
+        } else if (c == 9) {
+            // tab magic
+            const char *compl_str;
+            size_t compl_len = mp_repl_autocomplete(rl.line->buf + rl.orig_line_len, rl.cursor_pos - rl.orig_line_len, &mp_plat_print, &compl_str);
+            if (compl_len == 0) {
+                // no match
+            } else if (compl_len == (size_t)(-1)) {
+                // many matches
+                mp_hal_stdout_tx_str(rl.prompt);
+                mp_hal_stdout_tx_strn(rl.line->buf + rl.orig_line_len, rl.cursor_pos - rl.orig_line_len);
+                redraw_from_cursor = true;
+            } else {
+                // one match
+                for (size_t i = 0; i < compl_len; ++i) {
+                    vstr_ins_byte(rl.line, rl.cursor_pos + i, *compl_str++);
+                }
+                // set redraw parameters
+                redraw_from_cursor = true;
+                redraw_step_forward = compl_len;
+            }
+        #endif
+        } else if (32 <= c && c <= 126) {
+            // printable character
+            vstr_ins_char(rl.line, rl.cursor_pos, c);
+            // set redraw parameters
+            redraw_from_cursor = true;
+            redraw_step_forward = 1;
+        }
+    } else if (rl.escape_seq == ESEQ_ESC) {
+        switch (c) {
+            case '[':
+                rl.escape_seq = ESEQ_ESC_BRACKET;
+                break;
+            case 'O':
+                rl.escape_seq = ESEQ_ESC_O;
+                break;
+            default:
+                DEBUG_printf("(ESC %d)", c);
+                rl.escape_seq = ESEQ_NONE;
+        }
+    } else if (rl.escape_seq == ESEQ_ESC_BRACKET) {
+        if ('0' <= c && c <= '9') {
+            rl.escape_seq = ESEQ_ESC_BRACKET_DIGIT;
+            rl.escape_seq_buf[0] = c;
+        } else {
+            rl.escape_seq = ESEQ_NONE;
+            if (c == 'A') {
+#if MICROPY_REPL_EMACS_KEYS
+up_arrow_key:
+#endif
+                // up arrow
+                if (rl.hist_cur + 1 < (int)READLINE_HIST_SIZE && MP_STATE_PORT(readline_hist)[rl.hist_cur + 1] != NULL) {
+                    // increase hist num
+                    rl.hist_cur += 1;
+                    // set line to history
+                    rl.line->len = rl.orig_line_len;
+                    vstr_add_str(rl.line, MP_STATE_PORT(readline_hist)[rl.hist_cur]);
+                    // set redraw parameters
+                    redraw_step_back = rl.cursor_pos - rl.orig_line_len;
+                    redraw_from_cursor = true;
+                    redraw_step_forward = rl.line->len - rl.orig_line_len;
+                }
+            } else if (c == 'B') {
+#if MICROPY_REPL_EMACS_KEYS
+down_arrow_key:
+#endif
+                // down arrow
+                if (rl.hist_cur >= 0) {
+                    // decrease hist num
+                    rl.hist_cur -= 1;
+                    // set line to history
+                    vstr_cut_tail_bytes(rl.line, rl.line->len - rl.orig_line_len);
+                    if (rl.hist_cur >= 0) {
+                        vstr_add_str(rl.line, MP_STATE_PORT(readline_hist)[rl.hist_cur]);
+                    }
+                    // set redraw parameters
+                    redraw_step_back = rl.cursor_pos - rl.orig_line_len;
+                    redraw_from_cursor = true;
+                    redraw_step_forward = rl.line->len - rl.orig_line_len;
+                }
+            } else if (c == 'C') {
+#if MICROPY_REPL_EMACS_KEYS
+right_arrow_key:
+#endif
+                // right arrow
+                if (rl.cursor_pos < rl.line->len) {
+                    redraw_step_forward = 1;
+                }
+            } else if (c == 'D') {
+#if MICROPY_REPL_EMACS_KEYS
+left_arrow_key:
+#endif
+                // left arrow
+                if (rl.cursor_pos > rl.orig_line_len) {
+                    redraw_step_back = 1;
+                }
+            } else if (c == 'H') {
+                // home
+                goto home_key;
+            } else if (c == 'F') {
+                // end
+                goto end_key;
+            } else {
+                DEBUG_printf("(ESC [ %d)", c);
+            }
+        }
+    } else if (rl.escape_seq == ESEQ_ESC_BRACKET_DIGIT) {
+        if (c == '~') {
+            if (rl.escape_seq_buf[0] == '1' || rl.escape_seq_buf[0] == '7') {
+home_key:
+                redraw_step_back = rl.cursor_pos - rl.orig_line_len;
+            } else if (rl.escape_seq_buf[0] == '4' || rl.escape_seq_buf[0] == '8') {
+end_key:
+                redraw_step_forward = rl.line->len - rl.cursor_pos;
+            } else if (rl.escape_seq_buf[0] == '3') {
+                // delete
+#if MICROPY_REPL_EMACS_KEYS
+delete_key:
+#endif
+                if (rl.cursor_pos < rl.line->len) {
+                    vstr_cut_out_bytes(rl.line, rl.cursor_pos, 1);
+                    redraw_from_cursor = true;
+                }
+            } else {
+                DEBUG_printf("(ESC [ %c %d)", rl.escape_seq_buf[0], c);
+            }
+        } else {
+            DEBUG_printf("(ESC [ %c %d)", rl.escape_seq_buf[0], c);
+        }
+        rl.escape_seq = ESEQ_NONE;
+    } else if (rl.escape_seq == ESEQ_ESC_O) {
+        switch (c) {
+            case 'H':
+                goto home_key;
+            case 'F':
+                goto end_key;
+            default:
+                DEBUG_printf("(ESC O %d)", c);
+                rl.escape_seq = ESEQ_NONE;
+        }
+    } else {
+        rl.escape_seq = ESEQ_NONE;
+    }
+
+    // redraw command prompt, efficiently
+    if (redraw_step_back > 0) {
+        mp_hal_move_cursor_back(redraw_step_back);
+        rl.cursor_pos -= redraw_step_back;
+    }
+    if (redraw_from_cursor) {
+        if (rl.line->len < last_line_len) {
+            // erase old chars
+            mp_hal_erase_line_from_cursor(last_line_len - rl.cursor_pos);
+        }
+        // draw new chars
+        mp_hal_stdout_tx_strn(rl.line->buf + rl.cursor_pos, rl.line->len - rl.cursor_pos);
+        // move cursor forward if needed (already moved forward by length of line, so move it back)
+        mp_hal_move_cursor_back(rl.line->len - (rl.cursor_pos + redraw_step_forward));
+        rl.cursor_pos += redraw_step_forward;
+    } else if (redraw_step_forward > 0) {
+        // draw over old chars to move cursor forwards
+        mp_hal_stdout_tx_strn(rl.line->buf + rl.cursor_pos, redraw_step_forward);
+        rl.cursor_pos += redraw_step_forward;
+    }
+
+    return -1;
+}
+
+#if MICROPY_REPL_AUTO_INDENT
+STATIC void readline_auto_indent(void) {
+    vstr_t *line = rl.line;
+    if (line->len > 1 && line->buf[line->len - 1] == '\n') {
+        int i;
+        for (i = line->len - 1; i > 0; i--) {
+            if (line->buf[i - 1] == '\n') {
+                break;
+            }
+        }
+        size_t j;
+        for (j = i; j < line->len; j++) {
+            if (line->buf[j] != ' ') {
+                break;
+            }
+        }
+        // i=start of line; j=first non-space
+        if (i > 0 && j + 1 == line->len) {
+            // previous line is not first line and is all spaces
+            for (size_t k = i - 1; k > 0; --k) {
+                if (line->buf[k - 1] == '\n') {
+                    // don't auto-indent if last 2 lines are all spaces
+                    return;
+                } else if (line->buf[k - 1] != ' ') {
+                    // 2nd previous line is not all spaces
+                    break;
+                }
+            }
+        }
+        int n = (j - i) / 4;
+        if (line->buf[line->len - 2] == ':') {
+            n += 1;
+        }
+        while (n-- > 0) {
+            vstr_add_strn(line, "    ", 4);
+            mp_hal_stdout_tx_strn("    ", 4);
+            rl.cursor_pos += 4;
+        }
+    }
+}
+#endif
+
+void readline_note_newline(const char *prompt) {
+    rl.orig_line_len = rl.line->len;
+    rl.cursor_pos = rl.orig_line_len;
+    rl.prompt = prompt;
+    mp_hal_stdout_tx_str(prompt);
+    #if MICROPY_REPL_AUTO_INDENT
+    readline_auto_indent();
+    #endif
+}
+
+void readline_init(vstr_t *line, const char *prompt) {
+    rl.line = line;
+    rl.orig_line_len = line->len;
+    rl.escape_seq = ESEQ_NONE;
+    rl.escape_seq_buf[0] = 0;
+    rl.hist_cur = -1;
+    rl.cursor_pos = rl.orig_line_len;
+    rl.prompt = prompt;
+    mp_hal_stdout_tx_str(prompt);
+    #if MICROPY_REPL_AUTO_INDENT
+    readline_auto_indent();
+    #endif
+}
+
+int readline(vstr_t *line, const char *prompt) {
+    readline_init(line, prompt);
+    for (;;) {
+        int c = mp_hal_stdin_rx_chr();
+        int r = readline_process_char(c);
+        if (r >= 0) {
+            return r;
+        }
+    }
+}
+
+void readline_push_history(const char *line) {
+    if (line[0] != '\0'
+        && (MP_STATE_PORT(readline_hist)[0] == NULL
+            || strcmp(MP_STATE_PORT(readline_hist)[0], line) != 0)) {
+        // a line which is not empty and different from the last one
+        // so update the history
+        char *most_recent_hist = str_dup_maybe(line);
+        if (most_recent_hist != NULL) {
+            for (int i = READLINE_HIST_SIZE - 1; i > 0; i--) {
+                MP_STATE_PORT(readline_hist)[i] = MP_STATE_PORT(readline_hist)[i - 1];
+            }
+            MP_STATE_PORT(readline_hist)[0] = most_recent_hist;
+        }
+    }
+}

+ 48 - 0
lib/mp-readline/readline.h

@@ -0,0 +1,48 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_LIB_MP_READLINE_READLINE_H
+#define MICROPY_INCLUDED_LIB_MP_READLINE_READLINE_H
+
+#define CHAR_CTRL_A (1)
+#define CHAR_CTRL_B (2)
+#define CHAR_CTRL_C (3)
+#define CHAR_CTRL_D (4)
+#define CHAR_CTRL_E (5)
+#define CHAR_CTRL_F (6)
+#define CHAR_CTRL_K (11)
+#define CHAR_CTRL_N (14)
+#define CHAR_CTRL_P (16)
+#define CHAR_CTRL_U (21)
+
+void readline_init0(void);
+int readline(vstr_t *line, const char *prompt);
+void readline_push_history(const char *line);
+
+void readline_init(vstr_t *line, const char *prompt);
+void readline_note_newline(const char *prompt);
+int readline_process_char(int c);
+
+#endif // MICROPY_INCLUDED_LIB_MP_READLINE_READLINE_H

+ 50 - 0
lib/utils/interrupt_char.c

@@ -0,0 +1,50 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013-2016 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/obj.h"
+#include "py/mpstate.h"
+
+#if MICROPY_KBD_EXCEPTION
+
+int mp_interrupt_char;
+
+void mp_hal_set_interrupt_char(int c) {
+    if (c != -1) {
+        mp_obj_exception_clear_traceback(MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception)));
+    }
+    mp_interrupt_char = c;
+}
+
+void mp_keyboard_interrupt(void) {
+    MP_STATE_VM(mp_pending_exception) = MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception));
+    #if MICROPY_ENABLE_SCHEDULER
+    if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE) {
+        MP_STATE_VM(sched_state) = MP_SCHED_PENDING;
+    }
+    #endif
+}
+
+#endif

+ 33 - 0
lib/utils/interrupt_char.h

@@ -0,0 +1,33 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013-2016 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_LIB_UTILS_INTERRUPT_CHAR_H
+#define MICROPY_INCLUDED_LIB_UTILS_INTERRUPT_CHAR_H
+
+extern int mp_interrupt_char;
+void mp_hal_set_interrupt_char(int c);
+void mp_keyboard_interrupt(void);
+
+#endif // MICROPY_INCLUDED_LIB_UTILS_INTERRUPT_CHAR_H

+ 135 - 0
lib/utils/printf.c

@@ -0,0 +1,135 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/mpconfig.h"
+
+#if MICROPY_USE_INTERNAL_PRINTF
+
+#include <stdint.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "py/obj.h"
+#include "py/mphal.h"
+
+#if MICROPY_PY_BUILTINS_FLOAT
+#include "py/formatfloat.h"
+#endif
+
+#undef putchar  // Some stdlibs have a #define for putchar
+int printf(const char *fmt, ...);
+int vprintf(const char *fmt, va_list ap);
+int putchar(int c);
+int puts(const char *s);
+int vsnprintf(char *str, size_t size, const char *fmt, va_list ap);
+int snprintf(char *str, size_t size, const char *fmt, ...);
+
+int printf(const char *fmt, ...) {
+    va_list ap;
+    va_start(ap, fmt);
+    int ret = mp_vprintf(&mp_plat_print, fmt, ap);
+    va_end(ap);
+    return ret;
+}
+
+int vprintf(const char *fmt, va_list ap) {
+    return mp_vprintf(&mp_plat_print, fmt, ap);
+}
+
+#if MICROPY_DEBUG_PRINTERS
+int DEBUG_printf(const char *fmt, ...) {
+    va_list ap;
+    va_start(ap, fmt);
+    #ifndef MICROPY_DEBUG_PRINTER_DEST
+    #define MICROPY_DEBUG_PRINTER_DEST mp_plat_print
+    #endif
+    extern const mp_print_t MICROPY_DEBUG_PRINTER_DEST;
+    int ret = mp_vprintf(&MICROPY_DEBUG_PRINTER_DEST, fmt, ap);
+    va_end(ap);
+    return ret;
+}
+#endif
+
+// need this because gcc optimises printf("%c", c) -> putchar(c), and printf("a") -> putchar('a')
+int putchar(int c) {
+    char chr = c;
+    mp_hal_stdout_tx_strn_cooked(&chr, 1);
+    return chr;
+}
+
+// need this because gcc optimises printf("string\n") -> puts("string")
+int puts(const char *s) {
+    mp_hal_stdout_tx_strn_cooked(s, strlen(s));
+    char chr = '\n';
+    mp_hal_stdout_tx_strn_cooked(&chr, 1);
+    return 1;
+}
+
+typedef struct _strn_print_env_t {
+    char *cur;
+    size_t remain;
+} strn_print_env_t;
+
+STATIC void strn_print_strn(void *data, const char *str, size_t len) {
+    strn_print_env_t *strn_print_env = data;
+    if (len > strn_print_env->remain) {
+        len = strn_print_env->remain;
+    }
+    memcpy(strn_print_env->cur, str, len);
+    strn_print_env->cur += len;
+    strn_print_env->remain -= len;
+}
+
+#if defined(__GNUC__) && !defined(__clang__)
+// uClibc requires this alias to be defined, or there may be link errors
+// when linkings against it statically.
+int __GI_vsnprintf(char *str, size_t size, const char *fmt, va_list ap) __attribute__((weak, alias ("vsnprintf")));
+#endif
+
+int vsnprintf(char *str, size_t size, const char *fmt, va_list ap) {
+    strn_print_env_t strn_print_env = {str, size};
+    mp_print_t print = {&strn_print_env, strn_print_strn};
+    int len = mp_vprintf(&print, fmt, ap);
+    // add terminating null byte
+    if (size > 0) {
+        if (strn_print_env.remain == 0) {
+            strn_print_env.cur[-1] = 0;
+        } else {
+            strn_print_env.cur[0] = 0;
+        }
+    }
+    return len;
+}
+
+int snprintf(char *str, size_t size, const char *fmt, ...) {
+    va_list ap;
+    va_start(ap, fmt);
+    int ret = vsnprintf(str, size, fmt, ap);
+    va_end(ap);
+    return ret;
+}
+
+#endif //MICROPY_USE_INTERNAL_PRINTF

+ 530 - 0
lib/utils/pyexec.c

@@ -0,0 +1,530 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "py/compile.h"
+#include "py/runtime.h"
+#include "py/repl.h"
+#include "py/gc.h"
+#include "py/frozenmod.h"
+#include "py/mphal.h"
+#if defined(USE_DEVICE_MODE)
+#include "irq.h"
+#include "usb.h"
+#endif
+#include "lib/mp-readline/readline.h"
+#include "lib/utils/pyexec.h"
+#include "genhdr/mpversion.h"
+
+pyexec_mode_kind_t pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL;
+int pyexec_system_exit = 0;
+STATIC bool repl_display_debugging_info = 0;
+
+#define EXEC_FLAG_PRINT_EOF (1)
+#define EXEC_FLAG_ALLOW_DEBUGGING (2)
+#define EXEC_FLAG_IS_REPL (4)
+#define EXEC_FLAG_SOURCE_IS_RAW_CODE (8)
+#define EXEC_FLAG_SOURCE_IS_VSTR (16)
+#define EXEC_FLAG_SOURCE_IS_FILENAME (32)
+
+// parses, compiles and executes the code in the lexer
+// frees the lexer before returning
+// EXEC_FLAG_PRINT_EOF prints 2 EOF chars: 1 after normal output, 1 after exception output
+// EXEC_FLAG_ALLOW_DEBUGGING allows debugging info to be printed after executing the code
+// EXEC_FLAG_IS_REPL is used for REPL inputs (flag passed on to mp_compile)
+STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input_kind, int exec_flags) {
+    int ret = 0;
+    uint32_t start = 0;
+
+    // by default a SystemExit exception returns 0
+    pyexec_system_exit = 0;
+
+    nlr_buf_t nlr;
+    if (nlr_push(&nlr) == 0) {
+        mp_obj_t module_fun;
+        #if MICROPY_MODULE_FROZEN_MPY
+        if (exec_flags & EXEC_FLAG_SOURCE_IS_RAW_CODE) {
+            // source is a raw_code object, create the function
+            module_fun = mp_make_function_from_raw_code(source, MP_OBJ_NULL, MP_OBJ_NULL);
+        } else
+        #endif
+        {
+            #if MICROPY_ENABLE_COMPILER
+            mp_lexer_t *lex;
+            if (exec_flags & EXEC_FLAG_SOURCE_IS_VSTR) {
+                const vstr_t *vstr = source;
+                lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, vstr->buf, vstr->len, 0);
+            } else if (exec_flags & EXEC_FLAG_SOURCE_IS_FILENAME) {
+                lex = mp_lexer_new_from_file(source);
+            } else {
+                lex = (mp_lexer_t*)source;
+            }
+            // source is a lexer, parse and compile the script
+            qstr source_name = lex->source_name;
+            mp_parse_tree_t parse_tree = mp_parse(lex, input_kind);
+            module_fun = mp_compile(&parse_tree, source_name, MP_EMIT_OPT_NONE, exec_flags & EXEC_FLAG_IS_REPL);
+            #else
+            mp_raise_msg(&mp_type_RuntimeError, "script compilation not supported");
+            #endif
+        }
+
+        // execute code
+        mp_hal_set_interrupt_char(CHAR_CTRL_C); // allow ctrl-C to interrupt us
+        start = mp_hal_ticks_ms();
+        mp_call_function_0(module_fun);
+        mp_hal_set_interrupt_char(-1); // disable interrupt
+        nlr_pop();
+        ret = 1;
+        if (exec_flags & EXEC_FLAG_PRINT_EOF) {
+            mp_hal_stdout_tx_strn("\x04", 1);
+        }
+    } else {
+        // uncaught exception
+        // FIXME it could be that an interrupt happens just before we disable it here
+        mp_hal_set_interrupt_char(-1); // disable interrupt
+        // print EOF after normal output
+        if (exec_flags & EXEC_FLAG_PRINT_EOF) {
+            mp_hal_stdout_tx_strn("\x04", 1);
+        }
+        // check for SystemExit
+        if (mp_obj_is_subclass_fast(mp_obj_get_type((mp_obj_t)nlr.ret_val), &mp_type_SystemExit)) {
+            // at the moment, the value of SystemExit is unused
+            ret = pyexec_system_exit;
+        } else {
+            mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val);
+            ret = 0;
+        }
+    }
+
+    // display debugging info if wanted
+    if ((exec_flags & EXEC_FLAG_ALLOW_DEBUGGING) && repl_display_debugging_info) {
+        mp_uint_t ticks = mp_hal_ticks_ms() - start; // TODO implement a function that does this properly
+        printf("took " UINT_FMT " ms\n", ticks);
+        // qstr info
+        {
+            size_t n_pool, n_qstr, n_str_data_bytes, n_total_bytes;
+            qstr_pool_info(&n_pool, &n_qstr, &n_str_data_bytes, &n_total_bytes);
+            printf("qstr:\n  n_pool=" UINT_FMT "\n  n_qstr=" UINT_FMT "\n  "
+                   "n_str_data_bytes=" UINT_FMT "\n  n_total_bytes=" UINT_FMT "\n",
+                   (unsigned)n_pool, (unsigned)n_qstr, (unsigned)n_str_data_bytes, (unsigned)n_total_bytes);
+        }
+
+        #if MICROPY_ENABLE_GC
+        // run collection and print GC info
+        gc_collect();
+        gc_dump_info();
+        #endif
+    }
+
+    if (exec_flags & EXEC_FLAG_PRINT_EOF) {
+        mp_hal_stdout_tx_strn("\x04", 1);
+    }
+
+    return ret;
+}
+
+#if MICROPY_ENABLE_COMPILER
+#if MICROPY_REPL_EVENT_DRIVEN
+
+typedef struct _repl_t {
+    // This structure originally also held current REPL line,
+    // but it was moved to MP_STATE_VM(repl_line) as containing
+    // root pointer. Still keep structure in case more state
+    // will be added later.
+    //vstr_t line;
+    bool cont_line;
+} repl_t;
+
+repl_t repl;
+
+STATIC int pyexec_raw_repl_process_char(int c);
+STATIC int pyexec_friendly_repl_process_char(int c);
+
+void pyexec_event_repl_init(void) {
+    MP_STATE_VM(repl_line) = vstr_new(32);
+    repl.cont_line = false;
+    // no prompt before printing friendly REPL banner or entering raw REPL
+    readline_init(MP_STATE_VM(repl_line), "");
+    if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) {
+        pyexec_raw_repl_process_char(CHAR_CTRL_A);
+    } else {
+        pyexec_friendly_repl_process_char(CHAR_CTRL_B);
+    }
+}
+
+STATIC int pyexec_raw_repl_process_char(int c) {
+    if (c == CHAR_CTRL_A) {
+        // reset raw REPL
+        mp_hal_stdout_tx_str("raw REPL; CTRL-B to exit\r\n");
+        goto reset;
+    } else if (c == CHAR_CTRL_B) {
+        // change to friendly REPL
+        pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL;
+        vstr_reset(MP_STATE_VM(repl_line));
+        repl.cont_line = false;
+        pyexec_friendly_repl_process_char(CHAR_CTRL_B);
+        return 0;
+    } else if (c == CHAR_CTRL_C) {
+        // clear line
+        vstr_reset(MP_STATE_VM(repl_line));
+        return 0;
+    } else if (c == CHAR_CTRL_D) {
+        // input finished
+    } else {
+        // let through any other raw 8-bit value
+        vstr_add_byte(MP_STATE_VM(repl_line), c);
+        return 0;
+    }
+
+    // indicate reception of command
+    mp_hal_stdout_tx_str("OK");
+
+    if (MP_STATE_VM(repl_line)->len == 0) {
+        // exit for a soft reset
+        mp_hal_stdout_tx_str("\r\n");
+        vstr_clear(MP_STATE_VM(repl_line));
+        return PYEXEC_FORCED_EXIT;
+    }
+
+    int ret = parse_compile_execute(MP_STATE_VM(repl_line), MP_PARSE_FILE_INPUT, EXEC_FLAG_PRINT_EOF | EXEC_FLAG_SOURCE_IS_VSTR);
+    if (ret & PYEXEC_FORCED_EXIT) {
+        return ret;
+    }
+
+reset:
+    vstr_reset(MP_STATE_VM(repl_line));
+    mp_hal_stdout_tx_str(">");
+
+    return 0;
+}
+
+STATIC int pyexec_friendly_repl_process_char(int c) {
+    int ret = readline_process_char(c);
+
+    if (!repl.cont_line) {
+
+        if (ret == CHAR_CTRL_A) {
+            // change to raw REPL
+            pyexec_mode_kind = PYEXEC_MODE_RAW_REPL;
+            mp_hal_stdout_tx_str("\r\n");
+            pyexec_raw_repl_process_char(CHAR_CTRL_A);
+            return 0;
+        } else if (ret == CHAR_CTRL_B) {
+            // reset friendly REPL
+            mp_hal_stdout_tx_str("\r\n");
+            mp_hal_stdout_tx_str("MicroPython " MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE "; " MICROPY_HW_BOARD_NAME " with " MICROPY_HW_MCU_NAME "\r\n");
+            #if MICROPY_PY_BUILTINS_HELP
+            mp_hal_stdout_tx_str("Type \"help()\" for more information.\r\n");
+            #endif
+            goto input_restart;
+        } else if (ret == CHAR_CTRL_C) {
+            // break
+            mp_hal_stdout_tx_str("\r\n");
+            goto input_restart;
+        } else if (ret == CHAR_CTRL_D) {
+            // exit for a soft reset
+            mp_hal_stdout_tx_str("\r\n");
+            vstr_clear(MP_STATE_VM(repl_line));
+            return PYEXEC_FORCED_EXIT;
+        }
+
+        if (ret < 0) {
+            return 0;
+        }
+
+        if (!mp_repl_continue_with_input(vstr_null_terminated_str(MP_STATE_VM(repl_line)))) {
+            goto exec;
+        }
+
+        vstr_add_byte(MP_STATE_VM(repl_line), '\n');
+        repl.cont_line = true;
+        readline_note_newline("... ");
+        return 0;
+
+    } else {
+
+        if (ret == CHAR_CTRL_C) {
+           // cancel everything
+           mp_hal_stdout_tx_str("\r\n");
+           repl.cont_line = false;
+           goto input_restart;
+        } else if (ret == CHAR_CTRL_D) {
+            // stop entering compound statement
+            goto exec;
+        }
+
+        if (ret < 0) {
+            return 0;
+        }
+
+        if (mp_repl_continue_with_input(vstr_null_terminated_str(MP_STATE_VM(repl_line)))) {
+            vstr_add_byte(MP_STATE_VM(repl_line), '\n');
+            readline_note_newline("... ");
+            return 0;
+        }
+
+exec: ;
+        int ret = parse_compile_execute(MP_STATE_VM(repl_line), MP_PARSE_SINGLE_INPUT, EXEC_FLAG_ALLOW_DEBUGGING | EXEC_FLAG_IS_REPL | EXEC_FLAG_SOURCE_IS_VSTR);
+        if (ret & PYEXEC_FORCED_EXIT) {
+            return ret;
+        }
+
+input_restart:
+        vstr_reset(MP_STATE_VM(repl_line));
+        repl.cont_line = false;
+        readline_init(MP_STATE_VM(repl_line), ">>> ");
+        return 0;
+    }
+}
+
+uint8_t pyexec_repl_active;
+int pyexec_event_repl_process_char(int c) {
+    pyexec_repl_active = 1;
+    int res;
+    if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) {
+        res = pyexec_raw_repl_process_char(c);
+    } else {
+        res = pyexec_friendly_repl_process_char(c);
+    }
+    pyexec_repl_active = 0;
+    return res;
+}
+
+#else // MICROPY_REPL_EVENT_DRIVEN
+
+int pyexec_raw_repl(void) {
+    vstr_t line;
+    vstr_init(&line, 32);
+
+raw_repl_reset:
+    mp_hal_stdout_tx_str("raw REPL; CTRL-B to exit\r\n");
+
+    for (;;) {
+        vstr_reset(&line);
+        mp_hal_stdout_tx_str(">");
+        for (;;) {
+            int c = mp_hal_stdin_rx_chr();
+            if (c == CHAR_CTRL_A) {
+                // reset raw REPL
+                goto raw_repl_reset;
+            } else if (c == CHAR_CTRL_B) {
+                // change to friendly REPL
+                mp_hal_stdout_tx_str("\r\n");
+                vstr_clear(&line);
+                pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL;
+                return 0;
+            } else if (c == CHAR_CTRL_C) {
+                // clear line
+                vstr_reset(&line);
+            } else if (c == CHAR_CTRL_D) {
+                // input finished
+                break;
+            } else {
+                // let through any other raw 8-bit value
+                vstr_add_byte(&line, c);
+            }
+        }
+
+        // indicate reception of command
+        mp_hal_stdout_tx_str("OK");
+
+        if (line.len == 0) {
+            // exit for a soft reset
+            mp_hal_stdout_tx_str("\r\n");
+            vstr_clear(&line);
+            return PYEXEC_FORCED_EXIT;
+        }
+
+        int ret = parse_compile_execute(&line, MP_PARSE_FILE_INPUT, EXEC_FLAG_PRINT_EOF | EXEC_FLAG_SOURCE_IS_VSTR);
+        if (ret & PYEXEC_FORCED_EXIT) {
+            return ret;
+        }
+    }
+}
+
+int pyexec_friendly_repl(void) {
+    vstr_t line;
+    vstr_init(&line, 32);
+
+#if defined(USE_HOST_MODE) && MICROPY_HW_HAS_LCD
+    // in host mode, we enable the LCD for the repl
+    mp_obj_t lcd_o = mp_call_function_0(mp_load_name(qstr_from_str("LCD")));
+    mp_call_function_1(mp_load_attr(lcd_o, qstr_from_str("light")), mp_const_true);
+#endif
+
+friendly_repl_reset:
+    mp_hal_stdout_tx_str("MicroPython " MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE "; " MICROPY_HW_BOARD_NAME " with " MICROPY_HW_MCU_NAME "\r\n");
+    #if MICROPY_PY_BUILTINS_HELP
+    mp_hal_stdout_tx_str("Type \"help()\" for more information.\r\n");
+    #endif
+
+    // to test ctrl-C
+    /*
+    {
+        uint32_t x[4] = {0x424242, 0xdeaddead, 0x242424, 0xdeadbeef};
+        for (;;) {
+            nlr_buf_t nlr;
+            printf("pyexec_repl: %p\n", x);
+            mp_hal_set_interrupt_char(CHAR_CTRL_C);
+            if (nlr_push(&nlr) == 0) {
+                for (;;) {
+                }
+            } else {
+                printf("break\n");
+            }
+        }
+    }
+    */
+
+    for (;;) {
+    input_restart:
+
+        #if defined(USE_DEVICE_MODE)
+        if (usb_vcp_is_enabled()) {
+            // If the user gets to here and interrupts are disabled then
+            // they'll never see the prompt, traceback etc. The USB REPL needs
+            // interrupts to be enabled or no transfers occur. So we try to
+            // do the user a favor and reenable interrupts.
+            if (query_irq() == IRQ_STATE_DISABLED) {
+                enable_irq(IRQ_STATE_ENABLED);
+                mp_hal_stdout_tx_str("PYB: enabling IRQs\r\n");
+            }
+        }
+        #endif
+
+        vstr_reset(&line);
+        int ret = readline(&line, ">>> ");
+        mp_parse_input_kind_t parse_input_kind = MP_PARSE_SINGLE_INPUT;
+
+        if (ret == CHAR_CTRL_A) {
+            // change to raw REPL
+            mp_hal_stdout_tx_str("\r\n");
+            vstr_clear(&line);
+            pyexec_mode_kind = PYEXEC_MODE_RAW_REPL;
+            return 0;
+        } else if (ret == CHAR_CTRL_B) {
+            // reset friendly REPL
+            mp_hal_stdout_tx_str("\r\n");
+            goto friendly_repl_reset;
+        } else if (ret == CHAR_CTRL_C) {
+            // break
+            mp_hal_stdout_tx_str("\r\n");
+            continue;
+        } else if (ret == CHAR_CTRL_D) {
+            // exit for a soft reset
+            mp_hal_stdout_tx_str("\r\n");
+            vstr_clear(&line);
+            return PYEXEC_FORCED_EXIT;
+        } else if (ret == CHAR_CTRL_E) {
+            // paste mode
+            mp_hal_stdout_tx_str("\r\npaste mode; Ctrl-C to cancel, Ctrl-D to finish\r\n=== ");
+            vstr_reset(&line);
+            for (;;) {
+                char c = mp_hal_stdin_rx_chr();
+                if (c == CHAR_CTRL_C) {
+                    // cancel everything
+                    mp_hal_stdout_tx_str("\r\n");
+                    goto input_restart;
+                } else if (c == CHAR_CTRL_D) {
+                    // end of input
+                    mp_hal_stdout_tx_str("\r\n");
+                    break;
+                } else {
+                    // add char to buffer and echo
+                    vstr_add_byte(&line, c);
+                    if (c == '\r') {
+                        mp_hal_stdout_tx_str("\r\n=== ");
+                    } else {
+                        mp_hal_stdout_tx_strn(&c, 1);
+                    }
+                }
+            }
+            parse_input_kind = MP_PARSE_FILE_INPUT;
+        } else if (vstr_len(&line) == 0) {
+            continue;
+        } else {
+            // got a line with non-zero length, see if it needs continuing
+            while (mp_repl_continue_with_input(vstr_null_terminated_str(&line))) {
+                vstr_add_byte(&line, '\n');
+                ret = readline(&line, "... ");
+                if (ret == CHAR_CTRL_C) {
+                    // cancel everything
+                    mp_hal_stdout_tx_str("\r\n");
+                    goto input_restart;
+                } else if (ret == CHAR_CTRL_D) {
+                    // stop entering compound statement
+                    break;
+                }
+            }
+        }
+
+        ret = parse_compile_execute(&line, parse_input_kind, EXEC_FLAG_ALLOW_DEBUGGING | EXEC_FLAG_IS_REPL | EXEC_FLAG_SOURCE_IS_VSTR);
+        if (ret & PYEXEC_FORCED_EXIT) {
+            return ret;
+        }
+    }
+}
+
+#endif // MICROPY_REPL_EVENT_DRIVEN
+#endif // MICROPY_ENABLE_COMPILER
+
+int pyexec_file(const char *filename) {
+    return parse_compile_execute(filename, MP_PARSE_FILE_INPUT, EXEC_FLAG_SOURCE_IS_FILENAME);
+}
+
+#if MICROPY_MODULE_FROZEN
+int pyexec_frozen_module(const char *name) {
+    void *frozen_data;
+    int frozen_type = mp_find_frozen_module(name, strlen(name), &frozen_data);
+
+    switch (frozen_type) {
+        #if MICROPY_MODULE_FROZEN_STR
+        case MP_FROZEN_STR:
+            return parse_compile_execute(frozen_data, MP_PARSE_FILE_INPUT, 0);
+        #endif
+
+        #if MICROPY_MODULE_FROZEN_MPY
+        case MP_FROZEN_MPY:
+            return parse_compile_execute(frozen_data, MP_PARSE_FILE_INPUT, EXEC_FLAG_SOURCE_IS_RAW_CODE);
+        #endif
+
+        default:
+            printf("could not find module '%s'\n", name);
+            return false;
+    }
+}
+#endif
+
+mp_obj_t pyb_set_repl_info(mp_obj_t o_value) {
+    repl_display_debugging_info = mp_obj_get_int(o_value);
+    return mp_const_none;
+}
+
+MP_DEFINE_CONST_FUN_OBJ_1(pyb_set_repl_info_obj, pyb_set_repl_info);

+ 54 - 0
lib/utils/pyexec.h

@@ -0,0 +1,54 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_LIB_UTILS_PYEXEC_H
+#define MICROPY_INCLUDED_LIB_UTILS_PYEXEC_H
+
+typedef enum {
+    PYEXEC_MODE_RAW_REPL,
+    PYEXEC_MODE_FRIENDLY_REPL,
+} pyexec_mode_kind_t;
+
+extern pyexec_mode_kind_t pyexec_mode_kind;
+
+// Set this to the value (eg PYEXEC_FORCED_EXIT) that will be propagated through
+// the pyexec functions if a SystemExit exception is raised by the running code.
+// It will reset to 0 at the start of each execution (eg each REPL entry).
+extern int pyexec_system_exit;
+
+#define PYEXEC_FORCED_EXIT (0x100)
+#define PYEXEC_SWITCH_MODE (0x200)
+
+int pyexec_raw_repl(void);
+int pyexec_friendly_repl(void);
+int pyexec_file(const char *filename);
+int pyexec_frozen_module(const char *name);
+void pyexec_event_repl_init(void);
+int pyexec_event_repl_process_char(int c);
+extern uint8_t pyexec_repl_active;
+
+MP_DECLARE_CONST_FUN_OBJ_1(pyb_set_repl_info_obj);
+
+#endif // MICROPY_INCLUDED_LIB_UTILS_PYEXEC_H

+ 26 - 0
lib/utils/stdout_helpers.c

@@ -0,0 +1,26 @@
+#include <string.h>
+#include <unistd.h>
+#include "py/mpconfig.h"
+#include "py/mphal.h"
+
+/*
+ * Extra stdout functions
+ * These can be either optimized for a particular port, or reference
+ * implementation below can be used.
+ */
+
+// Send "cooked" string of given length, where every occurrence of
+// LF character is replaced with CR LF.
+void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len) {
+    while (len--) {
+        if (*str == '\n') {
+            mp_hal_stdout_tx_strn("\r", 1);
+        }
+        mp_hal_stdout_tx_strn(str++, 1);
+    }
+}
+
+// Send zero-terminated string
+void mp_hal_stdout_tx_str(const char *str) {
+    mp_hal_stdout_tx_strn(str, strlen(str));
+}

+ 120 - 0
port/_frozen_mpy.c

@@ -0,0 +1,120 @@
+#include "py/mpconfig.h"
+#include "py/objint.h"
+#include "py/objstr.h"
+#include "py/emitglue.h"
+
+#if MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE != 0
+#error "incompatible MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE"
+#endif
+
+#if MICROPY_LONGINT_IMPL != 2
+#error "incompatible MICROPY_LONGINT_IMPL"
+#endif
+
+#if MICROPY_PY_BUILTINS_FLOAT
+typedef struct _mp_obj_float_t {
+    mp_obj_base_t base;
+    mp_float_t value;
+} mp_obj_float_t;
+#endif
+
+#if MICROPY_PY_BUILTINS_COMPLEX
+typedef struct _mp_obj_complex_t {
+    mp_obj_base_t base;
+    mp_float_t real;
+    mp_float_t imag;
+} mp_obj_complex_t;
+#endif
+
+enum {
+    MP_QSTR_frozentest_dot_py = MP_QSTRnumber_of,
+    MP_QSTR_uPy,
+    MP_QSTR_i,
+};
+
+extern const qstr_pool_t mp_qstr_const_pool;
+const qstr_pool_t mp_qstr_frozen_const_pool = {
+    (qstr_pool_t*)&mp_qstr_const_pool, // previous pool
+    MP_QSTRnumber_of, // previous pool size
+    3, // allocated entries
+    3, // used entries
+    {
+        (const byte*)"\xfe\x0d" "frozentest.py",
+        (const byte*)"\xf9\x03" "uPy",
+        (const byte*)"\xcc\x01" "i",
+    },
+};
+
+// frozen bytecode for file frozentest.py, scope frozentest_<module>
+STATIC const byte bytecode_data_frozentest__lt_module_gt_[92] = {
+    0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d,
+    MP_QSTR__lt_module_gt_ & 0xff, MP_QSTR__lt_module_gt_ >> 8,
+    MP_QSTR_frozentest_dot_py & 0xff, MP_QSTR_frozentest_dot_py >> 8,
+    0x2a, 0x28, 0x28, 0x28, 0x2b, 0x28, 0x00, 0x00, 0xff,
+    0x1b, MP_QSTR_print & 0xff, MP_QSTR_print >> 8,
+    0x16, MP_QSTR_uPy & 0xff, MP_QSTR_uPy >> 8,
+    0x64, 0x01, 
+    0x32, 
+    0x1b, MP_QSTR_print & 0xff, MP_QSTR_print >> 8,
+    0x17, 0x00, 
+    0x64, 0x01, 
+    0x32, 
+    0x1b, MP_QSTR_print & 0xff, MP_QSTR_print >> 8,
+    0x17, 0x01, 
+    0x64, 0x01, 
+    0x32, 
+    0x1b, MP_QSTR_print & 0xff, MP_QSTR_print >> 8,
+    0x17, 0x02, 
+    0x64, 0x01, 
+    0x32, 
+    0x1b, MP_QSTR_print & 0xff, MP_QSTR_print >> 8,
+    0x14, 0xba, 0xef, 0x9a, 0x15, 
+    0x64, 0x01, 
+    0x32, 
+    0x80, 
+    0x35, 0x0f, 0x80, 
+    0x30, 
+    0x24, MP_QSTR_i & 0xff, MP_QSTR_i >> 8,
+    0x1b, MP_QSTR_print & 0xff, MP_QSTR_print >> 8,
+    0x1b, MP_QSTR_i & 0xff, MP_QSTR_i >> 8,
+    0x64, 0x01, 
+    0x32, 
+    0x81, 
+    0xe5, 
+    0x30, 
+    0x84, 
+    0xd7, 
+    0x36, 0xeb, 0x7f, 
+    0x32, 
+    0x11, 
+    0x5b, 
+};
+STATIC const mp_obj_str_t const_obj_frozentest__lt_module_gt__0 = {{&mp_type_str}, 246, 34, (const byte*)"\x61\x20\x6c\x6f\x6e\x67\x20\x73\x74\x72\x69\x6e\x67\x20\x74\x68\x61\x74\x20\x69\x73\x20\x6e\x6f\x74\x20\x69\x6e\x74\x65\x72\x6e\x65\x64"};
+STATIC const mp_obj_str_t const_obj_frozentest__lt_module_gt__1 = {{&mp_type_str}, 200, 38, (const byte*)"\x61\x20\x73\x74\x72\x69\x6e\x67\x20\x74\x68\x61\x74\x20\x68\x61\x73\x20\x75\x6e\x69\x63\x6f\x64\x65\x20\xce\xb1\xce\xb2\xce\xb3\x20\x63\x68\x61\x72\x73"};
+STATIC const mp_obj_str_t const_obj_frozentest__lt_module_gt__2 = {{&mp_type_bytes}, 57, 11, (const byte*)"\x62\x79\x74\x65\x73\x20\x31\x32\x33\x34\x01"};
+STATIC const mp_rom_obj_t const_table_data_frozentest__lt_module_gt_[3] = {
+    MP_ROM_PTR(&const_obj_frozentest__lt_module_gt__0),
+    MP_ROM_PTR(&const_obj_frozentest__lt_module_gt__1),
+    MP_ROM_PTR(&const_obj_frozentest__lt_module_gt__2),
+};
+const mp_raw_code_t raw_code_frozentest__lt_module_gt_ = {
+    .kind = MP_CODE_BYTECODE,
+    .scope_flags = 0x00,
+    .n_pos_args = 0,
+    .data.u_byte = {
+        .bytecode = bytecode_data_frozentest__lt_module_gt_,
+        .const_table = (mp_uint_t*)const_table_data_frozentest__lt_module_gt_,
+        #if MICROPY_PERSISTENT_CODE_SAVE
+        .bc_len = 92,
+        .n_obj = 3,
+        .n_raw_code = 0,
+        #endif
+    },
+};
+
+const char mp_frozen_mpy_names[] = {
+"frozentest.py\0"
+"\0"};
+const mp_raw_code_t *const mp_frozen_mpy_content[] = {
+    &raw_code_frozentest__lt_module_gt_,
+};

+ 8 - 0
port/genhdr/mpversion.h

@@ -0,0 +1,8 @@
+// This file was generated by py/makeversionhdr.py
+#define MICROPY_GIT_TAG "v1.9.3-6-g1742ab26-dirty"
+#define MICROPY_GIT_HASH "1742ab26-dirty"
+#define MICROPY_BUILD_DATE "2017-11-11"
+#define MICROPY_VERSION_MAJOR (1)
+#define MICROPY_VERSION_MINOR (9)
+#define MICROPY_VERSION_MICRO (3)
+#define MICROPY_VERSION_STRING "1.9.3"

+ 464 - 0
port/genhdr/qstrdefs.generated.h

@@ -0,0 +1,464 @@
+// This file was automatically generated by makeqstrdata.py
+
+QDEF(MP_QSTR_NULL, (const byte*)"\x00\x00" "")
+QDEF(MP_QSTR_, (const byte*)"\x05\x00" "")
+QDEF(MP_QSTR___abs__, (const byte*)"\x95\x07" "__abs__")
+QDEF(MP_QSTR___add__, (const byte*)"\xc4\x07" "__add__")
+QDEF(MP_QSTR___and__, (const byte*)"\x0e\x07" "__and__")
+QDEF(MP_QSTR___bool__, (const byte*)"\x2b\x08" "__bool__")
+QDEF(MP_QSTR___build_class__, (const byte*)"\x42\x0f" "__build_class__")
+QDEF(MP_QSTR___call__, (const byte*)"\xa7\x08" "__call__")
+QDEF(MP_QSTR___class__, (const byte*)"\x2b\x09" "__class__")
+QDEF(MP_QSTR___contains__, (const byte*)"\xc6\x0c" "__contains__")
+QDEF(MP_QSTR___del__, (const byte*)"\x68\x07" "__del__")
+QDEF(MP_QSTR___delitem__, (const byte*)"\xfd\x0b" "__delitem__")
+QDEF(MP_QSTR___divmod__, (const byte*)"\x78\x0a" "__divmod__")
+QDEF(MP_QSTR___enter__, (const byte*)"\x6d\x09" "__enter__")
+QDEF(MP_QSTR___eq__, (const byte*)"\x71\x06" "__eq__")
+QDEF(MP_QSTR___exit__, (const byte*)"\x45\x08" "__exit__")
+QDEF(MP_QSTR___file__, (const byte*)"\x03\x08" "__file__")
+QDEF(MP_QSTR___floordiv__, (const byte*)"\x46\x0c" "__floordiv__")
+QDEF(MP_QSTR___ge__, (const byte*)"\xa7\x06" "__ge__")
+QDEF(MP_QSTR___getattr__, (const byte*)"\x40\x0b" "__getattr__")
+QDEF(MP_QSTR___getitem__, (const byte*)"\x26\x0b" "__getitem__")
+QDEF(MP_QSTR___gt__, (const byte*)"\xb6\x06" "__gt__")
+QDEF(MP_QSTR___hash__, (const byte*)"\xf7\x08" "__hash__")
+QDEF(MP_QSTR___iadd__, (const byte*)"\x6d\x08" "__iadd__")
+QDEF(MP_QSTR___import__, (const byte*)"\x38\x0a" "__import__")
+QDEF(MP_QSTR___init__, (const byte*)"\x5f\x08" "__init__")
+QDEF(MP_QSTR___invert__, (const byte*)"\xf7\x0a" "__invert__")
+QDEF(MP_QSTR___isub__, (const byte*)"\x08\x08" "__isub__")
+QDEF(MP_QSTR___iter__, (const byte*)"\xcf\x08" "__iter__")
+QDEF(MP_QSTR___le__, (const byte*)"\xcc\x06" "__le__")
+QDEF(MP_QSTR___len__, (const byte*)"\xe2\x07" "__len__")
+QDEF(MP_QSTR___lshift__, (const byte*)"\x09\x0a" "__lshift__")
+QDEF(MP_QSTR___lt__, (const byte*)"\x5d\x06" "__lt__")
+QDEF(MP_QSTR___main__, (const byte*)"\x8e\x08" "__main__")
+QDEF(MP_QSTR___mod__, (const byte*)"\x63\x07" "__mod__")
+QDEF(MP_QSTR___module__, (const byte*)"\xff\x0a" "__module__")
+QDEF(MP_QSTR___mul__, (const byte*)"\x31\x07" "__mul__")
+QDEF(MP_QSTR___name__, (const byte*)"\xe2\x08" "__name__")
+QDEF(MP_QSTR___neg__, (const byte*)"\x69\x07" "__neg__")
+QDEF(MP_QSTR___new__, (const byte*)"\x79\x07" "__new__")
+QDEF(MP_QSTR___next__, (const byte*)"\x02\x08" "__next__")
+QDEF(MP_QSTR___or__, (const byte*)"\x38\x06" "__or__")
+QDEF(MP_QSTR___path__, (const byte*)"\xc8\x08" "__path__")
+QDEF(MP_QSTR___pos__, (const byte*)"\x29\x07" "__pos__")
+QDEF(MP_QSTR___pow__, (const byte*)"\x2d\x07" "__pow__")
+QDEF(MP_QSTR___qualname__, (const byte*)"\x6b\x0c" "__qualname__")
+QDEF(MP_QSTR___repl_print__, (const byte*)"\x01\x0e" "__repl_print__")
+QDEF(MP_QSTR___repr__, (const byte*)"\x10\x08" "__repr__")
+QDEF(MP_QSTR___reversed__, (const byte*)"\x61\x0c" "__reversed__")
+QDEF(MP_QSTR___rshift__, (const byte*)"\x57\x0a" "__rshift__")
+QDEF(MP_QSTR___setitem__, (const byte*)"\x32\x0b" "__setitem__")
+QDEF(MP_QSTR___str__, (const byte*)"\xd0\x07" "__str__")
+QDEF(MP_QSTR___sub__, (const byte*)"\x21\x07" "__sub__")
+QDEF(MP_QSTR___traceback__, (const byte*)"\x4f\x0d" "__traceback__")
+QDEF(MP_QSTR___truediv__, (const byte*)"\x88\x0b" "__truediv__")
+QDEF(MP_QSTR___xor__, (const byte*)"\x20\x07" "__xor__")
+QDEF(MP_QSTR__star_, (const byte*)"\x8f\x01" "*")
+QDEF(MP_QSTR__, (const byte*)"\xfa\x01" "_")
+QDEF(MP_QSTR__slash_, (const byte*)"\x8a\x01" "/")
+QDEF(MP_QSTR__percent__hash_o, (const byte*)"\x6c\x03" "%#o")
+QDEF(MP_QSTR__percent__hash_x, (const byte*)"\x7b\x03" "%#x")
+QDEF(MP_QSTR__brace_open__colon__hash_b_brace_close_, (const byte*)"\x58\x05" "{:#b}")
+QDEF(MP_QSTR__0x0a_, (const byte*)"\xaf\x01" "\x0a")
+QDEF(MP_QSTR_maximum_space_recursion_space_depth_space_exceeded, (const byte*)"\x73\x20" "maximum recursion depth exceeded")
+QDEF(MP_QSTR__lt_module_gt_, (const byte*)"\xbd\x08" "<module>")
+QDEF(MP_QSTR__lt_lambda_gt_, (const byte*)"\x80\x08" "<lambda>")
+QDEF(MP_QSTR__lt_listcomp_gt_, (const byte*)"\xd4\x0a" "<listcomp>")
+QDEF(MP_QSTR__lt_dictcomp_gt_, (const byte*)"\xcc\x0a" "<dictcomp>")
+QDEF(MP_QSTR__lt_setcomp_gt_, (const byte*)"\x54\x09" "<setcomp>")
+QDEF(MP_QSTR__lt_genexpr_gt_, (const byte*)"\x34\x09" "<genexpr>")
+QDEF(MP_QSTR__lt_string_gt_, (const byte*)"\x52\x08" "<string>")
+QDEF(MP_QSTR__lt_stdin_gt_, (const byte*)"\xe3\x07" "<stdin>")
+QDEF(MP_QSTR_utf_hyphen_8, (const byte*)"\xb7\x05" "utf-8")
+QDEF(MP_QSTR_ALT_OD, (const byte*)"\x88\x06" "ALT_OD")
+QDEF(MP_QSTR_ALT_PP, (const byte*)"\xe3\x06" "ALT_PP")
+QDEF(MP_QSTR_ANALOG, (const byte*)"\xaf\x06" "ANALOG")
+QDEF(MP_QSTR_ARRAY, (const byte*)"\x5c\x05" "ARRAY")
+QDEF(MP_QSTR_ArithmeticError, (const byte*)"\x2d\x0f" "ArithmeticError")
+QDEF(MP_QSTR_AssertionError, (const byte*)"\x97\x0e" "AssertionError")
+QDEF(MP_QSTR_AttributeError, (const byte*)"\x21\x0e" "AttributeError")
+QDEF(MP_QSTR_BFINT16, (const byte*)"\x95\x07" "BFINT16")
+QDEF(MP_QSTR_BFINT32, (const byte*)"\x53\x07" "BFINT32")
+QDEF(MP_QSTR_BFINT8, (const byte*)"\x4a\x06" "BFINT8")
+QDEF(MP_QSTR_BFUINT16, (const byte*)"\x40\x08" "BFUINT16")
+QDEF(MP_QSTR_BFUINT32, (const byte*)"\x06\x08" "BFUINT32")
+QDEF(MP_QSTR_BFUINT8, (const byte*)"\xbf\x07" "BFUINT8")
+QDEF(MP_QSTR_BF_LEN, (const byte*)"\x19\x06" "BF_LEN")
+QDEF(MP_QSTR_BF_POS, (const byte*)"\x52\x06" "BF_POS")
+QDEF(MP_QSTR_BIG_ENDIAN, (const byte*)"\xff\x0a" "BIG_ENDIAN")
+QDEF(MP_QSTR_BaseException, (const byte*)"\x07\x0d" "BaseException")
+QDEF(MP_QSTR_BytesIO, (const byte*)"\x1a\x07" "BytesIO")
+QDEF(MP_QSTR_DEBUG, (const byte*)"\x34\x05" "DEBUG")
+QDEF(MP_QSTR_DEEPSLEEP_RESET, (const byte*)"\x14\x0f" "DEEPSLEEP_RESET")
+QDEF(MP_QSTR_DecompIO, (const byte*)"\x93\x08" "DecompIO")
+QDEF(MP_QSTR_EOFError, (const byte*)"\x91\x08" "EOFError")
+QDEF(MP_QSTR_Ellipsis, (const byte*)"\xf0\x08" "Ellipsis")
+QDEF(MP_QSTR_Exception, (const byte*)"\xf2\x09" "Exception")
+QDEF(MP_QSTR_FLOAT32, (const byte*)"\xb4\x07" "FLOAT32")
+QDEF(MP_QSTR_FLOAT64, (const byte*)"\x17\x07" "FLOAT64")
+QDEF(MP_QSTR_GeneratorExit, (const byte*)"\x16\x0d" "GeneratorExit")
+QDEF(MP_QSTR_HARD_RESET, (const byte*)"\xb0\x0a" "HARD_RESET")
+QDEF(MP_QSTR_IN, (const byte*)"\x22\x02" "IN")
+QDEF(MP_QSTR_INT16, (const byte*)"\x91\x05" "INT16")
+QDEF(MP_QSTR_INT32, (const byte*)"\x57\x05" "INT32")
+QDEF(MP_QSTR_INT64, (const byte*)"\xf4\x05" "INT64")
+QDEF(MP_QSTR_INT8, (const byte*)"\xce\x04" "INT8")
+QDEF(MP_QSTR_IRQ_FALLING, (const byte*)"\x37\x0b" "IRQ_FALLING")
+QDEF(MP_QSTR_IRQ_RISING, (const byte*)"\x78\x0a" "IRQ_RISING")
+QDEF(MP_QSTR_IRQ_RISING_FALLING, (const byte*)"\x60\x12" "IRQ_RISING_FALLING")
+QDEF(MP_QSTR_ImportError, (const byte*)"\x20\x0b" "ImportError")
+QDEF(MP_QSTR_IndentationError, (const byte*)"\x5c\x10" "IndentationError")
+QDEF(MP_QSTR_IndexError, (const byte*)"\x83\x0a" "IndexError")
+QDEF(MP_QSTR_KeyError, (const byte*)"\xea\x08" "KeyError")
+QDEF(MP_QSTR_KeyboardInterrupt, (const byte*)"\xaf\x11" "KeyboardInterrupt")
+QDEF(MP_QSTR_LITTLE_ENDIAN, (const byte*)"\xbf\x0d" "LITTLE_ENDIAN")
+QDEF(MP_QSTR_LookupError, (const byte*)"\xff\x0b" "LookupError")
+QDEF(MP_QSTR_MemoryError, (const byte*)"\xdc\x0b" "MemoryError")
+QDEF(MP_QSTR_NATIVE, (const byte*)"\x04\x06" "NATIVE")
+QDEF(MP_QSTR_NameError, (const byte*)"\xba\x09" "NameError")
+QDEF(MP_QSTR_NoneType, (const byte*)"\x17\x08" "NoneType")
+QDEF(MP_QSTR_NotImplementedError, (const byte*)"\xc6\x13" "NotImplementedError")
+QDEF(MP_QSTR_OSError, (const byte*)"\xa1\x07" "OSError")
+QDEF(MP_QSTR_OUT_OD, (const byte*)"\x1f\x06" "OUT_OD")
+QDEF(MP_QSTR_OUT_PP, (const byte*)"\x34\x06" "OUT_PP")
+QDEF(MP_QSTR_OrderedDict, (const byte*)"\xf0\x0b" "OrderedDict")
+QDEF(MP_QSTR_OverflowError, (const byte*)"\x81\x0d" "OverflowError")
+QDEF(MP_QSTR_PTR, (const byte*)"\xb3\x03" "PTR")
+QDEF(MP_QSTR_PULL_DOWN, (const byte*)"\xad\x09" "PULL_DOWN")
+QDEF(MP_QSTR_PULL_NONE, (const byte*)"\x55\x09" "PULL_NONE")
+QDEF(MP_QSTR_PULL_UP, (const byte*)"\xba\x07" "PULL_UP")
+QDEF(MP_QSTR_PWRON_RESET, (const byte*)"\xdb\x0b" "PWRON_RESET")
+QDEF(MP_QSTR_Pin, (const byte*)"\x12\x03" "Pin")
+QDEF(MP_QSTR_PinBase, (const byte*)"\x47\x07" "PinBase")
+QDEF(MP_QSTR_RuntimeError, (const byte*)"\x61\x0c" "RuntimeError")
+QDEF(MP_QSTR_SOFT_RESET, (const byte*)"\x01\x0a" "SOFT_RESET")
+QDEF(MP_QSTR_Signal, (const byte*)"\x9b\x06" "Signal")
+QDEF(MP_QSTR_StopIteration, (const byte*)"\xea\x0d" "StopIteration")
+QDEF(MP_QSTR_StringIO, (const byte*)"\x76\x08" "StringIO")
+QDEF(MP_QSTR_SyntaxError, (const byte*)"\x94\x0b" "SyntaxError")
+QDEF(MP_QSTR_SystemExit, (const byte*)"\x20\x0a" "SystemExit")
+QDEF(MP_QSTR_TypeError, (const byte*)"\x25\x09" "TypeError")
+QDEF(MP_QSTR_UINT16, (const byte*)"\xc4\x06" "UINT16")
+QDEF(MP_QSTR_UINT32, (const byte*)"\x82\x06" "UINT32")
+QDEF(MP_QSTR_UINT64, (const byte*)"\x61\x06" "UINT64")
+QDEF(MP_QSTR_UINT8, (const byte*)"\xbb\x05" "UINT8")
+QDEF(MP_QSTR_UnicodeError, (const byte*)"\x22\x0c" "UnicodeError")
+QDEF(MP_QSTR_VOID, (const byte*)"\x31\x04" "VOID")
+QDEF(MP_QSTR_ValueError, (const byte*)"\x96\x0a" "ValueError")
+QDEF(MP_QSTR_WDT_RESET, (const byte*)"\x08\x09" "WDT_RESET")
+QDEF(MP_QSTR_ZeroDivisionError, (const byte*)"\xb6\x11" "ZeroDivisionError")
+QDEF(MP_QSTR_a2b_base64, (const byte*)"\x3c\x0a" "a2b_base64")
+QDEF(MP_QSTR_abs, (const byte*)"\x95\x03" "abs")
+QDEF(MP_QSTR_acos, (const byte*)"\x1b\x04" "acos")
+QDEF(MP_QSTR_acosh, (const byte*)"\x13\x05" "acosh")
+QDEF(MP_QSTR_add, (const byte*)"\x44\x03" "add")
+QDEF(MP_QSTR_addressof, (const byte*)"\x5a\x09" "addressof")
+QDEF(MP_QSTR_all, (const byte*)"\x44\x03" "all")
+QDEF(MP_QSTR_any, (const byte*)"\x13\x03" "any")
+QDEF(MP_QSTR_append, (const byte*)"\x6b\x06" "append")
+QDEF(MP_QSTR_args, (const byte*)"\xc2\x04" "args")
+QDEF(MP_QSTR_array, (const byte*)"\x7c\x05" "array")
+QDEF(MP_QSTR_asin, (const byte*)"\x50\x04" "asin")
+QDEF(MP_QSTR_asinh, (const byte*)"\x38\x05" "asinh")
+QDEF(MP_QSTR_atan, (const byte*)"\x1f\x04" "atan")
+QDEF(MP_QSTR_atan2, (const byte*)"\xcd\x05" "atan2")
+QDEF(MP_QSTR_atanh, (const byte*)"\x97\x05" "atanh")
+QDEF(MP_QSTR_b2a_base64, (const byte*)"\x3c\x0a" "b2a_base64")
+QDEF(MP_QSTR_bin, (const byte*)"\xe0\x03" "bin")
+QDEF(MP_QSTR_bool, (const byte*)"\xeb\x04" "bool")
+QDEF(MP_QSTR_bound_method, (const byte*)"\x97\x0c" "bound_method")
+QDEF(MP_QSTR_builtins, (const byte*)"\xf7\x08" "builtins")
+QDEF(MP_QSTR_bytearray, (const byte*)"\x76\x09" "bytearray")
+QDEF(MP_QSTR_bytearray_at, (const byte*)"\x9c\x0c" "bytearray_at")
+QDEF(MP_QSTR_bytecode, (const byte*)"\x22\x08" "bytecode")
+QDEF(MP_QSTR_bytes, (const byte*)"\x5c\x05" "bytes")
+QDEF(MP_QSTR_bytes_at, (const byte*)"\xb6\x08" "bytes_at")
+QDEF(MP_QSTR_callable, (const byte*)"\x0d\x08" "callable")
+QDEF(MP_QSTR_ceil, (const byte*)"\x06\x04" "ceil")
+QDEF(MP_QSTR_center, (const byte*)"\x4e\x06" "center")
+QDEF(MP_QSTR_choice, (const byte*)"\x2e\x06" "choice")
+QDEF(MP_QSTR_chr, (const byte*)"\xdc\x03" "chr")
+QDEF(MP_QSTR_classmethod, (const byte*)"\xb4\x0b" "classmethod")
+QDEF(MP_QSTR_clear, (const byte*)"\x7c\x05" "clear")
+QDEF(MP_QSTR_close, (const byte*)"\x33\x05" "close")
+QDEF(MP_QSTR_closure, (const byte*)"\x74\x07" "closure")
+QDEF(MP_QSTR_cmath, (const byte*)"\xb6\x05" "cmath")
+QDEF(MP_QSTR_code, (const byte*)"\x68\x04" "code")
+QDEF(MP_QSTR_collect, (const byte*)"\x9b\x07" "collect")
+QDEF(MP_QSTR_compile, (const byte*)"\xf4\x07" "compile")
+QDEF(MP_QSTR_complex, (const byte*)"\xc5\x07" "complex")
+QDEF(MP_QSTR_const, (const byte*)"\xc0\x05" "const")
+QDEF(MP_QSTR_copy, (const byte*)"\xe0\x04" "copy")
+QDEF(MP_QSTR_copysign, (const byte*)"\x33\x08" "copysign")
+QDEF(MP_QSTR_cos, (const byte*)"\x7a\x03" "cos")
+QDEF(MP_QSTR_cosh, (const byte*)"\xd2\x04" "cosh")
+QDEF(MP_QSTR_count, (const byte*)"\xa6\x05" "count")
+QDEF(MP_QSTR_current_tid, (const byte*)"\xca\x0b" "current_tid")
+QDEF(MP_QSTR_decompress, (const byte*)"\x62\x0a" "decompress")
+QDEF(MP_QSTR_deepsleep, (const byte*)"\x9e\x09" "deepsleep")
+QDEF(MP_QSTR_default, (const byte*)"\xce\x07" "default")
+QDEF(MP_QSTR_degrees, (const byte*)"\x02\x07" "degrees")
+QDEF(MP_QSTR_delay, (const byte*)"\x50\x05" "delay")
+QDEF(MP_QSTR_deleter, (const byte*)"\x6e\x07" "deleter")
+QDEF(MP_QSTR_dict, (const byte*)"\x3f\x04" "dict")
+QDEF(MP_QSTR_dict_view, (const byte*)"\x2d\x09" "dict_view")
+QDEF(MP_QSTR_difference, (const byte*)"\x72\x0a" "difference")
+QDEF(MP_QSTR_difference_update, (const byte*)"\x9c\x11" "difference_update")
+QDEF(MP_QSTR_digest, (const byte*)"\xcd\x06" "digest")
+QDEF(MP_QSTR_dir, (const byte*)"\xfa\x03" "dir")
+QDEF(MP_QSTR_disable, (const byte*)"\x91\x07" "disable")
+QDEF(MP_QSTR_disable_irq, (const byte*)"\x04\x0b" "disable_irq")
+QDEF(MP_QSTR_discard, (const byte*)"\x0f\x07" "discard")
+QDEF(MP_QSTR_divmod, (const byte*)"\xb8\x06" "divmod")
+QDEF(MP_QSTR_doc, (const byte*)"\x2d\x03" "doc")
+QDEF(MP_QSTR_dumps, (const byte*)"\x7a\x05" "dumps")
+QDEF(MP_QSTR_e, (const byte*)"\xc0\x01" "e")
+QDEF(MP_QSTR_elapsed_micros, (const byte*)"\x39\x0e" "elapsed_micros")
+QDEF(MP_QSTR_elapsed_millis, (const byte*)"\x8e\x0e" "elapsed_millis")
+QDEF(MP_QSTR_enable, (const byte*)"\x04\x06" "enable")
+QDEF(MP_QSTR_enable_irq, (const byte*)"\x91\x0a" "enable_irq")
+QDEF(MP_QSTR_end, (const byte*)"\x0a\x03" "end")
+QDEF(MP_QSTR_endswith, (const byte*)"\x1b\x08" "endswith")
+QDEF(MP_QSTR_enumerate, (const byte*)"\x71\x09" "enumerate")
+QDEF(MP_QSTR_erf, (const byte*)"\x94\x03" "erf")
+QDEF(MP_QSTR_erfc, (const byte*)"\x77\x04" "erfc")
+QDEF(MP_QSTR_eval, (const byte*)"\x9b\x04" "eval")
+QDEF(MP_QSTR_exec, (const byte*)"\x1e\x04" "exec")
+QDEF(MP_QSTR_execfile, (const byte*)"\x58\x08" "execfile")
+QDEF(MP_QSTR_exp, (const byte*)"\xc8\x03" "exp")
+QDEF(MP_QSTR_expm1, (const byte*)"\x74\x05" "expm1")
+QDEF(MP_QSTR_extend, (const byte*)"\x63\x06" "extend")
+QDEF(MP_QSTR_fabs, (const byte*)"\x93\x04" "fabs")
+QDEF(MP_QSTR_fault_debug, (const byte*)"\x61\x0b" "fault_debug")
+QDEF(MP_QSTR_filter, (const byte*)"\x25\x06" "filter")
+QDEF(MP_QSTR_find, (const byte*)"\x01\x04" "find")
+QDEF(MP_QSTR_float, (const byte*)"\x35\x05" "float")
+QDEF(MP_QSTR_floor, (const byte*)"\x7d\x05" "floor")
+QDEF(MP_QSTR_flush, (const byte*)"\x61\x05" "flush")
+QDEF(MP_QSTR_fmod, (const byte*)"\xe5\x04" "fmod")
+QDEF(MP_QSTR_format, (const byte*)"\x26\x06" "format")
+QDEF(MP_QSTR_freq, (const byte*)"\xe5\x04" "freq")
+QDEF(MP_QSTR_frexp, (const byte*)"\x1c\x05" "frexp")
+QDEF(MP_QSTR_from_bytes, (const byte*)"\x35\x0a" "from_bytes")
+QDEF(MP_QSTR_fromkeys, (const byte*)"\x37\x08" "fromkeys")
+QDEF(MP_QSTR_frozenset, (const byte*)"\xed\x09" "frozenset")
+QDEF(MP_QSTR_function, (const byte*)"\x27\x08" "function")
+QDEF(MP_QSTR_gamma, (const byte*)"\x02\x05" "gamma")
+QDEF(MP_QSTR_gc, (const byte*)"\x61\x02" "gc")
+QDEF(MP_QSTR_generator, (const byte*)"\x96\x09" "generator")
+QDEF(MP_QSTR_get, (const byte*)"\x33\x03" "get")
+QDEF(MP_QSTR_getattr, (const byte*)"\xc0\x07" "getattr")
+QDEF(MP_QSTR_getrandbits, (const byte*)"\x66\x0b" "getrandbits")
+QDEF(MP_QSTR_getter, (const byte*)"\x90\x06" "getter")
+QDEF(MP_QSTR_getvalue, (const byte*)"\x78\x08" "getvalue")
+QDEF(MP_QSTR_globals, (const byte*)"\x9d\x07" "globals")
+QDEF(MP_QSTR_group, (const byte*)"\xba\x05" "group")
+QDEF(MP_QSTR_hard_reset, (const byte*)"\xd0\x0a" "hard_reset")
+QDEF(MP_QSTR_hasattr, (const byte*)"\x8c\x07" "hasattr")
+QDEF(MP_QSTR_hash, (const byte*)"\xb7\x04" "hash")
+QDEF(MP_QSTR_heap_lock, (const byte*)"\xad\x09" "heap_lock")
+QDEF(MP_QSTR_heap_unlock, (const byte*)"\x56\x0b" "heap_unlock")
+QDEF(MP_QSTR_heapify, (const byte*)"\xaf\x07" "heapify")
+QDEF(MP_QSTR_heappop, (const byte*)"\xd6\x07" "heappop")
+QDEF(MP_QSTR_heappush, (const byte*)"\x87\x08" "heappush")
+QDEF(MP_QSTR_help, (const byte*)"\x94\x04" "help")
+QDEF(MP_QSTR_hex, (const byte*)"\x70\x03" "hex")
+QDEF(MP_QSTR_hexlify, (const byte*)"\x2a\x07" "hexlify")
+QDEF(MP_QSTR_id, (const byte*)"\x28\x02" "id")
+QDEF(MP_QSTR_idle, (const byte*)"\xa1\x04" "idle")
+QDEF(MP_QSTR_imag, (const byte*)"\x47\x04" "imag")
+QDEF(MP_QSTR_index, (const byte*)"\x7b\x05" "index")
+QDEF(MP_QSTR_info, (const byte*)"\xeb\x04" "info")
+QDEF(MP_QSTR_init, (const byte*)"\x1f\x04" "init")
+QDEF(MP_QSTR_input, (const byte*)"\x73\x05" "input")
+QDEF(MP_QSTR_insert, (const byte*)"\x12\x06" "insert")
+QDEF(MP_QSTR_int, (const byte*)"\x16\x03" "int")
+QDEF(MP_QSTR_intersection, (const byte*)"\x28\x0c" "intersection")
+QDEF(MP_QSTR_intersection_update, (const byte*)"\x06\x13" "intersection_update")
+QDEF(MP_QSTR_invert, (const byte*)"\xb7\x06" "invert")
+QDEF(MP_QSTR_is_preempt_thread, (const byte*)"\x1a\x11" "is_preempt_thread")
+QDEF(MP_QSTR_isalpha, (const byte*)"\xeb\x07" "isalpha")
+QDEF(MP_QSTR_isdigit, (const byte*)"\xa8\x07" "isdigit")
+QDEF(MP_QSTR_isdisjoint, (const byte*)"\xf7\x0a" "isdisjoint")
+QDEF(MP_QSTR_isenabled, (const byte*)"\x9a\x09" "isenabled")
+QDEF(MP_QSTR_isfinite, (const byte*)"\xa6\x08" "isfinite")
+QDEF(MP_QSTR_isinf, (const byte*)"\x3e\x05" "isinf")
+QDEF(MP_QSTR_isinstance, (const byte*)"\xb6\x0a" "isinstance")
+QDEF(MP_QSTR_islower, (const byte*)"\xfc\x07" "islower")
+QDEF(MP_QSTR_isnan, (const byte*)"\x9e\x05" "isnan")
+QDEF(MP_QSTR_isspace, (const byte*)"\x5b\x07" "isspace")
+QDEF(MP_QSTR_issubclass, (const byte*)"\xb5\x0a" "issubclass")
+QDEF(MP_QSTR_issubset, (const byte*)"\xb9\x08" "issubset")
+QDEF(MP_QSTR_issuperset, (const byte*)"\xfc\x0a" "issuperset")
+QDEF(MP_QSTR_isupper, (const byte*)"\xdd\x07" "isupper")
+QDEF(MP_QSTR_items, (const byte*)"\xe3\x05" "items")
+QDEF(MP_QSTR_iter, (const byte*)"\x8f\x04" "iter")
+QDEF(MP_QSTR_iterator, (const byte*)"\x47\x08" "iterator")
+QDEF(MP_QSTR_join, (const byte*)"\xa7\x04" "join")
+QDEF(MP_QSTR_kbd_intr, (const byte*)"\xf6\x08" "kbd_intr")
+QDEF(MP_QSTR_keepends, (const byte*)"\x62\x08" "keepends")
+QDEF(MP_QSTR_key, (const byte*)"\x32\x03" "key")
+QDEF(MP_QSTR_keys, (const byte*)"\x01\x04" "keys")
+QDEF(MP_QSTR_ldexp, (const byte*)"\x40\x05" "ldexp")
+QDEF(MP_QSTR_len, (const byte*)"\x62\x03" "len")
+QDEF(MP_QSTR_lgamma, (const byte*)"\xce\x06" "lgamma")
+QDEF(MP_QSTR_list, (const byte*)"\x27\x04" "list")
+QDEF(MP_QSTR_little, (const byte*)"\x89\x06" "little")
+QDEF(MP_QSTR_load, (const byte*)"\x63\x04" "load")
+QDEF(MP_QSTR_loads, (const byte*)"\xb0\x05" "loads")
+QDEF(MP_QSTR_locals, (const byte*)"\x3b\x06" "locals")
+QDEF(MP_QSTR_log, (const byte*)"\x21\x03" "log")
+QDEF(MP_QSTR_log10, (const byte*)"\x40\x05" "log10")
+QDEF(MP_QSTR_log2, (const byte*)"\x73\x04" "log2")
+QDEF(MP_QSTR_lower, (const byte*)"\xc6\x05" "lower")
+QDEF(MP_QSTR_lstrip, (const byte*)"\xe5\x06" "lstrip")
+QDEF(MP_QSTR_machine, (const byte*)"\x60\x07" "machine")
+QDEF(MP_QSTR_map, (const byte*)"\xb9\x03" "map")
+QDEF(MP_QSTR_match, (const byte*)"\x96\x05" "match")
+QDEF(MP_QSTR_math, (const byte*)"\x35\x04" "math")
+QDEF(MP_QSTR_max, (const byte*)"\xb1\x03" "max")
+QDEF(MP_QSTR_mem, (const byte*)"\x20\x03" "mem")
+QDEF(MP_QSTR_mem_alloc, (const byte*)"\x52\x09" "mem_alloc")
+QDEF(MP_QSTR_mem_free, (const byte*)"\xcb\x08" "mem_free")
+QDEF(MP_QSTR_mem_info, (const byte*)"\xd1\x08" "mem_info")
+QDEF(MP_QSTR_memoryview, (const byte*)"\x69\x0a" "memoryview")
+QDEF(MP_QSTR_micropython, (const byte*)"\x0b\x0b" "micropython")
+QDEF(MP_QSTR_micros, (const byte*)"\xac\x06" "micros")
+QDEF(MP_QSTR_millis, (const byte*)"\x5b\x06" "millis")
+QDEF(MP_QSTR_min, (const byte*)"\xaf\x03" "min")
+QDEF(MP_QSTR_mode, (const byte*)"\x26\x04" "mode")
+QDEF(MP_QSTR_modf, (const byte*)"\x25\x04" "modf")
+QDEF(MP_QSTR_module, (const byte*)"\xbf\x06" "module")
+QDEF(MP_QSTR_modules, (const byte*)"\xec\x07" "modules")
+QDEF(MP_QSTR_mount, (const byte*)"\xa8\x05" "mount")
+QDEF(MP_QSTR_name, (const byte*)"\xa2\x04" "name")
+QDEF(MP_QSTR_namedtuple, (const byte*)"\x1e\x0a" "namedtuple")
+QDEF(MP_QSTR_next, (const byte*)"\x42\x04" "next")
+QDEF(MP_QSTR_object, (const byte*)"\x90\x06" "object")
+QDEF(MP_QSTR_oct, (const byte*)"\xfd\x03" "oct")
+QDEF(MP_QSTR_off, (const byte*)"\x8a\x03" "off")
+QDEF(MP_QSTR_on, (const byte*)"\x64\x02" "on")
+QDEF(MP_QSTR_open, (const byte*)"\xd1\x04" "open")
+QDEF(MP_QSTR_opt_level, (const byte*)"\x87\x09" "opt_level")
+QDEF(MP_QSTR_ord, (const byte*)"\x1c\x03" "ord")
+QDEF(MP_QSTR_partition, (const byte*)"\x87\x09" "partition")
+QDEF(MP_QSTR_peektime, (const byte*)"\x8b\x08" "peektime")
+QDEF(MP_QSTR_phase, (const byte*)"\x6a\x05" "phase")
+QDEF(MP_QSTR_pi, (const byte*)"\x1c\x02" "pi")
+QDEF(MP_QSTR_pin, (const byte*)"\xf2\x03" "pin")
+QDEF(MP_QSTR_polar, (const byte*)"\x05\x05" "polar")
+QDEF(MP_QSTR_pop, (const byte*)"\x2a\x03" "pop")
+QDEF(MP_QSTR_popitem, (const byte*)"\xbf\x07" "popitem")
+QDEF(MP_QSTR_pow, (const byte*)"\x2d\x03" "pow")
+QDEF(MP_QSTR_print, (const byte*)"\x54\x05" "print")
+QDEF(MP_QSTR_property, (const byte*)"\xc2\x08" "property")
+QDEF(MP_QSTR_pull, (const byte*)"\x80\x04" "pull")
+QDEF(MP_QSTR_push, (const byte*)"\xbb\x04" "push")
+QDEF(MP_QSTR_pyb, (const byte*)"\xee\x03" "pyb")
+QDEF(MP_QSTR_qstr_info, (const byte*)"\xb0\x09" "qstr_info")
+QDEF(MP_QSTR_radians, (const byte*)"\x87\x07" "radians")
+QDEF(MP_QSTR_randint, (const byte*)"\xaf\x07" "randint")
+QDEF(MP_QSTR_random, (const byte*)"\xbe\x06" "random")
+QDEF(MP_QSTR_randrange, (const byte*)"\xa3\x09" "randrange")
+QDEF(MP_QSTR_range, (const byte*)"\x1a\x05" "range")
+QDEF(MP_QSTR_read, (const byte*)"\xb7\x04" "read")
+QDEF(MP_QSTR_readinto, (const byte*)"\x4b\x08" "readinto")
+QDEF(MP_QSTR_readline, (const byte*)"\xf9\x08" "readline")
+QDEF(MP_QSTR_real, (const byte*)"\xbf\x04" "real")
+QDEF(MP_QSTR_rect, (const byte*)"\xe5\x04" "rect")
+QDEF(MP_QSTR_remove, (const byte*)"\x63\x06" "remove")
+QDEF(MP_QSTR_repl_info, (const byte*)"\xbf\x09" "repl_info")
+QDEF(MP_QSTR_replace, (const byte*)"\x49\x07" "replace")
+QDEF(MP_QSTR_repr, (const byte*)"\xd0\x04" "repr")
+QDEF(MP_QSTR_reset, (const byte*)"\x10\x05" "reset")
+QDEF(MP_QSTR_reset_cause, (const byte*)"\xce\x0b" "reset_cause")
+QDEF(MP_QSTR_reverse, (const byte*)"\x25\x07" "reverse")
+QDEF(MP_QSTR_reversed, (const byte*)"\xa1\x08" "reversed")
+QDEF(MP_QSTR_rfind, (const byte*)"\xd2\x05" "rfind")
+QDEF(MP_QSTR_rindex, (const byte*)"\xe9\x06" "rindex")
+QDEF(MP_QSTR_round, (const byte*)"\xe7\x05" "round")
+QDEF(MP_QSTR_rpartition, (const byte*)"\x15\x0a" "rpartition")
+QDEF(MP_QSTR_rsplit, (const byte*)"\xa5\x06" "rsplit")
+QDEF(MP_QSTR_rstrip, (const byte*)"\x3b\x06" "rstrip")
+QDEF(MP_QSTR_rtthread, (const byte*)"\x6d\x08" "rtthread")
+QDEF(MP_QSTR_search, (const byte*)"\xab\x06" "search")
+QDEF(MP_QSTR_seed, (const byte*)"\x92\x04" "seed")
+QDEF(MP_QSTR_seek, (const byte*)"\x9d\x04" "seek")
+QDEF(MP_QSTR_send, (const byte*)"\xb9\x04" "send")
+QDEF(MP_QSTR_sep, (const byte*)"\x23\x03" "sep")
+QDEF(MP_QSTR_set, (const byte*)"\x27\x03" "set")
+QDEF(MP_QSTR_setattr, (const byte*)"\xd4\x07" "setattr")
+QDEF(MP_QSTR_setdefault, (const byte*)"\x6c\x0a" "setdefault")
+QDEF(MP_QSTR_setter, (const byte*)"\x04\x06" "setter")
+QDEF(MP_QSTR_sha256, (const byte*)"\x2e\x06" "sha256")
+QDEF(MP_QSTR_sin, (const byte*)"\xb1\x03" "sin")
+QDEF(MP_QSTR_single, (const byte*)"\x3f\x06" "single")
+QDEF(MP_QSTR_sinh, (const byte*)"\xb9\x04" "sinh")
+QDEF(MP_QSTR_sizeof, (const byte*)"\x49\x06" "sizeof")
+QDEF(MP_QSTR_sleep, (const byte*)"\xea\x05" "sleep")
+QDEF(MP_QSTR_sleep_ms, (const byte*)"\x0b\x08" "sleep_ms")
+QDEF(MP_QSTR_sleep_us, (const byte*)"\x13\x08" "sleep_us")
+QDEF(MP_QSTR_slice, (const byte*)"\xb5\x05" "slice")
+QDEF(MP_QSTR_soft_reset, (const byte*)"\xe1\x0a" "soft_reset")
+QDEF(MP_QSTR_sort, (const byte*)"\xbf\x04" "sort")
+QDEF(MP_QSTR_sorted, (const byte*)"\x5e\x06" "sorted")
+QDEF(MP_QSTR_split, (const byte*)"\xb7\x05" "split")
+QDEF(MP_QSTR_splitlines, (const byte*)"\x6a\x0a" "splitlines")
+QDEF(MP_QSTR_sqrt, (const byte*)"\x21\x04" "sqrt")
+QDEF(MP_QSTR_stack_use, (const byte*)"\x97\x09" "stack_use")
+QDEF(MP_QSTR_stacks_analyze, (const byte*)"\x63\x0e" "stacks_analyze")
+QDEF(MP_QSTR_standby, (const byte*)"\xd2\x07" "standby")
+QDEF(MP_QSTR_start, (const byte*)"\x85\x05" "start")
+QDEF(MP_QSTR_startswith, (const byte*)"\x74\x0a" "startswith")
+QDEF(MP_QSTR_staticmethod, (const byte*)"\x62\x0c" "staticmethod")
+QDEF(MP_QSTR_step, (const byte*)"\x57\x04" "step")
+QDEF(MP_QSTR_stop, (const byte*)"\x9d\x04" "stop")
+QDEF(MP_QSTR_str, (const byte*)"\x50\x03" "str")
+QDEF(MP_QSTR_strip, (const byte*)"\x29\x05" "strip")
+QDEF(MP_QSTR_struct, (const byte*)"\x12\x06" "struct")
+QDEF(MP_QSTR_sum, (const byte*)"\x2e\x03" "sum")
+QDEF(MP_QSTR_super, (const byte*)"\xc4\x05" "super")
+QDEF(MP_QSTR_symmetric_difference, (const byte*)"\xce\x14" "symmetric_difference")
+QDEF(MP_QSTR_symmetric_difference_update, (const byte*)"\x60\x1b" "symmetric_difference_update")
+QDEF(MP_QSTR_sync, (const byte*)"\xa2\x04" "sync")
+QDEF(MP_QSTR_tan, (const byte*)"\xfe\x03" "tan")
+QDEF(MP_QSTR_tanh, (const byte*)"\xd6\x04" "tanh")
+QDEF(MP_QSTR_throw, (const byte*)"\xb3\x05" "throw")
+QDEF(MP_QSTR_ticks_add, (const byte*)"\x9d\x09" "ticks_add")
+QDEF(MP_QSTR_ticks_cpu, (const byte*)"\x1a\x09" "ticks_cpu")
+QDEF(MP_QSTR_ticks_diff, (const byte*)"\xb1\x0a" "ticks_diff")
+QDEF(MP_QSTR_ticks_ms, (const byte*)"\x42\x08" "ticks_ms")
+QDEF(MP_QSTR_ticks_us, (const byte*)"\x5a\x08" "ticks_us")
+QDEF(MP_QSTR_time, (const byte*)"\xf0\x04" "time")
+QDEF(MP_QSTR_to_bytes, (const byte*)"\xd8\x08" "to_bytes")
+QDEF(MP_QSTR_trunc, (const byte*)"\x5b\x05" "trunc")
+QDEF(MP_QSTR_tuple, (const byte*)"\xfd\x05" "tuple")
+QDEF(MP_QSTR_type, (const byte*)"\x9d\x04" "type")
+QDEF(MP_QSTR_ubinascii, (const byte*)"\xc4\x09" "ubinascii")
+QDEF(MP_QSTR_ucollections, (const byte*)"\x15\x0c" "ucollections")
+QDEF(MP_QSTR_uctypes, (const byte*)"\xf8\x07" "uctypes")
+QDEF(MP_QSTR_udelay, (const byte*)"\x25\x06" "udelay")
+QDEF(MP_QSTR_uhashlib, (const byte*)"\x65\x08" "uhashlib")
+QDEF(MP_QSTR_uheapq, (const byte*)"\x1d\x06" "uheapq")
+QDEF(MP_QSTR_uio, (const byte*)"\xb6\x03" "uio")
+QDEF(MP_QSTR_ujson, (const byte*)"\xe8\x05" "ujson")
+QDEF(MP_QSTR_umachine, (const byte*)"\x95\x08" "umachine")
+QDEF(MP_QSTR_unhexlify, (const byte*)"\xb1\x09" "unhexlify")
+QDEF(MP_QSTR_uniform, (const byte*)"\x01\x07" "uniform")
+QDEF(MP_QSTR_union, (const byte*)"\xf6\x05" "union")
+QDEF(MP_QSTR_unique_id, (const byte*)"\x04\x09" "unique_id")
+QDEF(MP_QSTR_update, (const byte*)"\xb4\x06" "update")
+QDEF(MP_QSTR_upper, (const byte*)"\x27\x05" "upper")
+QDEF(MP_QSTR_urandom, (const byte*)"\xab\x07" "urandom")
+QDEF(MP_QSTR_ure, (const byte*)"\x87\x03" "ure")
+QDEF(MP_QSTR_utime, (const byte*)"\xe5\x05" "utime")
+QDEF(MP_QSTR_utimeq, (const byte*)"\xf4\x06" "utimeq")
+QDEF(MP_QSTR_uzlib, (const byte*)"\x6d\x05" "uzlib")
+QDEF(MP_QSTR_value, (const byte*)"\x4e\x05" "value")
+QDEF(MP_QSTR_values, (const byte*)"\x7d\x06" "values")
+QDEF(MP_QSTR_wfi, (const byte*)"\x9d\x03" "wfi")
+QDEF(MP_QSTR_write, (const byte*)"\x98\x05" "write")
+QDEF(MP_QSTR_zip, (const byte*)"\xe6\x03" "zip")

+ 40 - 0
port/help.c

@@ -0,0 +1,40 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Armink (armink.ztl@gmail.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/builtin.h"
+
+const char rtthread_help_text[] =
+"Welcome to MicroPython on RT-Thread!\n"
+"\n"
+"Control commands:\n"
+"  CTRL-A        -- on a blank line, enter raw REPL mode\n"
+"  CTRL-B        -- on a blank line, enter normal REPL mode\n"
+"  CTRL-C        -- interrupt a running program\n"
+"  CTRL-D        -- on a blank line, do a soft reset of the board\n"
+"  CTRL-E        -- on a blank line, enter paste mode\n"
+"\n"
+"For further help on a specific object, type help(obj)\n"
+;

+ 242 - 0
port/machine_pin.c

@@ -0,0 +1,242 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Armink (armink.ztl@gmail.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <rtthread.h>
+#include <drivers/pin.h>
+
+#include "py/runtime.h"
+#include "py/gc.h"
+#include "py/mphal.h"
+#include "modmachine.h"
+
+#define GPIO_MODE_IN                           ((uint32_t)0x00000000)   /*!< Input Floating Mode                   */
+#define GPIO_MODE_OUT_PP                       ((uint32_t)0x00000001)   /*!< Output Push Pull Mode                 */
+#define GPIO_MODE_OUT_OD                       ((uint32_t)0x00000011)   /*!< Output Open Drain Mode                */
+#define GPIO_MODE_AF_PP                        ((uint32_t)0x00000002)   /*!< Alternate Function Push Pull Mode     */
+#define GPIO_MODE_AF_OD                        ((uint32_t)0x00000012)   /*!< Alternate Function Open Drain Mode    */
+#define GPIO_MODE_ANALOG                       ((uint32_t)0x00000003)   /*!< Analog Mode  */
+#define GPIO_NOPULL                            ((uint32_t)0x00000000)   /*!< No Pull-up or Pull-down activation  */
+#define GPIO_PULLUP                            ((uint32_t)0x00000001)   /*!< Pull-up activation                  */
+#define GPIO_PULLDOWN                          ((uint32_t)0x00000002)   /*!< Pull-down activation                */
+#define GPIO_MODE_IT_RISING                    ((uint32_t)0x10110000)   /*!< External Interrupt Mode with Rising edge trigger detection          */
+#define GPIO_MODE_IT_FALLING                   ((uint32_t)0x10210000)   /*!< External Interrupt Mode with Falling edge trigger detection         */
+#define GPIO_MODE_IT_RISING_FALLING            ((uint32_t)0x10310000)   /*!< External Interrupt Mode with Rising/Falling edge trigger detection  */
+
+const mp_obj_base_t machine_pin_obj_template = {&machine_pin_type};
+
+STATIC mp_obj_t machine_pin_obj_init_helper(machine_pin_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
+
+STATIC void machine_pin_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
+    machine_pin_obj_t *self = self_in;
+    mp_printf(print, "<Pin %d>", self->pin);
+}
+
+// constructor(drv_name, pin, ...)
+mp_obj_t mp_pin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+    mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);
+
+    // get the wanted port
+    if (!MP_OBJ_IS_TYPE(args[0], &mp_type_tuple)) {
+        mp_raise_ValueError("Pin id must be tuple of (\"GPIO_x\", pin#)");
+    }
+    mp_obj_t *items;
+    mp_obj_get_array_fixed_n(args[0], 2, &items);
+    const char *pin_name = mp_obj_str_get_str(items[0]);
+    int wanted_pin = mp_obj_get_int(items[1]);
+
+    machine_pin_obj_t *pin = m_new_obj(machine_pin_obj_t);
+    if (!pin) {
+        mp_raise_OSError(ENOMEM);
+    }
+
+    strncpy(pin->name, pin_name, sizeof(pin->name));
+    pin->base = machine_pin_obj_template;
+    pin->pin = wanted_pin;
+
+    if (n_args > 1 || n_kw > 0) {
+        // pin mode given, so configure this GPIO
+        mp_map_t kw_args;
+        mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
+        machine_pin_obj_init_helper(pin, n_args - 1, args + 1, &kw_args);
+    }
+
+    return (mp_obj_t)pin;
+}
+
+// pin.init(mode, pull=None, *, value)
+STATIC mp_obj_t machine_pin_obj_init_helper(machine_pin_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+    enum { ARG_mode, ARG_pull, ARG_value };
+    static const mp_arg_t allowed_args[] = {
+        { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT },
+        { MP_QSTR_pull, MP_ARG_OBJ, {.u_obj = mp_const_none}},
+        { MP_QSTR_value, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}},
+    };
+
+    // parse args
+    mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+    mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+    // get io mode
+    uint mode = args[ARG_mode].u_int;
+
+    // get pull mode
+    uint pull = GPIO_NOPULL;
+
+    if (args[ARG_pull].u_obj != mp_const_none) {
+        pull = mp_obj_get_int(args[ARG_pull].u_obj);
+    }
+
+    switch(mode) {
+    case GPIO_MODE_IN: {
+        if (pull == GPIO_PULLUP) {
+            mode = PIN_MODE_INPUT_PULLUP;
+        } else if (pull == GPIO_PULLDOWN) {
+            mode = PIN_MODE_INPUT_PULLDOWN;
+        } else {
+            mode = PIN_MODE_INPUT;
+        }
+        break;
+    }
+    case GPIO_MODE_OUT_PP : {
+        mode = PIN_MODE_OUTPUT;
+        break;
+    }
+    case GPIO_MODE_OUT_OD : {
+        mode = PIN_MODE_OUTPUT_OD;
+        break;
+    }
+    case GPIO_MODE_AF_PP :
+    case GPIO_MODE_AF_OD :
+    case GPIO_MODE_ANALOG :
+        //TODO
+        mp_raise_NotImplementedError("not implemented pin mode");
+    }
+
+    rt_pin_mode(self->pin, mode);
+
+    // get initial value
+    if (args[ARG_value].u_obj != MP_OBJ_NULL) {
+        rt_pin_write(self->pin, mp_obj_is_true(args[ARG_value].u_obj));
+    }
+
+    return mp_const_none;
+}
+
+// fast method for getting/setting pin value
+STATIC mp_obj_t machine_pin_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+    mp_arg_check_num(n_args, n_kw, 0, 1, false);
+    machine_pin_obj_t *self = self_in;
+    if (n_args == 0) {
+        return mp_obj_new_bool(rt_pin_read(self->pin));
+    } else {
+        rt_pin_write(self->pin, mp_obj_is_true(args[0]));
+        return mp_const_none;
+    }
+}
+
+// pin.init(mode, pull)
+STATIC mp_obj_t machine_pin_obj_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
+    return machine_pin_obj_init_helper(args[0], n_args - 1, args + 1, kw_args);
+}
+MP_DEFINE_CONST_FUN_OBJ_KW(machine_pin_init_obj, 1, machine_pin_obj_init);
+
+// pin.value([value])
+STATIC mp_obj_t machine_pin_value(size_t n_args, const mp_obj_t *args) {
+    return machine_pin_call(args[0], n_args - 1, 0, args + 1);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pin_value_obj, 1, 2, machine_pin_value);
+
+// pin.name()
+STATIC mp_obj_t machine_pin_name(size_t n_args, const mp_obj_t *args) {
+    machine_pin_obj_t *self = (machine_pin_obj_t *)args[0];
+    return mp_obj_new_str(self->name, strlen(self->name), false);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pin_name_obj, 1, 2, machine_pin_name);
+
+// pin.pin()
+STATIC mp_obj_t machine_pin_pin(size_t n_args, const mp_obj_t *args) {
+    return MP_OBJ_NEW_SMALL_INT(((machine_pin_obj_t *)args[0])->pin);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pin_pin_obj, 1, 2, machine_pin_pin);
+
+STATIC mp_uint_t machine_pin_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) {
+    (void)errcode;
+    machine_pin_obj_t *self = self_in;
+
+    switch (request) {
+        case MP_PIN_READ: {
+            uint32_t pin_val = rt_pin_read(self->pin);
+            return pin_val;
+        }
+        case MP_PIN_WRITE: {
+            rt_pin_write(self->pin, arg);
+            return 0;
+        }
+    }
+    return -1;
+}
+
+STATIC const mp_rom_map_elem_t machine_pin_locals_dict_table[] = {
+    // instance methods
+    { MP_ROM_QSTR(MP_QSTR_init),    MP_ROM_PTR(&machine_pin_init_obj) },
+    { MP_ROM_QSTR(MP_QSTR_value),   MP_ROM_PTR(&machine_pin_value_obj) },
+    { MP_ROM_QSTR(MP_QSTR_name),    MP_ROM_PTR(&machine_pin_name_obj) },
+    { MP_ROM_QSTR(MP_QSTR_pin),     MP_ROM_PTR(&machine_pin_pin_obj) },
+
+    // class constants
+    { MP_ROM_QSTR(MP_QSTR_ALT_OD),    MP_ROM_INT(GPIO_MODE_AF_OD) },
+    { MP_ROM_QSTR(MP_QSTR_ALT_PP),    MP_ROM_INT(GPIO_MODE_AF_PP) },
+    { MP_ROM_QSTR(MP_QSTR_ANALOG),    MP_ROM_INT(GPIO_MODE_ANALOG) },
+    { MP_ROM_QSTR(MP_QSTR_IN),        MP_ROM_INT(GPIO_MODE_IN) },
+    { MP_ROM_QSTR(MP_QSTR_OUT_PP),    MP_ROM_INT(GPIO_MODE_OUT_PP) },
+    { MP_ROM_QSTR(MP_QSTR_OUT_OD),    MP_ROM_INT(GPIO_MODE_OUT_OD) },
+    { MP_ROM_QSTR(MP_QSTR_PULL_DOWN), MP_ROM_INT(GPIO_PULLDOWN) },
+    { MP_ROM_QSTR(MP_QSTR_PULL_NONE), MP_ROM_INT(GPIO_NOPULL) },
+    { MP_ROM_QSTR(MP_QSTR_PULL_UP),   MP_ROM_INT(GPIO_PULLUP) },
+    { MP_ROM_QSTR(MP_QSTR_IRQ_RISING), MP_ROM_INT(GPIO_MODE_IT_RISING) },
+    { MP_ROM_QSTR(MP_QSTR_IRQ_FALLING), MP_ROM_INT(GPIO_MODE_IT_FALLING) },
+    { MP_ROM_QSTR(MP_QSTR_IRQ_RISING_FALLING), MP_ROM_INT(GPIO_MODE_IT_RISING_FALLING) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(machine_pin_locals_dict, machine_pin_locals_dict_table);
+
+STATIC const mp_pin_p_t machine_pin_pin_p = {
+    .ioctl = machine_pin_ioctl,
+};
+
+const mp_obj_type_t machine_pin_type = {
+    { &mp_type_type },
+    .name = MP_QSTR_Pin,
+    .print = machine_pin_print,
+    .make_new = mp_pin_make_new,
+    .call = machine_pin_call,
+    .protocol = &machine_pin_pin_p,
+    .locals_dict = (mp_obj_t)&machine_pin_locals_dict,
+};

+ 207 - 0
port/modmachine.c

@@ -0,0 +1,207 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Armink (armink.ztl@gmail.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include "py/obj.h"
+#include "py/runtime.h"
+#include "py/gc.h"
+#include "lib/utils/pyexec.h"
+#include "extmod/machine_mem.h"
+#include "extmod/machine_signal.h"
+#include "extmod/machine_pulse.h"
+#include "extmod/machine_i2c.h"
+#include "modmachine.h"
+
+#include <rthw.h>
+#include <board.h>
+
+#define PYB_RESET_SOFT      (0)
+#define PYB_RESET_POWER_ON  (1)
+#define PYB_RESET_HARD      (2)
+#define PYB_RESET_WDT       (3)
+#define PYB_RESET_DEEPSLEEP (4)
+
+#if MICROPY_PY_MACHINE
+
+STATIC mp_obj_t machine_info(uint n_args, const mp_obj_t *args) {
+    extern int cmd_free(int argc, char **argv);
+    extern long list_thread(void);
+    // RT-Thread info
+    {
+        printf("---------------------------------------------\n");
+        printf("RT-Thread\n");
+        printf("---------------------------------------------\n");
+        cmd_free(0, NULL);
+        list_thread();
+        printf("---------------------------------------------\n");
+    }
+
+    // qstr info
+    {
+        mp_uint_t n_pool, n_qstr, n_str_data_bytes, n_total_bytes;
+        qstr_pool_info(&n_pool, &n_qstr, &n_str_data_bytes, &n_total_bytes);
+        printf("qstr:\n  n_pool=" UINT_FMT "\n  n_qstr=" UINT_FMT "\n  n_str_data_bytes=" UINT_FMT "\n  n_total_bytes=" UINT_FMT "\n", n_pool, n_qstr, n_str_data_bytes, n_total_bytes);
+    }
+    printf("---------------------------------------------\n");
+
+    // GC info
+    {
+        gc_info_t info;
+        gc_info(&info);
+        printf("GC:\n");
+        printf("  " UINT_FMT " total\n", info.total);
+        printf("  " UINT_FMT " : " UINT_FMT "\n", info.used, info.free);
+        printf("  1=" UINT_FMT " 2=" UINT_FMT " m=" UINT_FMT "\n", info.num_1block, info.num_2block, info.max_block);
+    }
+
+    // free space on flash
+    {
+        //TODO
+    }
+
+    if (n_args == 1) {
+        // arg given means dump gc allocation table
+        gc_dump_alloc_table();
+    }
+
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_info_obj, 0, 1, machine_info);
+
+STATIC mp_obj_t machine_unique_id(void) {
+    //TODO
+    MP_RTT_NOT_IMPL_PRINT;
+    return 0;
+}
+MP_DEFINE_CONST_FUN_OBJ_0(machine_unique_id_obj, machine_unique_id);
+
+STATIC mp_obj_t machine_reset(void) {
+    NVIC_SystemReset();
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_0(machine_reset_obj, machine_reset);
+
+STATIC mp_obj_t machine_soft_reset(void) {
+    pyexec_system_exit = PYEXEC_FORCED_EXIT;
+    nlr_raise(mp_obj_new_exception(&mp_type_SystemExit));
+}
+MP_DEFINE_CONST_FUN_OBJ_0(machine_soft_reset_obj, machine_soft_reset);
+
+STATIC mp_obj_t machine_freq(void) {
+    //TODO
+    MP_RTT_NOT_IMPL_PRINT;
+    return MP_OBJ_SMALL_INT_VALUE(0);
+}
+MP_DEFINE_CONST_FUN_OBJ_0(machine_freq_obj, machine_freq);
+
+STATIC mp_obj_t pyb_wfi(void) {
+    //TODO __WFI();
+    MP_RTT_NOT_IMPL_PRINT;
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_0(pyb_wfi_obj, pyb_wfi);
+
+static rt_base_t int_lvl;
+STATIC mp_obj_t pyb_disable_irq(void) {
+    int_lvl = rt_hw_interrupt_disable();
+    return mp_obj_new_bool(1);
+}
+MP_DEFINE_CONST_FUN_OBJ_0(pyb_disable_irq_obj, pyb_disable_irq);
+
+STATIC mp_obj_t pyb_enable_irq(uint n_args, const mp_obj_t *arg) {
+    if (n_args == 0) {
+        rt_hw_interrupt_enable(int_lvl);
+    } else {
+        if (mp_obj_is_true(arg[0])) {
+            rt_hw_interrupt_enable(int_lvl);
+        } else {
+            int_lvl = rt_hw_interrupt_disable();
+        }
+    }
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_enable_irq_obj, 0, 1, pyb_enable_irq);
+
+STATIC mp_obj_t machine_sleep (void) {
+    //TODO
+    MP_RTT_NOT_IMPL_PRINT;
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_0(machine_sleep_obj, machine_sleep);
+
+STATIC mp_obj_t machine_deepsleep (void) {
+    //TODO
+    MP_RTT_NOT_IMPL_PRINT;
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_0(machine_deepsleep_obj, machine_deepsleep);
+
+STATIC mp_obj_t machine_reset_cause(void) {
+    //TODO
+    MP_RTT_NOT_IMPL_PRINT;
+    return MP_OBJ_NEW_SMALL_INT(42);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_reset_cause_obj, machine_reset_cause);
+
+
+STATIC const mp_rom_map_elem_t machine_module_globals_table[] = {
+    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_umachine) },
+    { MP_ROM_QSTR(MP_QSTR_info),                MP_ROM_PTR(&machine_info_obj) },
+    { MP_ROM_QSTR(MP_QSTR_unique_id),           MP_ROM_PTR(&machine_unique_id_obj) },
+    { MP_ROM_QSTR(MP_QSTR_reset),               MP_ROM_PTR(&machine_reset_obj) },
+    { MP_ROM_QSTR(MP_QSTR_soft_reset),          MP_ROM_PTR(&machine_soft_reset_obj) },
+    { MP_ROM_QSTR(MP_QSTR_freq),                MP_ROM_PTR(&machine_freq_obj) },
+    { MP_ROM_QSTR(MP_QSTR_idle),                MP_ROM_PTR(&pyb_wfi_obj) },
+    { MP_ROM_QSTR(MP_QSTR_sleep),               MP_ROM_PTR(&machine_sleep_obj) },
+    { MP_ROM_QSTR(MP_QSTR_deepsleep),           MP_ROM_PTR(&machine_deepsleep_obj) },
+    { MP_ROM_QSTR(MP_QSTR_reset_cause),         MP_ROM_PTR(&machine_reset_cause_obj) },
+    { MP_ROM_QSTR(MP_QSTR_disable_irq),         MP_ROM_PTR(&pyb_disable_irq_obj) },
+    { MP_ROM_QSTR(MP_QSTR_enable_irq),          MP_ROM_PTR(&pyb_enable_irq_obj) },
+//    { MP_ROM_QSTR(MP_QSTR_time_pulse_us),       MP_ROM_PTR(&machine_time_pulse_us_obj) },
+
+    { MP_ROM_QSTR(MP_QSTR_Pin),                 MP_ROM_PTR(&machine_pin_type) },
+    { MP_ROM_QSTR(MP_QSTR_Signal),              MP_ROM_PTR(&machine_signal_type) },
+//    { MP_ROM_QSTR(MP_QSTR_I2C),                 MP_ROM_PTR(&machine_i2c_type) },
+//    { MP_ROM_QSTR(MP_QSTR_SPI),                 MP_ROM_PTR(&machine_hard_spi_type) },
+//    { MP_ROM_QSTR(MP_QSTR_UART),                MP_ROM_PTR(&pyb_uart_type) },
+//    { MP_ROM_QSTR(MP_QSTR_WDT),                 MP_ROM_PTR(&pyb_wdt_type) },
+    { MP_ROM_QSTR(MP_QSTR_PWRON_RESET),         MP_ROM_INT(PYB_RESET_POWER_ON) },
+    { MP_ROM_QSTR(MP_QSTR_HARD_RESET),          MP_ROM_INT(PYB_RESET_HARD) },
+    { MP_ROM_QSTR(MP_QSTR_WDT_RESET),           MP_ROM_INT(PYB_RESET_WDT) },
+    { MP_ROM_QSTR(MP_QSTR_DEEPSLEEP_RESET),     MP_ROM_INT(PYB_RESET_DEEPSLEEP) },
+    { MP_ROM_QSTR(MP_QSTR_SOFT_RESET),          MP_ROM_INT(PYB_RESET_SOFT) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(machine_module_globals, machine_module_globals_table);
+
+const mp_obj_module_t mp_module_machine = {
+    .base = { &mp_type_module },
+    .globals = (mp_obj_dict_t*)&machine_module_globals,
+};
+
+#endif // MICROPY_PY_MACHINE

+ 52 - 0
port/modmachine.h

@@ -0,0 +1,52 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Armink (armink.ztl@gmail.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _MODMACHINE_H
+#define _MODMACHINE_H
+
+#include "py/obj.h"
+#include <rtthread.h>
+
+extern const mp_obj_type_t machine_pin_type;
+
+MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(machine_info_obj);
+MP_DECLARE_CONST_FUN_OBJ_0(machine_unique_id_obj);
+MP_DECLARE_CONST_FUN_OBJ_0(machine_reset_obj);
+MP_DECLARE_CONST_FUN_OBJ_0(machine_bootloader_obj);
+MP_DECLARE_CONST_FUN_OBJ_0(machine_freq_obj);
+MP_DECLARE_CONST_FUN_OBJ_0(pyb_wfi_obj);
+MP_DECLARE_CONST_FUN_OBJ_0(pyb_disable_irq_obj);
+MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_enable_irq_obj);
+MP_DECLARE_CONST_FUN_OBJ_0(machine_sleep_obj);
+MP_DECLARE_CONST_FUN_OBJ_0(machine_deepsleep_obj);
+
+typedef struct _machine_pin_obj_t {
+    mp_obj_base_t base;
+    char name[RT_NAME_MAX];
+    uint32_t pin;
+} machine_pin_obj_t;
+
+#endif // _MODMACHINE_H

+ 166 - 0
port/modpyb.c

@@ -0,0 +1,166 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Armink (armink.ztl@gmail.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include "py/runtime.h"
+#include "py/gc.h"
+#include "py/builtin.h"
+#include "py/mphal.h"
+#include "lib/utils/pyexec.h"
+#include "portmodules.h"
+#include "modmachine.h"
+#include "extmod/vfs.h"
+#include "extmod/utime_mphal.h"
+
+/// \function elapsed_millis(start)
+/// Returns the number of milliseconds which have elapsed since `start`.
+///
+/// This function takes care of counter wrap, and always returns a positive
+/// number. This means it can be used to measure periods upto about 12.4 days.
+///
+/// Example:
+///     start = pyb.millis()
+///     while pyb.elapsed_millis(start) < 1000:
+///         # Perform some operation
+STATIC mp_obj_t pyb_elapsed_millis(mp_obj_t start) {
+    uint32_t startMillis = mp_obj_get_int(start);
+    uint32_t currMillis = mp_hal_ticks_ms();
+    return MP_OBJ_NEW_SMALL_INT((currMillis - startMillis) & 0x3fffffff);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_elapsed_millis_obj, pyb_elapsed_millis);
+
+/// \function elapsed_micros(start)
+/// Returns the number of microseconds which have elapsed since `start`.
+///
+/// This function takes care of counter wrap, and always returns a positive
+/// number. This means it can be used to measure periods upto about 17.8 minutes.
+///
+/// Example:
+///     start = pyb.micros()
+///     while pyb.elapsed_micros(start) < 1000:
+///         # Perform some operation
+STATIC mp_obj_t pyb_elapsed_micros(mp_obj_t start) {
+    uint32_t startMicros = mp_obj_get_int(start);
+    uint32_t currMicros = mp_hal_ticks_us();
+    return MP_OBJ_NEW_SMALL_INT((currMicros - startMicros) & 0x3fffffff);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_elapsed_micros_obj, pyb_elapsed_micros);
+
+MP_DECLARE_CONST_FUN_OBJ_KW(pyb_main_obj); // defined in main.c
+
+STATIC const mp_rom_map_elem_t pyb_module_globals_table[] = {
+    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_pyb) },
+
+    { MP_ROM_QSTR(MP_QSTR_hard_reset), MP_ROM_PTR(&machine_reset_obj) },
+    { MP_ROM_QSTR(MP_QSTR_info), MP_ROM_PTR(&machine_info_obj) },
+    { MP_ROM_QSTR(MP_QSTR_unique_id), MP_ROM_PTR(&machine_unique_id_obj) },
+    { MP_ROM_QSTR(MP_QSTR_freq), MP_ROM_PTR(&machine_freq_obj) },
+    { MP_ROM_QSTR(MP_QSTR_repl_info), MP_ROM_PTR(&pyb_set_repl_info_obj) },
+
+    { MP_ROM_QSTR(MP_QSTR_wfi), MP_ROM_PTR(&pyb_wfi_obj) },
+    { MP_ROM_QSTR(MP_QSTR_disable_irq), MP_ROM_PTR(&pyb_disable_irq_obj) },
+    { MP_ROM_QSTR(MP_QSTR_enable_irq), MP_ROM_PTR(&pyb_enable_irq_obj) },
+
+    { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&machine_sleep_obj) },
+    { MP_ROM_QSTR(MP_QSTR_standby), MP_ROM_PTR(&machine_deepsleep_obj) },
+//    { MP_ROM_QSTR(MP_QSTR_main), MP_ROM_PTR(&pyb_main_obj) },
+//    { MP_ROM_QSTR(MP_QSTR_repl_uart), MP_ROM_PTR(&pyb_repl_uart_obj) },
+
+    { MP_ROM_QSTR(MP_QSTR_millis), MP_ROM_PTR(&mp_utime_ticks_ms_obj) },
+    { MP_ROM_QSTR(MP_QSTR_elapsed_millis), MP_ROM_PTR(&pyb_elapsed_millis_obj) },
+    { MP_ROM_QSTR(MP_QSTR_micros), MP_ROM_PTR(&mp_utime_ticks_us_obj) },
+    { MP_ROM_QSTR(MP_QSTR_elapsed_micros), MP_ROM_PTR(&pyb_elapsed_micros_obj) },
+    { MP_ROM_QSTR(MP_QSTR_delay), MP_ROM_PTR(&mp_utime_sleep_ms_obj) },
+    { MP_ROM_QSTR(MP_QSTR_udelay), MP_ROM_PTR(&mp_utime_sleep_us_obj) },
+    { MP_ROM_QSTR(MP_QSTR_sync), MP_ROM_PTR(&mod_os_sync_obj) },
+    { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&mp_os_mount_obj) },
+
+//    { MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&pyb_timer_type) },
+
+//#if MICROPY_HW_ENABLE_RNG
+//    { MP_ROM_QSTR(MP_QSTR_rng), MP_ROM_PTR(&pyb_rng_get_obj) },
+//#endif
+//
+//#if MICROPY_HW_ENABLE_RTC
+//    { MP_ROM_QSTR(MP_QSTR_RTC), MP_ROM_PTR(&pyb_rtc_type) },
+//#endif
+//
+    { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) },
+//    { MP_ROM_QSTR(MP_QSTR_ExtInt), MP_ROM_PTR(&extint_type) },
+//
+//#if MICROPY_HW_ENABLE_SERVO
+//    { MP_ROM_QSTR(MP_QSTR_pwm), MP_ROM_PTR(&pyb_pwm_set_obj) },
+//    { MP_ROM_QSTR(MP_QSTR_servo), MP_ROM_PTR(&pyb_servo_set_obj) },
+//    { MP_ROM_QSTR(MP_QSTR_Servo), MP_ROM_PTR(&pyb_servo_type) },
+//#endif
+//
+//#if MICROPY_HW_HAS_SWITCH
+//    { MP_ROM_QSTR(MP_QSTR_Switch), MP_ROM_PTR(&pyb_switch_type) },
+//#endif
+//
+//#if MICROPY_HW_HAS_FLASH
+//    { MP_ROM_QSTR(MP_QSTR_Flash), MP_ROM_PTR(&pyb_flash_type) },
+//#endif
+//
+//#if MICROPY_HW_HAS_SDCARD
+//    { MP_ROM_QSTR(MP_QSTR_SD), MP_ROM_PTR(&pyb_sdcard_obj) }, // now obsolete
+//    { MP_ROM_QSTR(MP_QSTR_SDCard), MP_ROM_PTR(&pyb_sdcard_type) },
+//#endif
+//
+//#if defined(MICROPY_HW_LED1)
+//    { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pyb_led_type) },
+//#endif
+//    { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&pyb_i2c_type) },
+//    { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&pyb_spi_type) },
+//    { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&pyb_uart_type) },
+//#if MICROPY_HW_ENABLE_CAN
+//    { MP_ROM_QSTR(MP_QSTR_CAN), MP_ROM_PTR(&pyb_can_type) },
+//#endif
+//
+//    { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&pyb_adc_type) },
+//    { MP_ROM_QSTR(MP_QSTR_ADCAll), MP_ROM_PTR(&pyb_adc_all_type) },
+//
+//#if MICROPY_HW_ENABLE_DAC
+//    { MP_ROM_QSTR(MP_QSTR_DAC), MP_ROM_PTR(&pyb_dac_type) },
+//#endif
+//
+//#if MICROPY_HW_HAS_MMA7660
+//    { MP_ROM_QSTR(MP_QSTR_Accel), MP_ROM_PTR(&pyb_accel_type) },
+//#endif
+//
+//#if MICROPY_HW_HAS_LCD
+//    { MP_ROM_QSTR(MP_QSTR_LCD), MP_ROM_PTR(&pyb_lcd_type) },
+//#endif
+};
+
+STATIC MP_DEFINE_CONST_DICT(pyb_module_globals, pyb_module_globals_table);
+
+const mp_obj_module_t pyb_module = {
+    .base = { &mp_type_module },
+    .globals = (mp_obj_dict_t*)&pyb_module_globals,
+};

+ 87 - 0
port/modrtthread.c

@@ -0,0 +1,87 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Armink (armink.ztl@gmail.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/mpconfig.h"
+#if MICROPY_PY_RTTHREAD
+
+#include <rtthread.h>
+
+#include "py/runtime.h"
+
+rt_bool_t rt_is_preempt_thread(void) {
+    if (rt_interrupt_get_nest() || rt_critical_level()) {
+        return RT_FALSE;
+    } else {
+        return RT_TRUE;
+    }
+}
+
+STATIC mp_obj_t mod_is_preempt_thread(void) {
+    return mp_obj_new_bool(rt_is_preempt_thread());
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_is_preempt_thread_obj, mod_is_preempt_thread);
+
+STATIC mp_obj_t mod_current_tid(void) {
+    return MP_OBJ_NEW_SMALL_INT(rt_thread_self());
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_current_tid_obj, mod_current_tid);
+
+STATIC mp_obj_t mod_stacks_analyze(void) {
+    extern long list_thread(void);
+    list_thread();
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_stacks_analyze_obj, mod_stacks_analyze);
+
+STATIC mp_obj_t os_sync(void) {
+    //TODO
+    MP_RTT_NOT_IMPL_PRINT;
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_0(mod_os_sync_obj, os_sync);
+
+STATIC mp_obj_t mp_os_mount(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+    //TODO
+    rt_kprintf("NOT implement, Please add for your board!\n");
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_KW(mp_os_mount_obj, 2, mp_os_mount);
+
+STATIC const mp_rom_map_elem_t mp_module_rtthread_globals_table[] = {
+    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_rtthread) },
+    { MP_ROM_QSTR(MP_QSTR_is_preempt_thread), MP_ROM_PTR(&mod_is_preempt_thread_obj) },
+    { MP_ROM_QSTR(MP_QSTR_current_tid), MP_ROM_PTR(&mod_current_tid_obj) },
+    { MP_ROM_QSTR(MP_QSTR_stacks_analyze), MP_ROM_PTR(&mod_stacks_analyze_obj) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(mp_module_rtthread_globals, mp_module_rtthread_globals_table);
+
+const mp_obj_module_t mp_module_rtthread = {
+    .base = { &mp_type_module },
+    .globals = (mp_obj_dict_t*)&mp_module_rtthread_globals,
+};
+
+#endif // MICROPY_PY_RTTHREAD

+ 61 - 0
port/modutime.c

@@ -0,0 +1,61 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Armink (armink.ztl@gmail.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/mpconfig.h"
+#if MICROPY_PY_UTIME
+
+#include <rtthread.h>
+
+#include "py/runtime.h"
+#include "py/smallint.h"
+#include "py/mphal.h"
+#include "extmod/utime_mphal.h"
+
+STATIC mp_obj_t mod_time_time(void) {
+    return mp_obj_new_int(rt_tick_get() / 1000);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_time_time_obj, mod_time_time);
+
+STATIC const mp_rom_map_elem_t mp_module_time_globals_table[] = {
+    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_utime) },
+    { MP_ROM_QSTR(MP_QSTR_sleep), MP_ROM_PTR(&mp_utime_sleep_obj) },
+    { MP_ROM_QSTR(MP_QSTR_sleep_ms), MP_ROM_PTR(&mp_utime_sleep_ms_obj) },
+    { MP_ROM_QSTR(MP_QSTR_sleep_us), MP_ROM_PTR(&mp_utime_sleep_us_obj) },
+    { MP_ROM_QSTR(MP_QSTR_time), MP_ROM_PTR(&mod_time_time_obj) },
+    { MP_ROM_QSTR(MP_QSTR_ticks_ms), MP_ROM_PTR(&mp_utime_ticks_ms_obj) },
+    { MP_ROM_QSTR(MP_QSTR_ticks_us), MP_ROM_PTR(&mp_utime_ticks_us_obj) },
+    { MP_ROM_QSTR(MP_QSTR_ticks_cpu), MP_ROM_PTR(&mp_utime_ticks_cpu_obj) },
+    { MP_ROM_QSTR(MP_QSTR_ticks_add), MP_ROM_PTR(&mp_utime_ticks_add_obj) },
+    { MP_ROM_QSTR(MP_QSTR_ticks_diff), MP_ROM_PTR(&mp_utime_ticks_diff_obj) },
+};
+STATIC MP_DEFINE_CONST_DICT(mp_module_time_globals, mp_module_time_globals_table);
+
+const mp_obj_module_t mp_module_time = {
+    .base = { &mp_type_module },
+    .globals = (mp_obj_dict_t*)&mp_module_time_globals,
+};
+
+#endif // MICROPY_PY_UTIME

+ 212 - 0
port/mpconfigport.h

@@ -0,0 +1,212 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Armink (armink.ztl@gmail.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <rtthread.h>
+#include <stdint.h>
+#include <libc/libc_errno.h>
+
+// options to control how MicroPython is built
+
+// You can disable the built-in MicroPython compiler by setting the following
+// config option to 0.  If you do this then you won't get a REPL prompt, but you
+// will still be able to execute pre-compiled scripts, compiled with mpy-cross.
+#define MICROPY_ENABLE_COMPILER     (1)
+
+#if defined(PKG_MICROPYTHON_HEAP_SIZE)
+#define MICROPY_HEAP_SIZE PKG_MICROPYTHON_HEAP_SIZE
+#else
+#define MICROPY_HEAP_SIZE (8 * 1024)
+#endif
+
+#define MICROPY_STACK_CHECK         (1)
+#define MICROPY_QSTR_BYTES_IN_HASH  (1)
+#define MICROPY_QSTR_EXTRA_POOL     mp_qstr_frozen_const_pool
+#define MICROPY_ALLOC_PATH_MAX      (256)
+#define MICROPY_ALLOC_PARSE_CHUNK_INIT (16)
+#define MICROPY_EMIT_X64            (0)
+#define MICROPY_EMIT_THUMB          (0)
+#define MICROPY_EMIT_INLINE_THUMB   (0)
+#define MICROPY_COMP_MODULE_CONST   (0)
+#define MICROPY_COMP_CONST          (0)
+#define MICROPY_COMP_DOUBLE_TUPLE_ASSIGN (0)
+#define MICROPY_COMP_TRIPLE_TUPLE_ASSIGN (0)
+#define MICROPY_MEM_STATS           (0)
+#define MICROPY_DEBUG_PRINTERS      (0)
+#define MICROPY_ENABLE_GC           (1)
+#define MICROPY_ENABLE_FINALISER    (1)
+#define MICROPY_GC_ALLOC_THRESHOLD  (0)
+#define MICROPY_REPL_EVENT_DRIVEN   (0)
+#define MICROPY_REPL_AUTO_INDENT    (1)
+#define MICROPY_KBD_EXCEPTION       (1)
+#define MICROPY_HELPER_REPL         (1)
+#define MICROPY_HELPER_LEXER_UNIX   (0)
+#define MICROPY_ENABLE_SOURCE_LINE  (0)
+#define MICROPY_ENABLE_DOC_STRING   (0)
+#define MICROPY_ERROR_REPORTING     (MICROPY_ERROR_REPORTING_TERSE)
+#define MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG (0)
+#define MICROPY_PY_ASYNC_AWAIT      (0)
+// control over Python builtins
+#define MICROPY_PY_FUNCTION_ATTRS   (1)
+#define MICROPY_PY_BUILTINS_STR_UNICODE (1)
+#define MICROPY_PY_BUILTINS_STR_CENTER (1)
+#define MICROPY_PY_BUILTINS_STR_PARTITION (1)
+#define MICROPY_PY_BUILTINS_STR_SPLITLINES (1)
+#define MICROPY_PY_BUILTINS_BYTEARRAY (1)
+#define MICROPY_PY_BUILTINS_MEMORYVIEW (1)
+#define MICROPY_PY_BUILTINS_SLICE_ATTRS (1)
+#define MICROPY_PY_ALL_SPECIAL_METHODS (1)
+#define MICROPY_PY_BUILTINS_COMPILE (1)
+#define MICROPY_PY_BUILTINS_EXECFILE (1)
+#define MICROPY_PY_BUILTINS_INPUT (1)
+#define MICROPY_PY_BUILTINS_POW3 (1)
+#define MICROPY_PY_BUILTINS_ENUMERATE (1)
+#define MICROPY_PY_BUILTINS_FILTER  (1)
+#define MICROPY_PY_BUILTINS_FROZENSET (1)
+#define MICROPY_PY_BUILTINS_REVERSED (1)
+#define MICROPY_PY_BUILTINS_SET     (1)
+#define MICROPY_PY_BUILTINS_HELP    (1)
+#define MICROPY_PY_BUILTINS_HELP_TEXT rtthread_help_text
+#define MICROPY_PY_BUILTINS_HELP_MODULES (1)
+#define MICROPY_PY_BUILTINS_SLICE   (1)
+#define MICROPY_PY_BUILTINS_PROPERTY (1)
+#define MICROPY_PY_BUILTINS_MIN_MAX (1)
+#define MICROPY_PY___FILE__         (1)
+#define MICROPY_PY_GC               (1)
+#define MICROPY_PY_ARRAY            (1)
+#define MICROPY_PY_ARRAY_SLICE_ASSIGN (1)
+#define MICROPY_PY_ATTRTUPLE        (1)
+#define MICROPY_PY_COLLECTIONS      (1)
+#define MICROPY_PY_COLLECTIONS_ORDEREDDICT (1)
+#define MICROPY_PY_MATH             (1)
+#define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (1)
+#define MICROPY_PY_CMATH            (1)
+#define MICROPY_PY_IO               (1)
+#define MICROPY_PY_MICROPYTHON_MEM_INFO (1)
+#define MICROPY_STREAMS_NON_BLOCK   (1)
+#define MICROPY_MODULE_WEAK_LINKS   (1)
+#define MICROPY_CAN_OVERRIDE_BUILTINS (1)
+#define MICROPY_USE_INTERNAL_ERRNO  (1)
+#define MICROPY_PY_STRUCT           (0)
+#define MICROPY_PY_RTTHREAD         (1)
+#define MICROPY_PY_SYS              (0)
+#define MICROPY_MODULE_FROZEN_MPY   (1)
+#define MICROPY_CPYTHON_COMPAT      (0)
+#define MICROPY_LONGINT_IMPL        (MICROPY_LONGINT_IMPL_MPZ)
+#define MICROPY_FLOAT_IMPL          (MICROPY_FLOAT_IMPL_DOUBLE)
+
+// extended modules
+#define MICROPY_PY_UCTYPES          (1)
+#define MICROPY_PY_UZLIB            (0)
+#define MICROPY_PY_UJSON            (1)
+#define MICROPY_PY_URE              (0)
+#define MICROPY_PY_UHEAPQ           (1)
+#define MICROPY_PY_UHASHLIB         (0)
+#define MICROPY_PY_UBINASCII        (1)
+#define MICROPY_PY_UTIME            (1)
+#define MICROPY_PY_UTIME_MP_HAL     (1)
+#define MICROPY_PY_UTIMEQ           (1)
+#define MICROPY_PY_URANDOM          (1)
+#define MICROPY_PY_URANDOM_EXTRA_FUNCS (1)
+#define MICROPY_PY_MACHINE          (1)
+#define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new
+
+#if defined(__CC_ARM)
+//TODO
+#elif defined(__ICCARM__)
+//#include <sys/types.h>
+#define MICROPY_NO_ALLOCA           1
+#define NORETURN                    __noreturn
+#define MP_WEAK                     __weak
+#define MP_NOINLINE
+#define MP_ALWAYSINLINE
+#define MP_LIKELY(x)               x
+#define MP_UNLIKELY(x)             x
+#elif defined(__GNUC__)
+// We need to provide a declaration/definition of alloca()
+#include <alloca.h>
+#else
+    #error "not supported compiler"
+#endif /* defined(__CC_ARM) */
+
+// type definitions for the specific machine
+
+#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void*)((mp_uint_t)(p) | 1))
+
+// This port is intended to be 32-bit, but unfortunately, int32_t for
+// different targets may be defined in different ways - either as int
+// or as long. This requires different printf formatting specifiers
+// to print such value. So, we avoid int32_t and use int directly.
+#define UINT_FMT "%u"
+#define INT_FMT "%d"
+typedef int mp_int_t; // must be pointer size
+typedef unsigned mp_uint_t; // must be pointer size
+
+typedef long mp_off_t;
+
+#define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len)
+
+// extra built in names to add to the global namespace
+#define MICROPY_PORT_BUILTINS \
+    { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) },
+
+#define MICROPY_HW_BOARD_NAME "RT-Thread Board"
+#define MICROPY_HW_MCU_NAME "stm32f4"
+
+#ifdef __linux__
+#define MICROPY_MIN_USE_STDOUT (1)
+#endif
+
+#ifdef __thumb__
+#define MICROPY_MIN_USE_CORTEX_CPU (1)
+#define MICROPY_MIN_USE_STM32_MCU (1)
+#endif
+
+#define MP_STATE_PORT                  MP_STATE_VM
+
+#define MICROPY_PORT_ROOT_POINTERS     const char *readline_hist[8];
+
+extern const struct _mp_obj_module_t pyb_module;
+extern const struct _mp_obj_module_t mp_module_rtthread;
+extern const struct _mp_obj_module_t mp_module_time;
+extern const struct _mp_obj_module_t mp_module_machine;
+
+#if MICROPY_PY_RTTHREAD
+#define MICROPY_PY_RTTHREAD_DEF { MP_ROM_QSTR(MP_QSTR_rtthread), MP_ROM_PTR(&mp_module_rtthread) },
+#else
+#define MICROPY_PY_RTTHREAD_DEF
+#endif
+
+#define MICROPY_PORT_BUILTIN_MODULES \
+    { MP_ROM_QSTR(MP_QSTR_machine), MP_ROM_PTR(&mp_module_machine) }, \
+    { MP_ROM_QSTR(MP_QSTR_pyb), MP_ROM_PTR(&pyb_module) }, \
+    MICROPY_PY_RTTHREAD_DEF \
+
+#define MICROPY_PORT_BUILTIN_MODULE_WEAK_LINKS \
+    { MP_ROM_QSTR(MP_QSTR_time), MP_ROM_PTR(&mp_module_time) }, \
+
+
+#define MP_RTT_NOT_IMPL_PRINT rt_kprintf("Not implement on %s:%ld, Please add for your board!\n", __FILE__, __FUNCTION__)
+

+ 48 - 0
port/mphalport.h

@@ -0,0 +1,48 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Armink (armink.ztl@gmail.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <rtthread.h>
+
+static inline mp_uint_t mp_hal_ticks_us(void) {
+    return rt_tick_get() * 1000;
+}
+
+static inline mp_uint_t mp_hal_ticks_ms(void) {
+    return rt_tick_get() * 1000 / RT_TICK_PER_SECOND;
+}
+
+static inline mp_uint_t mp_hal_ticks_cpu(void) {
+    return rt_tick_get();
+}
+
+static inline void mp_hal_delay_us(mp_uint_t delay) {
+    rt_thread_delay(rt_tick_from_millisecond(delay / 1000));
+}
+
+static inline void mp_hal_delay_ms(mp_uint_t delay) {
+    rt_thread_delay(rt_tick_from_millisecond(delay));
+}
+

+ 151 - 0
port/mpy_main.c

@@ -0,0 +1,151 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Armink (armink.ztl@gmail.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <rtthread.h>
+#include <shell.h>
+
+#include "py/compile.h"
+#include "py/runtime.h"
+#include "py/repl.h"
+#include "py/gc.h"
+#include "py/mperrno.h"
+#include "py/stackctrl.h"
+#include "lib/utils/pyexec.h"
+#include "rtt_getchar.h"
+
+#if MICROPY_ENABLE_COMPILER
+void do_str(const char *src, mp_parse_input_kind_t input_kind) {
+    nlr_buf_t nlr;
+    if (nlr_push(&nlr) == 0) {
+        mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0);
+        qstr source_name = lex->source_name;
+        mp_parse_tree_t parse_tree = mp_parse(lex, input_kind);
+        mp_obj_t module_fun = mp_compile(&parse_tree, source_name, MP_EMIT_OPT_NONE, true);
+        mp_call_function_0(module_fun);
+        nlr_pop();
+    } else {
+        // uncaught exception
+        mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val);
+    }
+}
+#endif
+
+static char *stack_top;
+static char heap[MICROPY_HEAP_SIZE];
+
+void mpy_main(const char *filename) {
+    int stack_dummy;
+    stack_top = (char*)&stack_dummy;
+
+    rt_kprintf("\n");
+
+    rtt_getchar_init();
+
+    mp_stack_set_top(stack_top);
+    // Make MicroPython's stack limit somewhat smaller than full stack available
+    mp_stack_set_limit(FINSH_THREAD_STACK_SIZE - 512);
+
+    #if MICROPY_ENABLE_GC
+    gc_init(heap, heap + sizeof(heap));
+    #endif
+
+    mp_init();
+
+    if (filename) {
+        pyexec_file(filename);
+    } else {
+        #if MICROPY_ENABLE_COMPILER
+        #if MICROPY_REPL_EVENT_DRIVEN
+        pyexec_event_repl_init();
+        for (;;) {
+            int c = mp_hal_stdin_rx_chr();
+            if (pyexec_event_repl_process_char(c)) {
+                break;
+            }
+        }
+        #else
+        pyexec_friendly_repl();
+        #endif
+//      do_str("print('hello world!', list(x+1 for x in range(10)), end='eol\\n')", MP_PARSE_SINGLE_INPUT);
+//      do_str("for i in range(10):\r\n  print(i)", MP_PARSE_FILE_INPUT);
+        #else
+        pyexec_frozen_module("frozentest.py");
+        #endif
+    }
+    mp_deinit();
+
+    rtt_getchar_deinit();
+}
+
+void gc_collect(void) {
+    // WARNING: This gc_collect implementation doesn't try to get root
+    // pointers from CPU registers, and thus may function incorrectly.
+    void *dummy;
+    gc_collect_start();
+    gc_collect_root(&dummy, ((mp_uint_t)stack_top - (mp_uint_t)&dummy) / sizeof(mp_uint_t));
+    gc_collect_end();
+    gc_dump_info();
+}
+
+mp_lexer_t *mp_lexer_new_from_file(const char *filename) {
+    mp_raise_OSError(ENOENT);
+}
+
+mp_import_stat_t mp_import_stat(const char *path) {
+    return MP_IMPORT_STAT_NO_EXIST;
+}
+
+mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open);
+
+NORETURN void nlr_jump_fail(void *val) {
+    while (1);
+}
+
+#ifndef NDEBUG
+void MP_WEAK __assert_func(const char *file, int line, const char *func, const char *expr) {
+    printf("Assertion '%s' failed, at file %s:%d\n", expr, file, line);
+    __fatal_error("Assertion failed");
+}
+#endif
+
+#if defined(RT_USING_FINSH) && defined(FINSH_USING_MSH)
+#include <finsh.h>
+static void python(uint8_t argc, char **argv) {
+    if (argc > 1) {
+        mpy_main(argv[1]);
+    } else {
+        mpy_main(NULL);
+    }
+}
+MSH_CMD_EXPORT(python, MicroPython: `python [file.py]` execute python script);
+#endif /* defined(RT_USING_FINSH) && defined(FINSH_USING_MSH) */

+ 35 - 0
port/mpy_project_cfg.h

@@ -0,0 +1,35 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Armink (armink.ztl@gmail.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * MicroPython Project compile configuration
+ */
+#define NDEBUG                         0
+#define N_X64                          0
+#define N_X86                          0
+#define N_THUMB                        0
+#define N_ARM                          0
+#define N_XTENSA                       0

+ 36 - 0
port/portmodules.h

@@ -0,0 +1,36 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Armink (armink.ztl@gmail.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _PORTMODULES_H
+#define _PORTMODULES_H
+
+MP_DECLARE_CONST_FUN_OBJ_1(time_sleep_ms_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(time_sleep_us_obj);
+
+MP_DECLARE_CONST_FUN_OBJ_0(mod_os_sync_obj);
+MP_DECLARE_CONST_FUN_OBJ_KW(mp_os_mount_obj);
+
+#endif // _PORTMODULES_H

+ 1 - 0
port/qstrdefsport.h

@@ -0,0 +1 @@
+// qstrs specific to this port

+ 104 - 0
port/rtt_getchar.c

@@ -0,0 +1,104 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Armink (armink.ztl@gmail.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <rtthread.h>
+#include <rtdevice.h>
+#include <rthw.h>
+#include "rtt_getchar.h"
+#include "lib/utils/interrupt_char.h"
+
+#define UART_FIFO_SIZE 256
+
+static struct rt_semaphore notice;
+static struct rt_ringbuffer *rx_fifo = NULL;
+static rt_err_t (*odev_rx_ind)(rt_device_t dev, rt_size_t size) = NULL;
+
+static rt_err_t getchar_rx_ind(rt_device_t dev, rt_size_t size) {
+    uint8_t ch;
+    rt_size_t i;
+
+    for (i = 0; i < size; i++) {
+        /* read a char */
+        if (rt_device_read(dev, 0, &ch, 1)) {
+            if (ch == mp_interrupt_char) {
+                mp_keyboard_interrupt();
+            } else {
+                rt_ringbuffer_put_force(rx_fifo, &ch, 1);
+                rt_sem_release(&notice);
+            }
+        }
+    }
+    return RT_EOK;
+}
+
+void rtt_getchar_init(void) {
+    rt_base_t int_lvl;
+    rt_device_t console;
+
+    rt_sem_init(&notice, "uart_notice", 0, RT_IPC_FLAG_FIFO);
+
+    /* create RX FIFO */
+    rx_fifo = rt_ringbuffer_create(UART_FIFO_SIZE);
+    /* created must success */
+    RT_ASSERT(rx_fifo);
+
+    int_lvl = rt_hw_interrupt_disable();
+    console = rt_console_get_device();
+    if (console) {
+        /* backup RX indicate */
+        odev_rx_ind = console->rx_indicate;
+        rt_device_set_rx_indicate(console, getchar_rx_ind);
+    }
+    rt_hw_interrupt_enable(int_lvl);
+
+}
+
+void rtt_getchar_deinit(void) {
+    rt_base_t int_lvl;
+    rt_device_t console;
+
+    rt_sem_detach(&notice);
+    rt_ringbuffer_destroy(rx_fifo);
+
+    int_lvl = rt_hw_interrupt_disable();
+    console = rt_console_get_device();
+    if (console && odev_rx_ind) {
+        /* restore RX indicate */
+        rt_device_set_rx_indicate(console, odev_rx_ind);
+    }
+    rt_hw_interrupt_enable(int_lvl);
+}
+
+int rtt_getchar(void) {
+    uint8_t ch;
+
+    rt_sem_take(&notice, RT_WAITING_FOREVER);
+    rt_ringbuffer_getchar(rx_fifo, &ch);
+
+    return ch;
+
+}

+ 34 - 0
port/rtt_getchar.h

@@ -0,0 +1,34 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Armink (armink.ztl@gmail.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _RTT_GETCHAR_H_
+#define _RTT_GETCHAR_H_
+
+void rtt_getchar_init(void);
+void rtt_getchar_deinit(void);
+int rtt_getchar(void);
+
+#endif /* _RTT_GETCHAR_H_ */

+ 48 - 0
port/uart_core.c

@@ -0,0 +1,48 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Armink (armink.ztl@gmail.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/mpconfig.h"
+#include "rtt_getchar.h"
+#include <rtthread.h>
+
+/*
+ * Core UART functions to implement for a port
+ */
+
+// Receive single character
+int mp_hal_stdin_rx_chr(void) {
+    return rtt_getchar();
+}
+
+// Send string of given length
+void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
+    rt_device_t console;
+
+    console = rt_console_get_device();
+    if (console) {
+        rt_device_write(console, 0, str, len);
+    }
+}

+ 144 - 0
py/argcheck.c

@@ -0,0 +1,144 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+
+#include "py/runtime.h"
+
+void mp_arg_check_num(size_t n_args, size_t n_kw, size_t n_args_min, size_t n_args_max, bool takes_kw) {
+    // TODO maybe take the function name as an argument so we can print nicer error messages
+
+    if (n_kw && !takes_kw) {
+        if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) {
+            mp_arg_error_terse_mismatch();
+        } else {
+            mp_raise_TypeError("function does not take keyword arguments");
+        }
+    }
+
+    if (n_args_min == n_args_max) {
+        if (n_args != n_args_min) {
+            if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) {
+                mp_arg_error_terse_mismatch();
+            } else {
+                nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
+                    "function takes %d positional arguments but %d were given",
+                    n_args_min, n_args));
+            }
+        }
+    } else {
+        if (n_args < n_args_min) {
+            if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) {
+                mp_arg_error_terse_mismatch();
+            } else {
+                nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
+                    "function missing %d required positional arguments",
+                    n_args_min - n_args));
+            }
+        } else if (n_args > n_args_max) {
+            if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) {
+                mp_arg_error_terse_mismatch();
+            } else {
+                nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
+                    "function expected at most %d arguments, got %d",
+                    n_args_max, n_args));
+            }
+        }
+    }
+}
+
+void mp_arg_parse_all(size_t n_pos, const mp_obj_t *pos, mp_map_t *kws, size_t n_allowed, const mp_arg_t *allowed, mp_arg_val_t *out_vals) {
+    size_t pos_found = 0, kws_found = 0;
+    for (size_t i = 0; i < n_allowed; i++) {
+        mp_obj_t given_arg;
+        if (i < n_pos) {
+            if (allowed[i].flags & MP_ARG_KW_ONLY) {
+                goto extra_positional;
+            }
+            pos_found++;
+            given_arg = pos[i];
+        } else {
+            mp_map_elem_t *kw = mp_map_lookup(kws, MP_OBJ_NEW_QSTR(allowed[i].qst), MP_MAP_LOOKUP);
+            if (kw == NULL) {
+                if (allowed[i].flags & MP_ARG_REQUIRED) {
+                    if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) {
+                        mp_arg_error_terse_mismatch();
+                    } else {
+                        nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
+                            "'%q' argument required", allowed[i].qst));
+                    }
+                }
+                out_vals[i] = allowed[i].defval;
+                continue;
+            } else {
+                kws_found++;
+                given_arg = kw->value;
+            }
+        }
+        if ((allowed[i].flags & MP_ARG_KIND_MASK) == MP_ARG_BOOL) {
+            out_vals[i].u_bool = mp_obj_is_true(given_arg);
+        } else if ((allowed[i].flags & MP_ARG_KIND_MASK) == MP_ARG_INT) {
+            out_vals[i].u_int = mp_obj_get_int(given_arg);
+        } else {
+            assert((allowed[i].flags & MP_ARG_KIND_MASK) == MP_ARG_OBJ);
+            out_vals[i].u_obj = given_arg;
+        }
+    }
+    if (pos_found < n_pos) {
+        extra_positional:
+        if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) {
+            mp_arg_error_terse_mismatch();
+        } else {
+            // TODO better error message
+            mp_raise_TypeError("extra positional arguments given");
+        }
+    }
+    if (kws_found < kws->used) {
+        if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) {
+            mp_arg_error_terse_mismatch();
+        } else {
+            // TODO better error message
+            mp_raise_TypeError("extra keyword arguments given");
+        }
+    }
+}
+
+void mp_arg_parse_all_kw_array(size_t n_pos, size_t n_kw, const mp_obj_t *args, size_t n_allowed, const mp_arg_t *allowed, mp_arg_val_t *out_vals) {
+    mp_map_t kw_args;
+    mp_map_init_fixed_table(&kw_args, n_kw, args + n_pos);
+    mp_arg_parse_all(n_pos, args, &kw_args, n_allowed, allowed, out_vals);
+}
+
+NORETURN void mp_arg_error_terse_mismatch(void) {
+    mp_raise_TypeError("argument num/types mismatch");
+}
+
+#if MICROPY_CPYTHON_COMPAT
+NORETURN void mp_arg_error_unimpl_kw(void) {
+    mp_raise_NotImplementedError("keyword argument(s) not yet implemented - use normal args instead");
+}
+#endif

+ 368 - 0
py/asmarm.c

@@ -0,0 +1,368 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014 Fabian Vogt
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+#include "py/mpconfig.h"
+
+// wrapper around everything in this file
+#if MICROPY_EMIT_ARM
+
+#include "py/asmarm.h"
+
+#define SIGNED_FIT24(x) (((x) & 0xff800000) == 0) || (((x) & 0xff000000) == 0xff000000)
+
+void asm_arm_end_pass(asm_arm_t *as) {
+    if (as->base.pass == MP_ASM_PASS_EMIT) {
+#ifdef __arm__
+        // flush I- and D-cache
+        asm volatile(
+                "0:"
+                "mrc p15, 0, r15, c7, c10, 3\n"
+                "bne 0b\n"
+                "mov r0, #0\n"
+                "mcr p15, 0, r0, c7, c7, 0\n"
+                : : : "r0", "cc");
+#endif
+    }
+}
+
+// Insert word into instruction flow
+STATIC void emit(asm_arm_t *as, uint op) {
+    uint8_t *c = mp_asm_base_get_cur_to_write_bytes(&as->base, 4);
+    if (c != NULL) {
+        *(uint32_t*)c = op;
+    }
+}
+
+// Insert word into instruction flow, add "ALWAYS" condition code
+STATIC void emit_al(asm_arm_t *as, uint op) {
+    emit(as, op | ASM_ARM_CC_AL);
+}
+
+// Basic instructions without condition code
+STATIC uint asm_arm_op_push(uint reglist) {
+    // stmfd sp!, {reglist}
+    return 0x92d0000 | (reglist & 0xFFFF);
+}
+
+STATIC uint asm_arm_op_pop(uint reglist) {
+    // ldmfd sp!, {reglist}
+    return 0x8bd0000 | (reglist & 0xFFFF);
+}
+
+STATIC uint asm_arm_op_mov_reg(uint rd, uint rn) {
+    // mov rd, rn
+    return 0x1a00000 | (rd << 12) | rn;
+}
+
+STATIC uint asm_arm_op_mov_imm(uint rd, uint imm) {
+    // mov rd, #imm
+    return 0x3a00000 | (rd << 12) | imm;
+}
+
+STATIC uint asm_arm_op_mvn_imm(uint rd, uint imm) {
+    // mvn rd, #imm
+    return 0x3e00000 | (rd << 12) | imm;
+}
+
+STATIC uint asm_arm_op_add_imm(uint rd, uint rn, uint imm) {
+    // add rd, rn, #imm
+    return 0x2800000 | (rn << 16) | (rd << 12) | (imm & 0xFF);
+}
+
+STATIC uint asm_arm_op_add_reg(uint rd, uint rn, uint rm) {
+    // add rd, rn, rm
+    return 0x0800000 | (rn << 16) | (rd << 12) | rm;
+}
+
+STATIC uint asm_arm_op_sub_imm(uint rd, uint rn, uint imm) {
+    // sub rd, rn, #imm
+    return 0x2400000 | (rn << 16) | (rd << 12) | (imm & 0xFF);
+}
+
+STATIC uint asm_arm_op_sub_reg(uint rd, uint rn, uint rm) {
+    // sub rd, rn, rm
+    return 0x0400000 | (rn << 16) | (rd << 12) | rm;
+}
+
+STATIC uint asm_arm_op_mul_reg(uint rd, uint rm, uint rs) {
+    // mul rd, rm, rs
+    assert(rd != rm);
+    return 0x0000090 | (rd << 16) | (rs << 8) | rm;
+}
+
+STATIC uint asm_arm_op_and_reg(uint rd, uint rn, uint rm) {
+    // and rd, rn, rm
+    return 0x0000000 | (rn << 16) | (rd << 12) | rm;
+}
+
+STATIC uint asm_arm_op_eor_reg(uint rd, uint rn, uint rm) {
+    // eor rd, rn, rm
+    return 0x0200000 | (rn << 16) | (rd << 12) | rm;
+}
+
+STATIC uint asm_arm_op_orr_reg(uint rd, uint rn, uint rm) {
+    // orr rd, rn, rm
+    return 0x1800000 | (rn << 16) | (rd << 12) | rm;
+}
+
+void asm_arm_bkpt(asm_arm_t *as) {
+    // bkpt #0
+    emit_al(as, 0x1200070);
+}
+
+// locals:
+//  - stored on the stack in ascending order
+//  - numbered 0 through num_locals-1
+//  - SP points to first local
+//
+//  | SP
+//  v
+//  l0  l1  l2  ...  l(n-1)
+//  ^                ^
+//  | low address    | high address in RAM
+
+void asm_arm_entry(asm_arm_t *as, int num_locals) {
+
+    if (num_locals < 0) {
+        num_locals = 0;
+    }
+
+    as->stack_adjust = 0;
+    as->push_reglist = 1 << ASM_ARM_REG_R1
+        | 1 << ASM_ARM_REG_R2
+        | 1 << ASM_ARM_REG_R3
+        | 1 << ASM_ARM_REG_R4
+        | 1 << ASM_ARM_REG_R5
+        | 1 << ASM_ARM_REG_R6
+        | 1 << ASM_ARM_REG_R7
+        | 1 << ASM_ARM_REG_R8;
+
+    // Only adjust the stack if there are more locals than usable registers
+    if (num_locals > 3) {
+        as->stack_adjust = num_locals * 4;
+        // Align stack to 8 bytes
+        if (num_locals & 1) {
+            as->stack_adjust += 4;
+        }
+    }
+
+    emit_al(as, asm_arm_op_push(as->push_reglist | 1 << ASM_ARM_REG_LR));
+    if (as->stack_adjust > 0) {
+        emit_al(as, asm_arm_op_sub_imm(ASM_ARM_REG_SP, ASM_ARM_REG_SP, as->stack_adjust));
+    }
+}
+
+void asm_arm_exit(asm_arm_t *as) {
+    if (as->stack_adjust > 0) {
+        emit_al(as, asm_arm_op_add_imm(ASM_ARM_REG_SP, ASM_ARM_REG_SP, as->stack_adjust));
+    }
+
+    emit_al(as, asm_arm_op_pop(as->push_reglist | (1 << ASM_ARM_REG_PC)));
+}
+
+void asm_arm_push(asm_arm_t *as, uint reglist) {
+    emit_al(as, asm_arm_op_push(reglist));
+}
+
+void asm_arm_pop(asm_arm_t *as, uint reglist) {
+    emit_al(as, asm_arm_op_pop(reglist));
+}
+
+void asm_arm_mov_reg_reg(asm_arm_t *as, uint reg_dest, uint reg_src) {
+    emit_al(as, asm_arm_op_mov_reg(reg_dest, reg_src));
+}
+
+void asm_arm_mov_reg_i32(asm_arm_t *as, uint rd, int imm) {
+    // TODO: There are more variants of immediate values
+    if ((imm & 0xFF) == imm) {
+        emit_al(as, asm_arm_op_mov_imm(rd, imm));
+    } else if (imm < 0 && imm >= -256) {
+        // mvn is "move not", not "move negative"
+        emit_al(as, asm_arm_op_mvn_imm(rd, ~imm));
+    } else {
+        //Insert immediate into code and jump over it
+        emit_al(as, 0x59f0000 | (rd << 12)); // ldr rd, [pc]
+        emit_al(as, 0xa000000); // b pc
+        emit(as, imm);
+    }
+}
+
+void asm_arm_mov_local_reg(asm_arm_t *as, int local_num, uint rd) {
+    // str rd, [sp, #local_num*4]
+    emit_al(as, 0x58d0000 | (rd << 12) | (local_num << 2));
+}
+
+void asm_arm_mov_reg_local(asm_arm_t *as, uint rd, int local_num) {
+    // ldr rd, [sp, #local_num*4]
+    emit_al(as, 0x59d0000 | (rd << 12) | (local_num << 2));
+}
+
+void asm_arm_cmp_reg_i8(asm_arm_t *as, uint rd, int imm) {
+    // cmp rd, #imm
+    emit_al(as, 0x3500000 | (rd << 16) | (imm & 0xFF));
+}
+
+void asm_arm_cmp_reg_reg(asm_arm_t *as, uint rd, uint rn) {
+    // cmp rd, rn
+    emit_al(as, 0x1500000 | (rd << 16) | rn);
+}
+
+void asm_arm_setcc_reg(asm_arm_t *as, uint rd, uint cond) {
+    emit(as, asm_arm_op_mov_imm(rd, 1) | cond); // movCOND rd, #1
+    emit(as, asm_arm_op_mov_imm(rd, 0) | (cond ^ (1 << 28))); // mov!COND rd, #0
+}
+
+void asm_arm_add_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm) {
+    // add rd, rn, rm
+    emit_al(as, asm_arm_op_add_reg(rd, rn, rm));
+}
+
+void asm_arm_sub_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm) {
+    // sub rd, rn, rm
+    emit_al(as, asm_arm_op_sub_reg(rd, rn, rm));
+}
+
+void asm_arm_mul_reg_reg_reg(asm_arm_t *as, uint rd, uint rs, uint rm) {
+    // rs and rm are swapped because of restriction rd!=rm
+    // mul rd, rm, rs
+    emit_al(as, asm_arm_op_mul_reg(rd, rm, rs));
+}
+
+void asm_arm_and_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm) {
+    // and rd, rn, rm
+    emit_al(as, asm_arm_op_and_reg(rd, rn, rm));
+}
+
+void asm_arm_eor_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm) {
+    // eor rd, rn, rm
+    emit_al(as, asm_arm_op_eor_reg(rd, rn, rm));
+}
+
+void asm_arm_orr_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm) {
+    // orr rd, rn, rm
+    emit_al(as, asm_arm_op_orr_reg(rd, rn, rm));
+}
+
+void asm_arm_mov_reg_local_addr(asm_arm_t *as, uint rd, int local_num) {
+    // add rd, sp, #local_num*4
+    emit_al(as, asm_arm_op_add_imm(rd, ASM_ARM_REG_SP, local_num << 2));
+}
+
+void asm_arm_lsl_reg_reg(asm_arm_t *as, uint rd, uint rs) {
+    // mov rd, rd, lsl rs
+    emit_al(as, 0x1a00010 | (rd << 12) | (rs << 8) | rd);
+}
+
+void asm_arm_asr_reg_reg(asm_arm_t *as, uint rd, uint rs) {
+    // mov rd, rd, asr rs
+    emit_al(as, 0x1a00050 | (rd << 12) | (rs << 8) | rd);
+}
+
+void asm_arm_ldr_reg_reg(asm_arm_t *as, uint rd, uint rn, uint byte_offset) {
+    // ldr rd, [rn, #off]
+    emit_al(as, 0x5900000 | (rn << 16) | (rd << 12) | byte_offset);
+}
+
+void asm_arm_ldrh_reg_reg(asm_arm_t *as, uint rd, uint rn) {
+    // ldrh rd, [rn]
+    emit_al(as, 0x1d000b0 | (rn << 16) | (rd << 12));
+}
+
+void asm_arm_ldrb_reg_reg(asm_arm_t *as, uint rd, uint rn) {
+    // ldrb rd, [rn]
+    emit_al(as, 0x5d00000 | (rn << 16) | (rd << 12));
+}
+
+void asm_arm_str_reg_reg(asm_arm_t *as, uint rd, uint rm, uint byte_offset) {
+    // str rd, [rm, #off]
+    emit_al(as, 0x5800000 | (rm << 16) | (rd << 12) | byte_offset);
+}
+
+void asm_arm_strh_reg_reg(asm_arm_t *as, uint rd, uint rm) {
+    // strh rd, [rm]
+    emit_al(as, 0x1c000b0 | (rm << 16) | (rd << 12));
+}
+
+void asm_arm_strb_reg_reg(asm_arm_t *as, uint rd, uint rm) {
+    // strb rd, [rm]
+    emit_al(as, 0x5c00000 | (rm << 16) | (rd << 12));
+}
+
+void asm_arm_str_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn) {
+    // str rd, [rm, rn, lsl #2]
+    emit_al(as, 0x7800100 | (rm << 16) | (rd << 12) | rn);
+}
+
+void asm_arm_strh_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn) {
+    // strh doesn't support scaled register index
+    emit_al(as, 0x1a00080 | (ASM_ARM_REG_R8 << 12) | rn); // mov r8, rn, lsl #1
+    emit_al(as, 0x18000b0 | (rm << 16) | (rd << 12) | ASM_ARM_REG_R8); // strh rd, [rm, r8]
+}
+
+void asm_arm_strb_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn) {
+    // strb rd, [rm, rn]
+    emit_al(as, 0x7c00000 | (rm << 16) | (rd << 12) | rn);
+}
+
+void asm_arm_bcc_label(asm_arm_t *as, int cond, uint label) {
+    assert(label < as->base.max_num_labels);
+    mp_uint_t dest = as->base.label_offsets[label];
+    mp_int_t rel = dest - as->base.code_offset;
+    rel -= 8; // account for instruction prefetch, PC is 8 bytes ahead of this instruction
+    rel >>= 2; // in ARM mode the branch target is 32-bit aligned, so the 2 LSB are omitted
+
+    if (SIGNED_FIT24(rel)) {
+        emit(as, cond | 0xa000000 | (rel & 0xffffff));
+    } else {
+        printf("asm_arm_bcc: branch does not fit in 24 bits\n");
+    }
+}
+
+void asm_arm_b_label(asm_arm_t *as, uint label) {
+    asm_arm_bcc_label(as, ASM_ARM_CC_AL, label);
+}
+
+void asm_arm_bl_ind(asm_arm_t *as, void *fun_ptr, uint fun_id, uint reg_temp) {
+    // If the table offset fits into the ldr instruction
+    if (fun_id < (0x1000 / 4)) {
+        emit_al(as, asm_arm_op_mov_reg(ASM_ARM_REG_LR, ASM_ARM_REG_PC)); // mov lr, pc
+        emit_al(as, 0x597f000 | (fun_id << 2)); // ldr pc, [r7, #fun_id*4]
+        return;
+    }
+
+    emit_al(as, 0x59f0004 | (reg_temp << 12)); // ldr rd, [pc, #4]
+    // Set lr after fun_ptr
+    emit_al(as, asm_arm_op_add_imm(ASM_ARM_REG_LR, ASM_ARM_REG_PC, 4)); // add lr, pc, #4
+    emit_al(as, asm_arm_op_mov_reg(ASM_ARM_REG_PC, reg_temp)); // mov pc, reg_temp
+    emit(as, (uint) fun_ptr);
+}
+
+#endif // MICROPY_EMIT_ARM

+ 205 - 0
py/asmarm.h

@@ -0,0 +1,205 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014 Fabian Vogt
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_PY_ASMARM_H
+#define MICROPY_INCLUDED_PY_ASMARM_H
+
+#include "py/misc.h"
+#include "py/asmbase.h"
+
+#define ASM_ARM_REG_R0  (0)
+#define ASM_ARM_REG_R1  (1)
+#define ASM_ARM_REG_R2  (2)
+#define ASM_ARM_REG_R3  (3)
+#define ASM_ARM_REG_R4  (4)
+#define ASM_ARM_REG_R5  (5)
+#define ASM_ARM_REG_R6  (6)
+#define ASM_ARM_REG_R7  (7)
+#define ASM_ARM_REG_R8  (8)
+#define ASM_ARM_REG_R9  (9)
+#define ASM_ARM_REG_R10 (10)
+#define ASM_ARM_REG_R11 (11)
+#define ASM_ARM_REG_R12 (12)
+#define ASM_ARM_REG_R13 (13)
+#define ASM_ARM_REG_R14 (14)
+#define ASM_ARM_REG_R15 (15)
+#define ASM_ARM_REG_SP  (ASM_ARM_REG_R13)
+#define ASM_ARM_REG_LR  (ASM_ARM_REG_R14)
+#define ASM_ARM_REG_PC  (ASM_ARM_REG_R15)
+
+#define ASM_ARM_CC_EQ (0x0 << 28)
+#define ASM_ARM_CC_NE (0x1 << 28)
+#define ASM_ARM_CC_CS (0x2 << 28)
+#define ASM_ARM_CC_CC (0x3 << 28)
+#define ASM_ARM_CC_MI (0x4 << 28)
+#define ASM_ARM_CC_PL (0x5 << 28)
+#define ASM_ARM_CC_VS (0x6 << 28)
+#define ASM_ARM_CC_VC (0x7 << 28)
+#define ASM_ARM_CC_HI (0x8 << 28)
+#define ASM_ARM_CC_LS (0x9 << 28)
+#define ASM_ARM_CC_GE (0xa << 28)
+#define ASM_ARM_CC_LT (0xb << 28)
+#define ASM_ARM_CC_GT (0xc << 28)
+#define ASM_ARM_CC_LE (0xd << 28)
+#define ASM_ARM_CC_AL (0xe << 28)
+
+typedef struct _asm_arm_t {
+    mp_asm_base_t base;
+    uint push_reglist;
+    uint stack_adjust;
+} asm_arm_t;
+
+void asm_arm_end_pass(asm_arm_t *as);
+
+void asm_arm_entry(asm_arm_t *as, int num_locals);
+void asm_arm_exit(asm_arm_t *as);
+
+void asm_arm_bkpt(asm_arm_t *as);
+
+// mov
+void asm_arm_mov_reg_reg(asm_arm_t *as, uint reg_dest, uint reg_src);
+void asm_arm_mov_reg_i32(asm_arm_t *as, uint rd, int imm);
+void asm_arm_mov_local_reg(asm_arm_t *as, int local_num, uint rd);
+void asm_arm_mov_reg_local(asm_arm_t *as, uint rd, int local_num);
+void asm_arm_setcc_reg(asm_arm_t *as, uint rd, uint cond);
+
+// compare
+void asm_arm_cmp_reg_i8(asm_arm_t *as, uint rd, int imm);
+void asm_arm_cmp_reg_reg(asm_arm_t *as, uint rd, uint rn);
+
+// arithmetic
+void asm_arm_add_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm);
+void asm_arm_sub_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm);
+void asm_arm_mul_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm);
+void asm_arm_and_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm);
+void asm_arm_eor_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm);
+void asm_arm_orr_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm);
+void asm_arm_mov_reg_local_addr(asm_arm_t *as, uint rd, int local_num);
+void asm_arm_lsl_reg_reg(asm_arm_t *as, uint rd, uint rs);
+void asm_arm_asr_reg_reg(asm_arm_t *as, uint rd, uint rs);
+
+// memory
+void asm_arm_ldr_reg_reg(asm_arm_t *as, uint rd, uint rn, uint byte_offset);
+void asm_arm_ldrh_reg_reg(asm_arm_t *as, uint rd, uint rn);
+void asm_arm_ldrb_reg_reg(asm_arm_t *as, uint rd, uint rn);
+void asm_arm_str_reg_reg(asm_arm_t *as, uint rd, uint rm, uint byte_offset);
+void asm_arm_strh_reg_reg(asm_arm_t *as, uint rd, uint rm);
+void asm_arm_strb_reg_reg(asm_arm_t *as, uint rd, uint rm);
+// store to array
+void asm_arm_str_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn);
+void asm_arm_strh_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn);
+void asm_arm_strb_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn);
+
+// stack
+void asm_arm_push(asm_arm_t *as, uint reglist);
+void asm_arm_pop(asm_arm_t *as, uint reglist);
+
+// control flow
+void asm_arm_bcc_label(asm_arm_t *as, int cond, uint label);
+void asm_arm_b_label(asm_arm_t *as, uint label);
+void asm_arm_bl_ind(asm_arm_t *as, void *fun_ptr, uint fun_id, uint reg_temp);
+
+#if GENERIC_ASM_API
+
+// The following macros provide a (mostly) arch-independent API to
+// generate native code, and are used by the native emitter.
+
+#define ASM_WORD_SIZE (4)
+
+#define REG_RET ASM_ARM_REG_R0
+#define REG_ARG_1 ASM_ARM_REG_R0
+#define REG_ARG_2 ASM_ARM_REG_R1
+#define REG_ARG_3 ASM_ARM_REG_R2
+#define REG_ARG_4 ASM_ARM_REG_R3
+
+#define REG_TEMP0 ASM_ARM_REG_R0
+#define REG_TEMP1 ASM_ARM_REG_R1
+#define REG_TEMP2 ASM_ARM_REG_R2
+
+#define REG_LOCAL_1 ASM_ARM_REG_R4
+#define REG_LOCAL_2 ASM_ARM_REG_R5
+#define REG_LOCAL_3 ASM_ARM_REG_R6
+#define REG_LOCAL_NUM (3)
+
+#define ASM_T               asm_arm_t
+#define ASM_END_PASS        asm_arm_end_pass
+#define ASM_ENTRY           asm_arm_entry
+#define ASM_EXIT            asm_arm_exit
+
+#define ASM_JUMP            asm_arm_b_label
+#define ASM_JUMP_IF_REG_ZERO(as, reg, label) \
+    do { \
+        asm_arm_cmp_reg_i8(as, reg, 0); \
+        asm_arm_bcc_label(as, ASM_ARM_CC_EQ, label); \
+    } while (0)
+#define ASM_JUMP_IF_REG_NONZERO(as, reg, label) \
+    do { \
+        asm_arm_cmp_reg_i8(as, reg, 0); \
+        asm_arm_bcc_label(as, ASM_ARM_CC_NE, label); \
+    } while (0)
+#define ASM_JUMP_IF_REG_EQ(as, reg1, reg2, label) \
+    do { \
+        asm_arm_cmp_reg_reg(as, reg1, reg2); \
+        asm_arm_bcc_label(as, ASM_ARM_CC_EQ, label); \
+    } while (0)
+#define ASM_CALL_IND(as, ptr, idx) asm_arm_bl_ind(as, ptr, idx, ASM_ARM_REG_R3)
+
+#define ASM_MOV_REG_TO_LOCAL(as, reg, local_num) asm_arm_mov_local_reg(as, (local_num), (reg))
+#define ASM_MOV_IMM_TO_REG(as, imm, reg) asm_arm_mov_reg_i32(as, (reg), (imm))
+#define ASM_MOV_ALIGNED_IMM_TO_REG(as, imm, reg) asm_arm_mov_reg_i32(as, (reg), (imm))
+#define ASM_MOV_IMM_TO_LOCAL_USING(as, imm, local_num, reg_temp) \
+    do { \
+        asm_arm_mov_reg_i32(as, (reg_temp), (imm)); \
+        asm_arm_mov_local_reg(as, (local_num), (reg_temp)); \
+    } while (false)
+#define ASM_MOV_LOCAL_TO_REG(as, local_num, reg) asm_arm_mov_reg_local(as, (reg), (local_num))
+#define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_arm_mov_reg_reg((as), (reg_dest), (reg_src))
+#define ASM_MOV_LOCAL_ADDR_TO_REG(as, local_num, reg) asm_arm_mov_reg_local_addr(as, (reg), (local_num))
+
+#define ASM_LSL_REG_REG(as, reg_dest, reg_shift) asm_arm_lsl_reg_reg((as), (reg_dest), (reg_shift))
+#define ASM_ASR_REG_REG(as, reg_dest, reg_shift) asm_arm_asr_reg_reg((as), (reg_dest), (reg_shift))
+#define ASM_OR_REG_REG(as, reg_dest, reg_src) asm_arm_orr_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src))
+#define ASM_XOR_REG_REG(as, reg_dest, reg_src) asm_arm_eor_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src))
+#define ASM_AND_REG_REG(as, reg_dest, reg_src) asm_arm_and_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src))
+#define ASM_ADD_REG_REG(as, reg_dest, reg_src) asm_arm_add_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src))
+#define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_arm_sub_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src))
+#define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_arm_mul_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src))
+
+#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) asm_arm_ldr_reg_reg((as), (reg_dest), (reg_base), 0)
+#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_arm_ldr_reg_reg((as), (reg_dest), (reg_base), 4 * (word_offset))
+#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_arm_ldrb_reg_reg((as), (reg_dest), (reg_base))
+#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_arm_ldrh_reg_reg((as), (reg_dest), (reg_base))
+#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_arm_ldr_reg_reg((as), (reg_dest), (reg_base), 0)
+
+#define ASM_STORE_REG_REG(as, reg_value, reg_base) asm_arm_str_reg_reg((as), (reg_value), (reg_base), 0)
+#define ASM_STORE_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_arm_str_reg_reg((as), (reg_dest), (reg_base), 4 * (word_offset))
+#define ASM_STORE8_REG_REG(as, reg_value, reg_base) asm_arm_strb_reg_reg((as), (reg_value), (reg_base))
+#define ASM_STORE16_REG_REG(as, reg_value, reg_base) asm_arm_strh_reg_reg((as), (reg_value), (reg_base))
+#define ASM_STORE32_REG_REG(as, reg_value, reg_base) asm_arm_str_reg_reg((as), (reg_value), (reg_base), 0)
+
+#endif // GENERIC_ASM_API
+
+#endif // MICROPY_INCLUDED_PY_ASMARM_H

+ 102 - 0
py/asmbase.c

@@ -0,0 +1,102 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include "py/obj.h"
+#include "py/misc.h"
+#include "py/asmbase.h"
+
+#if MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_ASM
+
+void mp_asm_base_init(mp_asm_base_t *as, size_t max_num_labels) {
+    as->max_num_labels = max_num_labels;
+    as->label_offsets = m_new(size_t, max_num_labels);
+}
+
+void mp_asm_base_deinit(mp_asm_base_t *as, bool free_code) {
+    if (free_code) {
+        MP_PLAT_FREE_EXEC(as->code_base, as->code_size);
+    }
+    m_del(size_t, as->label_offsets, as->max_num_labels);
+}
+
+void mp_asm_base_start_pass(mp_asm_base_t *as, int pass) {
+    if (pass == MP_ASM_PASS_COMPUTE) {
+        // reset all labels
+        memset(as->label_offsets, -1, as->max_num_labels * sizeof(size_t));
+    } else if (pass == MP_ASM_PASS_EMIT) {
+        // allocating executable RAM is platform specific
+        MP_PLAT_ALLOC_EXEC(as->code_offset, (void**)&as->code_base, &as->code_size);
+        assert(as->code_base != NULL);
+    }
+    as->pass = pass;
+    as->code_offset = 0;
+}
+
+// all functions must go through this one to emit bytes
+// if as->pass < MP_ASM_PASS_EMIT, then this function just counts the number
+// of bytes needed and returns NULL, and callers should not store any data
+uint8_t *mp_asm_base_get_cur_to_write_bytes(mp_asm_base_t *as, size_t num_bytes_to_write) {
+    uint8_t *c = NULL;
+    if (as->pass == MP_ASM_PASS_EMIT) {
+        assert(as->code_offset + num_bytes_to_write <= as->code_size);
+        c = as->code_base + as->code_offset;
+    }
+    as->code_offset += num_bytes_to_write;
+    return c;
+}
+
+void mp_asm_base_label_assign(mp_asm_base_t *as, size_t label) {
+    assert(label < as->max_num_labels);
+    if (as->pass < MP_ASM_PASS_EMIT) {
+        // assign label offset
+        assert(as->label_offsets[label] == (size_t)-1);
+        as->label_offsets[label] = as->code_offset;
+    } else {
+        // ensure label offset has not changed from PASS_COMPUTE to PASS_EMIT
+        assert(as->label_offsets[label] == as->code_offset);
+    }
+}
+
+// align must be a multiple of 2
+void mp_asm_base_align(mp_asm_base_t* as, unsigned int align) {
+    as->code_offset = (as->code_offset + align - 1) & (~(align - 1));
+}
+
+// this function assumes a little endian machine
+void mp_asm_base_data(mp_asm_base_t* as, unsigned int bytesize, uintptr_t val) {
+    uint8_t *c = mp_asm_base_get_cur_to_write_bytes(as, bytesize);
+    if (c != NULL) {
+        for (unsigned int i = 0; i < bytesize; i++) {
+            *c++ = val;
+            val >>= 8;
+        }
+    }
+}
+
+#endif // MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_ASM

+ 69 - 0
py/asmbase.h

@@ -0,0 +1,69 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_PY_ASMBASE_H
+#define MICROPY_INCLUDED_PY_ASMBASE_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#define MP_ASM_PASS_COMPUTE (1)
+#define MP_ASM_PASS_EMIT    (2)
+
+typedef struct _mp_asm_base_t {
+    int pass;
+    size_t code_offset;
+    size_t code_size;
+    uint8_t *code_base;
+
+    size_t max_num_labels;
+    size_t *label_offsets;
+} mp_asm_base_t;
+
+void mp_asm_base_init(mp_asm_base_t *as, size_t max_num_labels);
+void mp_asm_base_deinit(mp_asm_base_t *as, bool free_code);
+void mp_asm_base_start_pass(mp_asm_base_t *as, int pass);
+uint8_t *mp_asm_base_get_cur_to_write_bytes(mp_asm_base_t *as, size_t num_bytes_to_write);
+void mp_asm_base_label_assign(mp_asm_base_t *as, size_t label);
+void mp_asm_base_align(mp_asm_base_t* as, unsigned int align);
+void mp_asm_base_data(mp_asm_base_t* as, unsigned int bytesize, uintptr_t val);
+
+static inline size_t mp_asm_base_get_code_pos(mp_asm_base_t *as) {
+    return as->code_offset;
+}
+
+static inline size_t mp_asm_base_get_code_size(mp_asm_base_t *as) {
+    return as->code_size;
+}
+
+static inline void *mp_asm_base_get_code(mp_asm_base_t *as) {
+    #if defined(MP_PLAT_COMMIT_EXEC)
+    return MP_PLAT_COMMIT_EXEC(as->code_base, as->code_size);
+    #else
+    return as->code_base;
+    #endif
+}
+
+#endif // MICROPY_INCLUDED_PY_ASMBASE_H

+ 380 - 0
py/asmthumb.c

@@ -0,0 +1,380 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+#include "py/mpconfig.h"
+
+// wrapper around everything in this file
+#if MICROPY_EMIT_THUMB || MICROPY_EMIT_INLINE_THUMB
+
+#include "py/mphal.h"
+#include "py/asmthumb.h"
+
+#define UNSIGNED_FIT8(x) (((x) & 0xffffff00) == 0)
+#define UNSIGNED_FIT16(x) (((x) & 0xffff0000) == 0)
+#define SIGNED_FIT8(x) (((x) & 0xffffff80) == 0) || (((x) & 0xffffff80) == 0xffffff80)
+#define SIGNED_FIT9(x) (((x) & 0xffffff00) == 0) || (((x) & 0xffffff00) == 0xffffff00)
+#define SIGNED_FIT12(x) (((x) & 0xfffff800) == 0) || (((x) & 0xfffff800) == 0xfffff800)
+#define SIGNED_FIT23(x) (((x) & 0xffc00000) == 0) || (((x) & 0xffc00000) == 0xffc00000)
+
+static inline byte *asm_thumb_get_cur_to_write_bytes(asm_thumb_t *as, int n) {
+    return mp_asm_base_get_cur_to_write_bytes(&as->base, n);
+}
+
+void asm_thumb_end_pass(asm_thumb_t *as) {
+    (void)as;
+    // could check labels are resolved...
+
+    #if defined(MCU_SERIES_F7)
+    if (as->base.pass == MP_ASM_PASS_EMIT) {
+        // flush D-cache, so the code emitted is stored in memory
+        MP_HAL_CLEAN_DCACHE(as->base.code_base, as->base.code_size);
+        // invalidate I-cache
+        SCB_InvalidateICache();
+    }
+    #endif
+}
+
+/*
+STATIC void asm_thumb_write_byte_1(asm_thumb_t *as, byte b1) {
+    byte *c = asm_thumb_get_cur_to_write_bytes(as, 1);
+    c[0] = b1;
+}
+*/
+
+/*
+#define IMM32_L0(x) ((x) & 0xff)
+#define IMM32_L1(x) (((x) >> 8) & 0xff)
+#define IMM32_L2(x) (((x) >> 16) & 0xff)
+#define IMM32_L3(x) (((x) >> 24) & 0xff)
+
+STATIC void asm_thumb_write_word32(asm_thumb_t *as, int w32) {
+    byte *c = asm_thumb_get_cur_to_write_bytes(as, 4);
+    c[0] = IMM32_L0(w32);
+    c[1] = IMM32_L1(w32);
+    c[2] = IMM32_L2(w32);
+    c[3] = IMM32_L3(w32);
+}
+*/
+
+// rlolist is a bit map indicating desired lo-registers
+#define OP_PUSH_RLIST(rlolist)      (0xb400 | (rlolist))
+#define OP_PUSH_RLIST_LR(rlolist)   (0xb400 | 0x0100 | (rlolist))
+#define OP_POP_RLIST(rlolist)       (0xbc00 | (rlolist))
+#define OP_POP_RLIST_PC(rlolist)    (0xbc00 | 0x0100 | (rlolist))
+
+#define OP_ADD_SP(num_words) (0xb000 | (num_words))
+#define OP_SUB_SP(num_words) (0xb080 | (num_words))
+
+// locals:
+//  - stored on the stack in ascending order
+//  - numbered 0 through num_locals-1
+//  - SP points to first local
+//
+//  | SP
+//  v
+//  l0  l1  l2  ...  l(n-1)
+//  ^                ^
+//  | low address    | high address in RAM
+
+void asm_thumb_entry(asm_thumb_t *as, int num_locals) {
+    // work out what to push and how many extra spaces to reserve on stack
+    // so that we have enough for all locals and it's aligned an 8-byte boundary
+    // we push extra regs (r1, r2, r3) to help do the stack adjustment
+    // we probably should just always subtract from sp, since this would be more efficient
+    // for push rlist, lowest numbered register at the lowest address
+    uint reglist;
+    uint stack_adjust;
+    if (num_locals < 0) {
+        num_locals = 0;
+    }
+    // don't pop r0 because it's used for return value
+    switch (num_locals) {
+        case 0:
+            reglist = 0xf2;
+            stack_adjust = 0;
+            break;
+
+        case 1:
+            reglist = 0xf2;
+            stack_adjust = 0;
+            break;
+
+        case 2:
+            reglist = 0xfe;
+            stack_adjust = 0;
+            break;
+
+        case 3:
+            reglist = 0xfe;
+            stack_adjust = 0;
+            break;
+
+        default:
+            reglist = 0xfe;
+            stack_adjust = ((num_locals - 3) + 1) & (~1);
+            break;
+    }
+    asm_thumb_op16(as, OP_PUSH_RLIST_LR(reglist));
+    if (stack_adjust > 0) {
+        asm_thumb_op16(as, OP_SUB_SP(stack_adjust));
+    }
+    as->push_reglist = reglist;
+    as->stack_adjust = stack_adjust;
+}
+
+void asm_thumb_exit(asm_thumb_t *as) {
+    if (as->stack_adjust > 0) {
+        asm_thumb_op16(as, OP_ADD_SP(as->stack_adjust));
+    }
+    asm_thumb_op16(as, OP_POP_RLIST_PC(as->push_reglist));
+}
+
+STATIC mp_uint_t get_label_dest(asm_thumb_t *as, uint label) {
+    assert(label < as->base.max_num_labels);
+    return as->base.label_offsets[label];
+}
+
+void asm_thumb_op16(asm_thumb_t *as, uint op) {
+    byte *c = asm_thumb_get_cur_to_write_bytes(as, 2);
+    if (c != NULL) {
+        // little endian
+        c[0] = op;
+        c[1] = op >> 8;
+    }
+}
+
+void asm_thumb_op32(asm_thumb_t *as, uint op1, uint op2) {
+    byte *c = asm_thumb_get_cur_to_write_bytes(as, 4);
+    if (c != NULL) {
+        // little endian, op1 then op2
+        c[0] = op1;
+        c[1] = op1 >> 8;
+        c[2] = op2;
+        c[3] = op2 >> 8;
+    }
+}
+
+#define OP_FORMAT_4(op, rlo_dest, rlo_src) ((op) | ((rlo_src) << 3) | (rlo_dest))
+
+void asm_thumb_format_4(asm_thumb_t *as, uint op, uint rlo_dest, uint rlo_src) {
+    assert(rlo_dest < ASM_THUMB_REG_R8);
+    assert(rlo_src < ASM_THUMB_REG_R8);
+    asm_thumb_op16(as, OP_FORMAT_4(op, rlo_dest, rlo_src));
+}
+
+void asm_thumb_mov_reg_reg(asm_thumb_t *as, uint reg_dest, uint reg_src) {
+    uint op_lo;
+    if (reg_src < 8) {
+        op_lo = reg_src << 3;
+    } else {
+        op_lo = 0x40 | ((reg_src - 8) << 3);
+    }
+    if (reg_dest < 8) {
+        op_lo |= reg_dest;
+    } else {
+        op_lo |= 0x80 | (reg_dest - 8);
+    }
+    // mov reg_dest, reg_src
+    asm_thumb_op16(as, 0x4600 | op_lo);
+}
+
+// if loading lo half with movw, the i16 value will be zero extended into the r32 register!
+void asm_thumb_mov_reg_i16(asm_thumb_t *as, uint mov_op, uint reg_dest, int i16_src) {
+    assert(reg_dest < ASM_THUMB_REG_R15);
+    // mov[wt] reg_dest, #i16_src
+    asm_thumb_op32(as, mov_op | ((i16_src >> 1) & 0x0400) | ((i16_src >> 12) & 0xf), ((i16_src << 4) & 0x7000) | (reg_dest << 8) | (i16_src & 0xff));
+}
+
+#define OP_B_N(byte_offset) (0xe000 | (((byte_offset) >> 1) & 0x07ff))
+
+bool asm_thumb_b_n_label(asm_thumb_t *as, uint label) {
+    mp_uint_t dest = get_label_dest(as, label);
+    mp_int_t rel = dest - as->base.code_offset;
+    rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction
+    asm_thumb_op16(as, OP_B_N(rel));
+    return as->base.pass != MP_ASM_PASS_EMIT || SIGNED_FIT12(rel);
+}
+
+#define OP_BCC_N(cond, byte_offset) (0xd000 | ((cond) << 8) | (((byte_offset) >> 1) & 0x00ff))
+
+// all these bit arithmetics need coverage testing!
+#define OP_BCC_W_HI(cond, byte_offset) (0xf000 | ((cond) << 6) | (((byte_offset) >> 10) & 0x0400) | (((byte_offset) >> 14) & 0x003f))
+#define OP_BCC_W_LO(byte_offset) (0x8000 | ((byte_offset) & 0x2000) | (((byte_offset) >> 1) & 0x0fff))
+
+bool asm_thumb_bcc_nw_label(asm_thumb_t *as, int cond, uint label, bool wide) {
+    mp_uint_t dest = get_label_dest(as, label);
+    mp_int_t rel = dest - as->base.code_offset;
+    rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction
+    if (!wide) {
+        asm_thumb_op16(as, OP_BCC_N(cond, rel));
+        return as->base.pass != MP_ASM_PASS_EMIT || SIGNED_FIT9(rel);
+    } else {
+        asm_thumb_op32(as, OP_BCC_W_HI(cond, rel), OP_BCC_W_LO(rel));
+        return true;
+    }
+}
+
+#define OP_BL_HI(byte_offset) (0xf000 | (((byte_offset) >> 12) & 0x07ff))
+#define OP_BL_LO(byte_offset) (0xf800 | (((byte_offset) >> 1) & 0x07ff))
+
+bool asm_thumb_bl_label(asm_thumb_t *as, uint label) {
+    mp_uint_t dest = get_label_dest(as, label);
+    mp_int_t rel = dest - as->base.code_offset;
+    rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction
+    asm_thumb_op32(as, OP_BL_HI(rel), OP_BL_LO(rel));
+    return as->base.pass != MP_ASM_PASS_EMIT || SIGNED_FIT23(rel);
+}
+
+void asm_thumb_mov_reg_i32(asm_thumb_t *as, uint reg_dest, mp_uint_t i32) {
+    // movw, movt does it in 8 bytes
+    // ldr [pc, #], dw does it in 6 bytes, but we might not reach to end of code for dw
+
+    asm_thumb_mov_reg_i16(as, ASM_THUMB_OP_MOVW, reg_dest, i32);
+    asm_thumb_mov_reg_i16(as, ASM_THUMB_OP_MOVT, reg_dest, i32 >> 16);
+}
+
+void asm_thumb_mov_reg_i32_optimised(asm_thumb_t *as, uint reg_dest, int i32) {
+    if (reg_dest < 8 && UNSIGNED_FIT8(i32)) {
+        asm_thumb_mov_rlo_i8(as, reg_dest, i32);
+    } else if (UNSIGNED_FIT16(i32)) {
+        asm_thumb_mov_reg_i16(as, ASM_THUMB_OP_MOVW, reg_dest, i32);
+    } else {
+        asm_thumb_mov_reg_i32(as, reg_dest, i32);
+    }
+}
+
+// i32 is stored as a full word in the code, and aligned to machine-word boundary
+// TODO this is very inefficient, improve it!
+void asm_thumb_mov_reg_i32_aligned(asm_thumb_t *as, uint reg_dest, int i32) {
+    // align on machine-word + 2
+    if ((as->base.code_offset & 3) == 0) {
+        asm_thumb_op16(as, ASM_THUMB_OP_NOP);
+    }
+    // jump over the i32 value (instruction prefetch adds 2 to PC)
+    asm_thumb_op16(as, OP_B_N(2));
+    // store i32 on machine-word aligned boundary
+    mp_asm_base_data(&as->base, 4, i32);
+    // do the actual load of the i32 value
+    asm_thumb_mov_reg_i32_optimised(as, reg_dest, i32);
+}
+
+#define OP_STR_TO_SP_OFFSET(rlo_dest, word_offset) (0x9000 | ((rlo_dest) << 8) | ((word_offset) & 0x00ff))
+#define OP_LDR_FROM_SP_OFFSET(rlo_dest, word_offset) (0x9800 | ((rlo_dest) << 8) | ((word_offset) & 0x00ff))
+
+void asm_thumb_mov_local_reg(asm_thumb_t *as, int local_num, uint rlo_src) {
+    assert(rlo_src < ASM_THUMB_REG_R8);
+    int word_offset = local_num;
+    assert(as->base.pass < MP_ASM_PASS_EMIT || word_offset >= 0);
+    asm_thumb_op16(as, OP_STR_TO_SP_OFFSET(rlo_src, word_offset));
+}
+
+void asm_thumb_mov_reg_local(asm_thumb_t *as, uint rlo_dest, int local_num) {
+    assert(rlo_dest < ASM_THUMB_REG_R8);
+    int word_offset = local_num;
+    assert(as->base.pass < MP_ASM_PASS_EMIT || word_offset >= 0);
+    asm_thumb_op16(as, OP_LDR_FROM_SP_OFFSET(rlo_dest, word_offset));
+}
+
+#define OP_ADD_REG_SP_OFFSET(rlo_dest, word_offset) (0xa800 | ((rlo_dest) << 8) | ((word_offset) & 0x00ff))
+
+void asm_thumb_mov_reg_local_addr(asm_thumb_t *as, uint rlo_dest, int local_num) {
+    assert(rlo_dest < ASM_THUMB_REG_R8);
+    int word_offset = local_num;
+    assert(as->base.pass < MP_ASM_PASS_EMIT || word_offset >= 0);
+    asm_thumb_op16(as, OP_ADD_REG_SP_OFFSET(rlo_dest, word_offset));
+}
+
+// this could be wrong, because it should have a range of +/- 16MiB...
+#define OP_BW_HI(byte_offset) (0xf000 | (((byte_offset) >> 12) & 0x07ff))
+#define OP_BW_LO(byte_offset) (0xb800 | (((byte_offset) >> 1) & 0x07ff))
+
+void asm_thumb_b_label(asm_thumb_t *as, uint label) {
+    mp_uint_t dest = get_label_dest(as, label);
+    mp_int_t rel = dest - as->base.code_offset;
+    rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction
+    if (dest != (mp_uint_t)-1 && rel <= -4) {
+        // is a backwards jump, so we know the size of the jump on the first pass
+        // calculate rel assuming 12 bit relative jump
+        if (SIGNED_FIT12(rel)) {
+            asm_thumb_op16(as, OP_B_N(rel));
+        } else {
+            goto large_jump;
+        }
+    } else {
+        // is a forwards jump, so need to assume it's large
+        large_jump:
+        asm_thumb_op32(as, OP_BW_HI(rel), OP_BW_LO(rel));
+    }
+}
+
+void asm_thumb_bcc_label(asm_thumb_t *as, int cond, uint label) {
+    mp_uint_t dest = get_label_dest(as, label);
+    mp_int_t rel = dest - as->base.code_offset;
+    rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction
+    if (dest != (mp_uint_t)-1 && rel <= -4) {
+        // is a backwards jump, so we know the size of the jump on the first pass
+        // calculate rel assuming 9 bit relative jump
+        if (SIGNED_FIT9(rel)) {
+            asm_thumb_op16(as, OP_BCC_N(cond, rel));
+        } else {
+            goto large_jump;
+        }
+    } else {
+        // is a forwards jump, so need to assume it's large
+        large_jump:
+        asm_thumb_op32(as, OP_BCC_W_HI(cond, rel), OP_BCC_W_LO(rel));
+    }
+}
+
+#define OP_BLX(reg) (0x4780 | ((reg) << 3))
+#define OP_SVC(arg) (0xdf00 | (arg))
+
+void asm_thumb_bl_ind(asm_thumb_t *as, void *fun_ptr, uint fun_id, uint reg_temp) {
+    /* TODO make this use less bytes
+    uint rlo_base = ASM_THUMB_REG_R3;
+    uint rlo_dest = ASM_THUMB_REG_R7;
+    uint word_offset = 4;
+    asm_thumb_op16(as, 0x0000);
+    asm_thumb_op16(as, 0x6800 | (word_offset << 6) | (rlo_base << 3) | rlo_dest); // ldr rlo_dest, [rlo_base, #offset]
+    asm_thumb_op16(as, 0x4780 | (ASM_THUMB_REG_R9 << 3)); // blx reg
+    */
+
+    if (fun_id < 32) {
+        // load ptr to function from table, indexed by fun_id (must be in range 0-31); 4 bytes
+        asm_thumb_op16(as, ASM_THUMB_FORMAT_9_10_ENCODE(ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_WORD_TRANSFER, reg_temp, ASM_THUMB_REG_R7, fun_id));
+        asm_thumb_op16(as, OP_BLX(reg_temp));
+    } else {
+        // load ptr to function into register using immediate; 6 bytes
+        asm_thumb_mov_reg_i32(as, reg_temp, (mp_uint_t)fun_ptr);
+        asm_thumb_op16(as, OP_BLX(reg_temp));
+    }
+}
+
+#endif // MICROPY_EMIT_THUMB || MICROPY_EMIT_INLINE_THUMB

+ 321 - 0
py/asmthumb.h

@@ -0,0 +1,321 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_PY_ASMTHUMB_H
+#define MICROPY_INCLUDED_PY_ASMTHUMB_H
+
+#include "py/misc.h"
+#include "py/asmbase.h"
+
+#define ASM_THUMB_REG_R0  (0)
+#define ASM_THUMB_REG_R1  (1)
+#define ASM_THUMB_REG_R2  (2)
+#define ASM_THUMB_REG_R3  (3)
+#define ASM_THUMB_REG_R4  (4)
+#define ASM_THUMB_REG_R5  (5)
+#define ASM_THUMB_REG_R6  (6)
+#define ASM_THUMB_REG_R7  (7)
+#define ASM_THUMB_REG_R8  (8)
+#define ASM_THUMB_REG_R9  (9)
+#define ASM_THUMB_REG_R10 (10)
+#define ASM_THUMB_REG_R11 (11)
+#define ASM_THUMB_REG_R12 (12)
+#define ASM_THUMB_REG_R13 (13)
+#define ASM_THUMB_REG_R14 (14)
+#define ASM_THUMB_REG_R15 (15)
+#define ASM_THUMB_REG_LR  (REG_R14)
+
+#define ASM_THUMB_CC_EQ (0x0)
+#define ASM_THUMB_CC_NE (0x1)
+#define ASM_THUMB_CC_CS (0x2)
+#define ASM_THUMB_CC_CC (0x3)
+#define ASM_THUMB_CC_MI (0x4)
+#define ASM_THUMB_CC_PL (0x5)
+#define ASM_THUMB_CC_VS (0x6)
+#define ASM_THUMB_CC_VC (0x7)
+#define ASM_THUMB_CC_HI (0x8)
+#define ASM_THUMB_CC_LS (0x9)
+#define ASM_THUMB_CC_GE (0xa)
+#define ASM_THUMB_CC_LT (0xb)
+#define ASM_THUMB_CC_GT (0xc)
+#define ASM_THUMB_CC_LE (0xd)
+
+typedef struct _asm_thumb_t {
+    mp_asm_base_t base;
+    uint32_t push_reglist;
+    uint32_t stack_adjust;
+} asm_thumb_t;
+
+void asm_thumb_end_pass(asm_thumb_t *as);
+
+void asm_thumb_entry(asm_thumb_t *as, int num_locals);
+void asm_thumb_exit(asm_thumb_t *as);
+
+// argument order follows ARM, in general dest is first
+// note there is a difference between movw and mov.w, and many others!
+
+#define ASM_THUMB_OP_IT (0xbf00)
+#define ASM_THUMB_OP_ITE_EQ (0xbf0c)
+#define ASM_THUMB_OP_ITE_CS (0xbf2c)
+#define ASM_THUMB_OP_ITE_MI (0xbf4c)
+#define ASM_THUMB_OP_ITE_VS (0xbf6c)
+#define ASM_THUMB_OP_ITE_HI (0xbf8c)
+#define ASM_THUMB_OP_ITE_GE (0xbfac)
+#define ASM_THUMB_OP_ITE_GT (0xbfcc)
+
+#define ASM_THUMB_OP_NOP        (0xbf00)
+#define ASM_THUMB_OP_WFI        (0xbf30)
+#define ASM_THUMB_OP_CPSID_I    (0xb672) // cpsid i, disable irq
+#define ASM_THUMB_OP_CPSIE_I    (0xb662) // cpsie i, enable irq
+
+void asm_thumb_op16(asm_thumb_t *as, uint op);
+void asm_thumb_op32(asm_thumb_t *as, uint op1, uint op2);
+
+static inline void asm_thumb_it_cc(asm_thumb_t *as, uint cc, uint mask)
+    { asm_thumb_op16(as, ASM_THUMB_OP_IT | (cc << 4) | mask); }
+
+// FORMAT 1: move shifted register
+
+#define ASM_THUMB_FORMAT_1_LSL (0x0000)
+#define ASM_THUMB_FORMAT_1_LSR (0x0800)
+#define ASM_THUMB_FORMAT_1_ASR (0x1000)
+
+#define ASM_THUMB_FORMAT_1_ENCODE(op, rlo_dest, rlo_src, offset) \
+    ((op) | ((offset) << 6) | ((rlo_src) << 3) | (rlo_dest))
+
+static inline void asm_thumb_format_1(asm_thumb_t *as, uint op, uint rlo_dest, uint rlo_src, uint offset) {
+    assert(rlo_dest < ASM_THUMB_REG_R8);
+    assert(rlo_src < ASM_THUMB_REG_R8);
+    asm_thumb_op16(as, ASM_THUMB_FORMAT_1_ENCODE(op, rlo_dest, rlo_src, offset));
+}
+
+// FORMAT 2: add/subtract
+
+#define ASM_THUMB_FORMAT_2_ADD (0x1800)
+#define ASM_THUMB_FORMAT_2_SUB (0x1a00)
+#define ASM_THUMB_FORMAT_2_REG_OPERAND (0x0000)
+#define ASM_THUMB_FORMAT_2_IMM_OPERAND (0x0400)
+
+#define ASM_THUMB_FORMAT_2_ENCODE(op, rlo_dest, rlo_src, src_b) \
+    ((op) | ((src_b) << 6) | ((rlo_src) << 3) | (rlo_dest))
+
+static inline void asm_thumb_format_2(asm_thumb_t *as, uint op, uint rlo_dest, uint rlo_src, int src_b) {
+    assert(rlo_dest < ASM_THUMB_REG_R8);
+    assert(rlo_src < ASM_THUMB_REG_R8);
+    asm_thumb_op16(as, ASM_THUMB_FORMAT_2_ENCODE(op, rlo_dest, rlo_src, src_b));
+}
+
+static inline void asm_thumb_add_rlo_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_src_a, uint rlo_src_b)
+    { asm_thumb_format_2(as, ASM_THUMB_FORMAT_2_ADD | ASM_THUMB_FORMAT_2_REG_OPERAND, rlo_dest, rlo_src_a, rlo_src_b); }
+static inline void asm_thumb_add_rlo_rlo_i3(asm_thumb_t *as, uint rlo_dest, uint rlo_src_a, int i3_src)
+    { asm_thumb_format_2(as, ASM_THUMB_FORMAT_2_ADD | ASM_THUMB_FORMAT_2_IMM_OPERAND, rlo_dest, rlo_src_a, i3_src); }
+static inline void asm_thumb_sub_rlo_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_src_a, uint rlo_src_b)
+    { asm_thumb_format_2(as, ASM_THUMB_FORMAT_2_SUB | ASM_THUMB_FORMAT_2_REG_OPERAND, rlo_dest, rlo_src_a, rlo_src_b); }
+static inline void asm_thumb_sub_rlo_rlo_i3(asm_thumb_t *as, uint rlo_dest, uint rlo_src_a, int i3_src)
+    { asm_thumb_format_2(as, ASM_THUMB_FORMAT_2_SUB | ASM_THUMB_FORMAT_2_IMM_OPERAND, rlo_dest, rlo_src_a, i3_src); }
+
+// FORMAT 3: move/compare/add/subtract immediate
+// These instructions all do zero extension of the i8 value
+
+#define ASM_THUMB_FORMAT_3_MOV (0x2000)
+#define ASM_THUMB_FORMAT_3_CMP (0x2800)
+#define ASM_THUMB_FORMAT_3_ADD (0x3000)
+#define ASM_THUMB_FORMAT_3_SUB (0x3800)
+
+#define ASM_THUMB_FORMAT_3_ENCODE(op, rlo, i8) ((op) | ((rlo) << 8) | (i8))
+
+static inline void asm_thumb_format_3(asm_thumb_t *as, uint op, uint rlo, int i8) {
+    assert(rlo < ASM_THUMB_REG_R8);
+    asm_thumb_op16(as, ASM_THUMB_FORMAT_3_ENCODE(op, rlo, i8));
+}
+
+static inline void asm_thumb_mov_rlo_i8(asm_thumb_t *as, uint rlo, int i8) { asm_thumb_format_3(as, ASM_THUMB_FORMAT_3_MOV, rlo, i8); }
+static inline void asm_thumb_cmp_rlo_i8(asm_thumb_t *as, uint rlo, int i8) { asm_thumb_format_3(as, ASM_THUMB_FORMAT_3_CMP, rlo, i8); }
+static inline void asm_thumb_add_rlo_i8(asm_thumb_t *as, uint rlo, int i8) { asm_thumb_format_3(as, ASM_THUMB_FORMAT_3_ADD, rlo, i8); }
+static inline void asm_thumb_sub_rlo_i8(asm_thumb_t *as, uint rlo, int i8) { asm_thumb_format_3(as, ASM_THUMB_FORMAT_3_SUB, rlo, i8); }
+
+// FORMAT 4: ALU operations
+
+#define ASM_THUMB_FORMAT_4_AND (0x4000)
+#define ASM_THUMB_FORMAT_4_EOR (0x4040)
+#define ASM_THUMB_FORMAT_4_LSL (0x4080)
+#define ASM_THUMB_FORMAT_4_LSR (0x40c0)
+#define ASM_THUMB_FORMAT_4_ASR (0x4100)
+#define ASM_THUMB_FORMAT_4_ADC (0x4140)
+#define ASM_THUMB_FORMAT_4_SBC (0x4180)
+#define ASM_THUMB_FORMAT_4_ROR (0x41c0)
+#define ASM_THUMB_FORMAT_4_TST (0x4200)
+#define ASM_THUMB_FORMAT_4_NEG (0x4240)
+#define ASM_THUMB_FORMAT_4_CMP (0x4280)
+#define ASM_THUMB_FORMAT_4_CMN (0x42c0)
+#define ASM_THUMB_FORMAT_4_ORR (0x4300)
+#define ASM_THUMB_FORMAT_4_MUL (0x4340)
+#define ASM_THUMB_FORMAT_4_BIC (0x4380)
+#define ASM_THUMB_FORMAT_4_MVN (0x43c0)
+
+void asm_thumb_format_4(asm_thumb_t *as, uint op, uint rlo_dest, uint rlo_src);
+
+static inline void asm_thumb_cmp_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_src) { asm_thumb_format_4(as, ASM_THUMB_FORMAT_4_CMP, rlo_dest, rlo_src); }
+
+// FORMAT 9: load/store with immediate offset
+// For word transfers the offset must be aligned, and >>2
+
+// FORMAT 10: load/store halfword
+// The offset must be aligned, and >>1
+// The load is zero extended into the register
+
+#define ASM_THUMB_FORMAT_9_STR (0x6000)
+#define ASM_THUMB_FORMAT_9_LDR (0x6800)
+#define ASM_THUMB_FORMAT_9_WORD_TRANSFER (0x0000)
+#define ASM_THUMB_FORMAT_9_BYTE_TRANSFER (0x1000)
+
+#define ASM_THUMB_FORMAT_10_STRH (0x8000)
+#define ASM_THUMB_FORMAT_10_LDRH (0x8800)
+
+#define ASM_THUMB_FORMAT_9_10_ENCODE(op, rlo_dest, rlo_base, offset) \
+    ((op) | (((offset) << 6) & 0x07c0) | ((rlo_base) << 3) | (rlo_dest))
+
+static inline void asm_thumb_format_9_10(asm_thumb_t *as, uint op, uint rlo_dest, uint rlo_base, uint offset)
+    { asm_thumb_op16(as, ASM_THUMB_FORMAT_9_10_ENCODE(op, rlo_dest, rlo_base, offset)); }
+
+static inline void asm_thumb_str_rlo_rlo_i5(asm_thumb_t *as, uint rlo_src, uint rlo_base, uint word_offset)
+    { asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_9_STR | ASM_THUMB_FORMAT_9_WORD_TRANSFER, rlo_src, rlo_base, word_offset); }
+static inline void asm_thumb_strb_rlo_rlo_i5(asm_thumb_t *as, uint rlo_src, uint rlo_base, uint byte_offset)
+    { asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_9_STR | ASM_THUMB_FORMAT_9_BYTE_TRANSFER, rlo_src, rlo_base, byte_offset); }
+static inline void asm_thumb_strh_rlo_rlo_i5(asm_thumb_t *as, uint rlo_src, uint rlo_base, uint byte_offset)
+    { asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_10_STRH, rlo_src, rlo_base, byte_offset); }
+static inline void asm_thumb_ldr_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uint rlo_base, uint word_offset)
+    { asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_WORD_TRANSFER, rlo_dest, rlo_base, word_offset); }
+static inline void asm_thumb_ldrb_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uint rlo_base, uint byte_offset)
+    { asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_BYTE_TRANSFER , rlo_dest, rlo_base, byte_offset); }
+static inline void asm_thumb_ldrh_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uint rlo_base, uint byte_offset)
+    { asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_10_LDRH, rlo_dest, rlo_base, byte_offset); }
+
+// TODO convert these to above format style
+
+#define ASM_THUMB_OP_MOVW (0xf240)
+#define ASM_THUMB_OP_MOVT (0xf2c0)
+
+void asm_thumb_mov_reg_reg(asm_thumb_t *as, uint reg_dest, uint reg_src);
+void asm_thumb_mov_reg_i16(asm_thumb_t *as, uint mov_op, uint reg_dest, int i16_src);
+
+// these return true if the destination is in range, false otherwise
+bool asm_thumb_b_n_label(asm_thumb_t *as, uint label);
+bool asm_thumb_bcc_nw_label(asm_thumb_t *as, int cond, uint label, bool wide);
+bool asm_thumb_bl_label(asm_thumb_t *as, uint label);
+
+void asm_thumb_mov_reg_i32(asm_thumb_t *as, uint reg_dest, mp_uint_t i32_src); // convenience
+void asm_thumb_mov_reg_i32_optimised(asm_thumb_t *as, uint reg_dest, int i32_src); // convenience
+void asm_thumb_mov_reg_i32_aligned(asm_thumb_t *as, uint reg_dest, int i32); // convenience
+void asm_thumb_mov_local_reg(asm_thumb_t *as, int local_num_dest, uint rlo_src); // convenience
+void asm_thumb_mov_reg_local(asm_thumb_t *as, uint rlo_dest, int local_num); // convenience
+void asm_thumb_mov_reg_local_addr(asm_thumb_t *as, uint rlo_dest, int local_num); // convenience
+
+void asm_thumb_b_label(asm_thumb_t *as, uint label); // convenience: picks narrow or wide branch
+void asm_thumb_bcc_label(asm_thumb_t *as, int cc, uint label); // convenience: picks narrow or wide branch
+void asm_thumb_bl_ind(asm_thumb_t *as, void *fun_ptr, uint fun_id, uint reg_temp); // convenience
+
+#if GENERIC_ASM_API
+
+// The following macros provide a (mostly) arch-independent API to
+// generate native code, and are used by the native emitter.
+
+#define ASM_WORD_SIZE (4)
+
+#define REG_RET ASM_THUMB_REG_R0
+#define REG_ARG_1 ASM_THUMB_REG_R0
+#define REG_ARG_2 ASM_THUMB_REG_R1
+#define REG_ARG_3 ASM_THUMB_REG_R2
+#define REG_ARG_4 ASM_THUMB_REG_R3
+// rest of args go on stack
+
+#define REG_TEMP0 ASM_THUMB_REG_R0
+#define REG_TEMP1 ASM_THUMB_REG_R1
+#define REG_TEMP2 ASM_THUMB_REG_R2
+
+#define REG_LOCAL_1 ASM_THUMB_REG_R4
+#define REG_LOCAL_2 ASM_THUMB_REG_R5
+#define REG_LOCAL_3 ASM_THUMB_REG_R6
+#define REG_LOCAL_NUM (3)
+
+#define ASM_T               asm_thumb_t
+#define ASM_END_PASS        asm_thumb_end_pass
+#define ASM_ENTRY           asm_thumb_entry
+#define ASM_EXIT            asm_thumb_exit
+
+#define ASM_JUMP            asm_thumb_b_label
+#define ASM_JUMP_IF_REG_ZERO(as, reg, label) \
+    do { \
+        asm_thumb_cmp_rlo_i8(as, reg, 0); \
+        asm_thumb_bcc_label(as, ASM_THUMB_CC_EQ, label); \
+    } while (0)
+#define ASM_JUMP_IF_REG_NONZERO(as, reg, label) \
+    do { \
+        asm_thumb_cmp_rlo_i8(as, reg, 0); \
+        asm_thumb_bcc_label(as, ASM_THUMB_CC_NE, label); \
+    } while (0)
+#define ASM_JUMP_IF_REG_EQ(as, reg1, reg2, label) \
+    do { \
+        asm_thumb_cmp_rlo_rlo(as, reg1, reg2); \
+        asm_thumb_bcc_label(as, ASM_THUMB_CC_EQ, label); \
+    } while (0)
+#define ASM_CALL_IND(as, ptr, idx) asm_thumb_bl_ind(as, ptr, idx, ASM_THUMB_REG_R3)
+
+#define ASM_MOV_REG_TO_LOCAL(as, reg, local_num) asm_thumb_mov_local_reg(as, (local_num), (reg))
+#define ASM_MOV_IMM_TO_REG(as, imm, reg) asm_thumb_mov_reg_i32_optimised(as, (reg), (imm))
+#define ASM_MOV_ALIGNED_IMM_TO_REG(as, imm, reg) asm_thumb_mov_reg_i32_aligned(as, (reg), (imm))
+#define ASM_MOV_IMM_TO_LOCAL_USING(as, imm, local_num, reg_temp) \
+    do { \
+        asm_thumb_mov_reg_i32_optimised(as, (reg_temp), (imm)); \
+        asm_thumb_mov_local_reg(as, (local_num), (reg_temp)); \
+    } while (false)
+#define ASM_MOV_LOCAL_TO_REG(as, local_num, reg) asm_thumb_mov_reg_local(as, (reg), (local_num))
+#define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_thumb_mov_reg_reg((as), (reg_dest), (reg_src))
+#define ASM_MOV_LOCAL_ADDR_TO_REG(as, local_num, reg) asm_thumb_mov_reg_local_addr(as, (reg), (local_num))
+
+#define ASM_LSL_REG_REG(as, reg_dest, reg_shift) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_LSL, (reg_dest), (reg_shift))
+#define ASM_ASR_REG_REG(as, reg_dest, reg_shift) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_ASR, (reg_dest), (reg_shift))
+#define ASM_OR_REG_REG(as, reg_dest, reg_src) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_ORR, (reg_dest), (reg_src))
+#define ASM_XOR_REG_REG(as, reg_dest, reg_src) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_EOR, (reg_dest), (reg_src))
+#define ASM_AND_REG_REG(as, reg_dest, reg_src) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_AND, (reg_dest), (reg_src))
+#define ASM_ADD_REG_REG(as, reg_dest, reg_src) asm_thumb_add_rlo_rlo_rlo((as), (reg_dest), (reg_dest), (reg_src))
+#define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_thumb_sub_rlo_rlo_rlo((as), (reg_dest), (reg_dest), (reg_src))
+#define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_MUL, (reg_dest), (reg_src))
+
+#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) asm_thumb_ldr_rlo_rlo_i5((as), (reg_dest), (reg_base), 0)
+#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_thumb_ldr_rlo_rlo_i5((as), (reg_dest), (reg_base), (word_offset))
+#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_thumb_ldrb_rlo_rlo_i5((as), (reg_dest), (reg_base), 0)
+#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_thumb_ldrh_rlo_rlo_i5((as), (reg_dest), (reg_base), 0)
+#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_thumb_ldr_rlo_rlo_i5((as), (reg_dest), (reg_base), 0)
+
+#define ASM_STORE_REG_REG(as, reg_src, reg_base) asm_thumb_str_rlo_rlo_i5((as), (reg_src), (reg_base), 0)
+#define ASM_STORE_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) asm_thumb_str_rlo_rlo_i5((as), (reg_src), (reg_base), (word_offset))
+#define ASM_STORE8_REG_REG(as, reg_src, reg_base) asm_thumb_strb_rlo_rlo_i5((as), (reg_src), (reg_base), 0)
+#define ASM_STORE16_REG_REG(as, reg_src, reg_base) asm_thumb_strh_rlo_rlo_i5((as), (reg_src), (reg_base), 0)
+#define ASM_STORE32_REG_REG(as, reg_src, reg_base) asm_thumb_str_rlo_rlo_i5((as), (reg_src), (reg_base), 0)
+
+#endif // GENERIC_ASM_API
+
+#endif // MICROPY_INCLUDED_PY_ASMTHUMB_H

+ 632 - 0
py/asmx64.c

@@ -0,0 +1,632 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+#include "py/mpconfig.h"
+
+// wrapper around everything in this file
+#if MICROPY_EMIT_X64
+
+#include "py/asmx64.h"
+
+/* all offsets are measured in multiples of 8 bytes */
+#define WORD_SIZE                (8)
+
+#define OPCODE_NOP               (0x90)
+#define OPCODE_PUSH_R64          (0x50) /* +rq */
+#define OPCODE_PUSH_I64          (0x68)
+#define OPCODE_PUSH_M64          (0xff) /* /6 */
+#define OPCODE_POP_R64           (0x58) /* +rq */
+#define OPCODE_RET               (0xc3)
+#define OPCODE_MOV_I8_TO_R8      (0xb0) /* +rb */
+#define OPCODE_MOV_I64_TO_R64    (0xb8) /* +rq */
+#define OPCODE_MOV_I32_TO_RM32   (0xc7)
+#define OPCODE_MOV_R8_TO_RM8     (0x88) /* /r */
+#define OPCODE_MOV_R64_TO_RM64   (0x89) /* /r */
+#define OPCODE_MOV_RM64_TO_R64   (0x8b) /* /r */
+#define OPCODE_MOVZX_RM8_TO_R64  (0xb6) /* 0x0f 0xb6/r */
+#define OPCODE_MOVZX_RM16_TO_R64 (0xb7) /* 0x0f 0xb7/r */
+#define OPCODE_LEA_MEM_TO_R64    (0x8d) /* /r */
+#define OPCODE_AND_R64_TO_RM64   (0x21) /* /r */
+#define OPCODE_OR_R64_TO_RM64    (0x09) /* /r */
+#define OPCODE_XOR_R64_TO_RM64   (0x31) /* /r */
+#define OPCODE_ADD_R64_TO_RM64   (0x01) /* /r */
+#define OPCODE_ADD_I32_TO_RM32   (0x81) /* /0 */
+#define OPCODE_ADD_I8_TO_RM32    (0x83) /* /0 */
+#define OPCODE_SUB_R64_FROM_RM64 (0x29)
+#define OPCODE_SUB_I32_FROM_RM64 (0x81) /* /5 */
+#define OPCODE_SUB_I8_FROM_RM64  (0x83) /* /5 */
+//#define OPCODE_SHL_RM32_BY_I8    (0xc1) /* /4 */
+//#define OPCODE_SHR_RM32_BY_I8    (0xc1) /* /5 */
+//#define OPCODE_SAR_RM32_BY_I8    (0xc1) /* /7 */
+#define OPCODE_SHL_RM64_CL       (0xd3) /* /4 */
+#define OPCODE_SAR_RM64_CL       (0xd3) /* /7 */
+//#define OPCODE_CMP_I32_WITH_RM32 (0x81) /* /7 */
+//#define OPCODE_CMP_I8_WITH_RM32  (0x83) /* /7 */
+#define OPCODE_CMP_R64_WITH_RM64 (0x39) /* /r */
+//#define OPCODE_CMP_RM32_WITH_R32 (0x3b)
+#define OPCODE_TEST_R8_WITH_RM8  (0x84) /* /r */
+#define OPCODE_JMP_REL8          (0xeb)
+#define OPCODE_JMP_REL32         (0xe9)
+#define OPCODE_JCC_REL8          (0x70) /* | jcc type */
+#define OPCODE_JCC_REL32_A       (0x0f)
+#define OPCODE_JCC_REL32_B       (0x80) /* | jcc type */
+#define OPCODE_SETCC_RM8_A       (0x0f)
+#define OPCODE_SETCC_RM8_B       (0x90) /* | jcc type, /0 */
+#define OPCODE_CALL_REL32        (0xe8)
+#define OPCODE_CALL_RM32         (0xff) /* /2 */
+#define OPCODE_LEAVE             (0xc9)
+
+#define MODRM_R64(x)    (((x) & 0x7) << 3)
+#define MODRM_RM_DISP0  (0x00)
+#define MODRM_RM_DISP8  (0x40)
+#define MODRM_RM_DISP32 (0x80)
+#define MODRM_RM_REG    (0xc0)
+#define MODRM_RM_R64(x) ((x) & 0x7)
+
+#define OP_SIZE_PREFIX (0x66)
+
+#define REX_PREFIX  (0x40)
+#define REX_W       (0x08)  // width
+#define REX_R       (0x04)  // register
+#define REX_X       (0x02)  // index
+#define REX_B       (0x01)  // base
+#define REX_W_FROM_R64(r64) ((r64) >> 0 & 0x08)
+#define REX_R_FROM_R64(r64) ((r64) >> 1 & 0x04)
+#define REX_X_FROM_R64(r64) ((r64) >> 2 & 0x02)
+#define REX_B_FROM_R64(r64) ((r64) >> 3 & 0x01)
+
+#define IMM32_L0(x) ((x) & 0xff)
+#define IMM32_L1(x) (((x) >> 8) & 0xff)
+#define IMM32_L2(x) (((x) >> 16) & 0xff)
+#define IMM32_L3(x) (((x) >> 24) & 0xff)
+#define IMM64_L4(x) (((x) >> 32) & 0xff)
+#define IMM64_L5(x) (((x) >> 40) & 0xff)
+#define IMM64_L6(x) (((x) >> 48) & 0xff)
+#define IMM64_L7(x) (((x) >> 56) & 0xff)
+
+#define UNSIGNED_FIT8(x) (((x) & 0xffffffffffffff00) == 0)
+#define UNSIGNED_FIT32(x) (((x) & 0xffffffff00000000) == 0)
+#define SIGNED_FIT8(x) (((x) & 0xffffff80) == 0) || (((x) & 0xffffff80) == 0xffffff80)
+
+static inline byte *asm_x64_get_cur_to_write_bytes(asm_x64_t *as, int n) {
+    return mp_asm_base_get_cur_to_write_bytes(&as->base, n);
+}
+
+STATIC void asm_x64_write_byte_1(asm_x64_t *as, byte b1) {
+    byte* c = asm_x64_get_cur_to_write_bytes(as, 1);
+    if (c != NULL) {
+        c[0] = b1;
+    }
+}
+
+STATIC void asm_x64_write_byte_2(asm_x64_t *as, byte b1, byte b2) {
+    byte* c = asm_x64_get_cur_to_write_bytes(as, 2);
+    if (c != NULL) {
+        c[0] = b1;
+        c[1] = b2;
+    }
+}
+
+STATIC void asm_x64_write_byte_3(asm_x64_t *as, byte b1, byte b2, byte b3) {
+    byte* c = asm_x64_get_cur_to_write_bytes(as, 3);
+    if (c != NULL) {
+        c[0] = b1;
+        c[1] = b2;
+        c[2] = b3;
+    }
+}
+
+STATIC void asm_x64_write_word32(asm_x64_t *as, int w32) {
+    byte* c = asm_x64_get_cur_to_write_bytes(as, 4);
+    if (c != NULL) {
+        c[0] = IMM32_L0(w32);
+        c[1] = IMM32_L1(w32);
+        c[2] = IMM32_L2(w32);
+        c[3] = IMM32_L3(w32);
+    }
+}
+
+STATIC void asm_x64_write_word64(asm_x64_t *as, int64_t w64) {
+    byte* c = asm_x64_get_cur_to_write_bytes(as, 8);
+    if (c != NULL) {
+        c[0] = IMM32_L0(w64);
+        c[1] = IMM32_L1(w64);
+        c[2] = IMM32_L2(w64);
+        c[3] = IMM32_L3(w64);
+        c[4] = IMM64_L4(w64);
+        c[5] = IMM64_L5(w64);
+        c[6] = IMM64_L6(w64);
+        c[7] = IMM64_L7(w64);
+    }
+}
+
+/* unused
+STATIC void asm_x64_write_word32_to(asm_x64_t *as, int offset, int w32) {
+    byte* c;
+    assert(offset + 4 <= as->code_size);
+    c = as->code_base + offset;
+    c[0] = IMM32_L0(w32);
+    c[1] = IMM32_L1(w32);
+    c[2] = IMM32_L2(w32);
+    c[3] = IMM32_L3(w32);
+}
+*/
+
+STATIC void asm_x64_write_r64_disp(asm_x64_t *as, int r64, int disp_r64, int disp_offset) {
+    assert(disp_r64 != ASM_X64_REG_RSP);
+
+    if (disp_r64 == ASM_X64_REG_R12) {
+        // special case for r12; not fully implemented
+        assert(SIGNED_FIT8(disp_offset));
+        asm_x64_write_byte_3(as, MODRM_R64(r64) | MODRM_RM_DISP8 | MODRM_RM_R64(disp_r64), 0x24, IMM32_L0(disp_offset));
+        return;
+    }
+
+    if (disp_offset == 0 && disp_r64 != ASM_X64_REG_RBP) {
+        asm_x64_write_byte_1(as, MODRM_R64(r64) | MODRM_RM_DISP0 | MODRM_RM_R64(disp_r64));
+    } else if (SIGNED_FIT8(disp_offset)) {
+        asm_x64_write_byte_2(as, MODRM_R64(r64) | MODRM_RM_DISP8 | MODRM_RM_R64(disp_r64), IMM32_L0(disp_offset));
+    } else {
+        asm_x64_write_byte_1(as, MODRM_R64(r64) | MODRM_RM_DISP32 | MODRM_RM_R64(disp_r64));
+        asm_x64_write_word32(as, disp_offset);
+    }
+}
+
+STATIC void asm_x64_generic_r64_r64(asm_x64_t *as, int dest_r64, int src_r64, int op) {
+    asm_x64_write_byte_3(as, REX_PREFIX | REX_W | REX_R_FROM_R64(src_r64) | REX_B_FROM_R64(dest_r64), op, MODRM_R64(src_r64) | MODRM_RM_REG | MODRM_RM_R64(dest_r64));
+}
+
+void asm_x64_nop(asm_x64_t *as) {
+    asm_x64_write_byte_1(as, OPCODE_NOP);
+}
+
+void asm_x64_push_r64(asm_x64_t *as, int src_r64) {
+    if (src_r64 < 8) {
+        asm_x64_write_byte_1(as, OPCODE_PUSH_R64 | src_r64);
+    } else {
+        asm_x64_write_byte_2(as, REX_PREFIX | REX_B, OPCODE_PUSH_R64 | (src_r64 & 7));
+    }
+}
+
+/*
+void asm_x64_push_i32(asm_x64_t *as, int src_i32) {
+    asm_x64_write_byte_1(as, OPCODE_PUSH_I64);
+    asm_x64_write_word32(as, src_i32); // will be sign extended to 64 bits
+}
+*/
+
+/*
+void asm_x64_push_disp(asm_x64_t *as, int src_r64, int src_offset) {
+    assert(src_r64 < 8);
+    asm_x64_write_byte_1(as, OPCODE_PUSH_M64);
+    asm_x64_write_r64_disp(as, 6, src_r64, src_offset);
+}
+*/
+
+void asm_x64_pop_r64(asm_x64_t *as, int dest_r64) {
+    if (dest_r64 < 8) {
+        asm_x64_write_byte_1(as, OPCODE_POP_R64 | dest_r64);
+    } else {
+        asm_x64_write_byte_2(as, REX_PREFIX | REX_B, OPCODE_POP_R64 | (dest_r64 & 7));
+    }
+}
+
+STATIC void asm_x64_ret(asm_x64_t *as) {
+    asm_x64_write_byte_1(as, OPCODE_RET);
+}
+
+void asm_x64_mov_r64_r64(asm_x64_t *as, int dest_r64, int src_r64) {
+    asm_x64_generic_r64_r64(as, dest_r64, src_r64, OPCODE_MOV_R64_TO_RM64);
+}
+
+void asm_x64_mov_r8_to_mem8(asm_x64_t *as, int src_r64, int dest_r64, int dest_disp) {
+    if (src_r64 < 8 && dest_r64 < 8) {
+        asm_x64_write_byte_1(as, OPCODE_MOV_R8_TO_RM8);
+    } else {
+        asm_x64_write_byte_2(as, REX_PREFIX | REX_R_FROM_R64(src_r64) | REX_B_FROM_R64(dest_r64), OPCODE_MOV_R8_TO_RM8);
+    }
+    asm_x64_write_r64_disp(as, src_r64, dest_r64, dest_disp);
+}
+
+void asm_x64_mov_r16_to_mem16(asm_x64_t *as, int src_r64, int dest_r64, int dest_disp) {
+    if (src_r64 < 8 && dest_r64 < 8) {
+        asm_x64_write_byte_2(as, OP_SIZE_PREFIX, OPCODE_MOV_R64_TO_RM64);
+    } else {
+        asm_x64_write_byte_3(as, OP_SIZE_PREFIX, REX_PREFIX | REX_R_FROM_R64(src_r64) | REX_B_FROM_R64(dest_r64), OPCODE_MOV_R64_TO_RM64);
+    }
+    asm_x64_write_r64_disp(as, src_r64, dest_r64, dest_disp);
+}
+
+void asm_x64_mov_r32_to_mem32(asm_x64_t *as, int src_r64, int dest_r64, int dest_disp) {
+    if (src_r64 < 8 && dest_r64 < 8) {
+        asm_x64_write_byte_1(as, OPCODE_MOV_R64_TO_RM64);
+    } else {
+        asm_x64_write_byte_2(as, REX_PREFIX | REX_R_FROM_R64(src_r64) | REX_B_FROM_R64(dest_r64), OPCODE_MOV_R64_TO_RM64);
+    }
+    asm_x64_write_r64_disp(as, src_r64, dest_r64, dest_disp);
+}
+
+void asm_x64_mov_r64_to_mem64(asm_x64_t *as, int src_r64, int dest_r64, int dest_disp) {
+    // use REX prefix for 64 bit operation
+    asm_x64_write_byte_2(as, REX_PREFIX | REX_W | REX_R_FROM_R64(src_r64) | REX_B_FROM_R64(dest_r64), OPCODE_MOV_R64_TO_RM64);
+    asm_x64_write_r64_disp(as, src_r64, dest_r64, dest_disp);
+}
+
+void asm_x64_mov_mem8_to_r64zx(asm_x64_t *as, int src_r64, int src_disp, int dest_r64) {
+    assert(src_r64 < 8);
+    if (dest_r64 < 8) {
+        asm_x64_write_byte_2(as, 0x0f, OPCODE_MOVZX_RM8_TO_R64);
+    } else {
+        asm_x64_write_byte_3(as, REX_PREFIX | REX_R, 0x0f, OPCODE_MOVZX_RM8_TO_R64);
+    }
+    asm_x64_write_r64_disp(as, dest_r64, src_r64, src_disp);
+}
+
+void asm_x64_mov_mem16_to_r64zx(asm_x64_t *as, int src_r64, int src_disp, int dest_r64) {
+    assert(src_r64 < 8);
+    if (dest_r64 < 8) {
+        asm_x64_write_byte_2(as, 0x0f, OPCODE_MOVZX_RM16_TO_R64);
+    } else {
+        asm_x64_write_byte_3(as, REX_PREFIX | REX_R, 0x0f, OPCODE_MOVZX_RM16_TO_R64);
+    }
+    asm_x64_write_r64_disp(as, dest_r64, src_r64, src_disp);
+}
+
+void asm_x64_mov_mem32_to_r64zx(asm_x64_t *as, int src_r64, int src_disp, int dest_r64) {
+    assert(src_r64 < 8);
+    if (dest_r64 < 8) {
+        asm_x64_write_byte_1(as, OPCODE_MOV_RM64_TO_R64);
+    } else {
+        asm_x64_write_byte_2(as, REX_PREFIX | REX_R, OPCODE_MOV_RM64_TO_R64);
+    }
+    asm_x64_write_r64_disp(as, dest_r64, src_r64, src_disp);
+}
+
+void asm_x64_mov_mem64_to_r64(asm_x64_t *as, int src_r64, int src_disp, int dest_r64) {
+    // use REX prefix for 64 bit operation
+    asm_x64_write_byte_2(as, REX_PREFIX | REX_W | REX_R_FROM_R64(dest_r64) | REX_B_FROM_R64(src_r64), OPCODE_MOV_RM64_TO_R64);
+    asm_x64_write_r64_disp(as, dest_r64, src_r64, src_disp);
+}
+
+STATIC void asm_x64_lea_disp_to_r64(asm_x64_t *as, int src_r64, int src_disp, int dest_r64) {
+    // use REX prefix for 64 bit operation
+    assert(src_r64 < 8);
+    assert(dest_r64 < 8);
+    asm_x64_write_byte_2(as, REX_PREFIX | REX_W, OPCODE_LEA_MEM_TO_R64);
+    asm_x64_write_r64_disp(as, dest_r64, src_r64, src_disp);
+}
+
+/*
+void asm_x64_mov_i8_to_r8(asm_x64_t *as, int src_i8, int dest_r64) {
+    assert(dest_r64 < 8);
+    asm_x64_write_byte_2(as, OPCODE_MOV_I8_TO_R8 | dest_r64, src_i8);
+}
+*/
+
+STATIC void asm_x64_mov_i32_to_r64(asm_x64_t *as, int src_i32, int dest_r64) {
+    // cpu defaults to i32 to r64, with zero extension
+    if (dest_r64 < 8) {
+        asm_x64_write_byte_1(as, OPCODE_MOV_I64_TO_R64 | dest_r64);
+    } else {
+        asm_x64_write_byte_2(as, REX_PREFIX | REX_B, OPCODE_MOV_I64_TO_R64 | (dest_r64 & 7));
+    }
+    asm_x64_write_word32(as, src_i32);
+}
+
+void asm_x64_mov_i64_to_r64(asm_x64_t *as, int64_t src_i64, int dest_r64) {
+    // cpu defaults to i32 to r64
+    // to mov i64 to r64 need to use REX prefix
+    asm_x64_write_byte_2(as,
+        REX_PREFIX | REX_W | (dest_r64 < 8 ? 0 : REX_B),
+        OPCODE_MOV_I64_TO_R64 | (dest_r64 & 7));
+    asm_x64_write_word64(as, src_i64);
+}
+
+void asm_x64_mov_i64_to_r64_optimised(asm_x64_t *as, int64_t src_i64, int dest_r64) {
+    // TODO use movzx, movsx if possible
+    if (UNSIGNED_FIT32(src_i64)) {
+        // 5 bytes
+        asm_x64_mov_i32_to_r64(as, src_i64 & 0xffffffff, dest_r64);
+    } else {
+        // 10 bytes
+        asm_x64_mov_i64_to_r64(as, src_i64, dest_r64);
+    }
+}
+
+// src_i64 is stored as a full word in the code, and aligned to machine-word boundary
+void asm_x64_mov_i64_to_r64_aligned(asm_x64_t *as, int64_t src_i64, int dest_r64) {
+    // mov instruction uses 2 bytes for the instruction, before the i64
+    while (((as->base.code_offset + 2) & (WORD_SIZE - 1)) != 0) {
+        asm_x64_nop(as);
+    }
+    asm_x64_mov_i64_to_r64(as, src_i64, dest_r64);
+}
+
+void asm_x64_and_r64_r64(asm_x64_t *as, int dest_r64, int src_r64) {
+    asm_x64_generic_r64_r64(as, dest_r64, src_r64, OPCODE_AND_R64_TO_RM64);
+}
+
+void asm_x64_or_r64_r64(asm_x64_t *as, int dest_r64, int src_r64) {
+    asm_x64_generic_r64_r64(as, dest_r64, src_r64, OPCODE_OR_R64_TO_RM64);
+}
+
+void asm_x64_xor_r64_r64(asm_x64_t *as, int dest_r64, int src_r64) {
+    asm_x64_generic_r64_r64(as, dest_r64, src_r64, OPCODE_XOR_R64_TO_RM64);
+}
+
+void asm_x64_shl_r64_cl(asm_x64_t* as, int dest_r64) {
+    asm_x64_generic_r64_r64(as, dest_r64, 4, OPCODE_SHL_RM64_CL);
+}
+
+void asm_x64_sar_r64_cl(asm_x64_t* as, int dest_r64) {
+    asm_x64_generic_r64_r64(as, dest_r64, 7, OPCODE_SAR_RM64_CL);
+}
+
+void asm_x64_add_r64_r64(asm_x64_t *as, int dest_r64, int src_r64) {
+    asm_x64_generic_r64_r64(as, dest_r64, src_r64, OPCODE_ADD_R64_TO_RM64);
+}
+
+void asm_x64_sub_r64_r64(asm_x64_t *as, int dest_r64, int src_r64) {
+    asm_x64_generic_r64_r64(as, dest_r64, src_r64, OPCODE_SUB_R64_FROM_RM64);
+}
+
+void asm_x64_mul_r64_r64(asm_x64_t *as, int dest_r64, int src_r64) {
+    // imul reg64, reg/mem64 -- 0x0f 0xaf /r
+    asm_x64_write_byte_1(as, REX_PREFIX | REX_W | REX_R_FROM_R64(dest_r64) | REX_B_FROM_R64(src_r64));
+    asm_x64_write_byte_3(as, 0x0f, 0xaf, MODRM_R64(dest_r64) | MODRM_RM_REG | MODRM_RM_R64(src_r64));
+}
+
+/*
+void asm_x64_sub_i32_from_r32(asm_x64_t *as, int src_i32, int dest_r32) {
+    if (SIGNED_FIT8(src_i32)) {
+        // defaults to 32 bit operation
+        asm_x64_write_byte_2(as, OPCODE_SUB_I8_FROM_RM64, MODRM_R64(5) | MODRM_RM_REG | MODRM_RM_R64(dest_r32));
+        asm_x64_write_byte_1(as, src_i32 & 0xff);
+    } else {
+        // defaults to 32 bit operation
+        asm_x64_write_byte_2(as, OPCODE_SUB_I32_FROM_RM64, MODRM_R64(5) | MODRM_RM_REG | MODRM_RM_R64(dest_r32));
+        asm_x64_write_word32(as, src_i32);
+    }
+}
+*/
+
+STATIC void asm_x64_sub_r64_i32(asm_x64_t *as, int dest_r64, int src_i32) {
+    assert(dest_r64 < 8);
+    if (SIGNED_FIT8(src_i32)) {
+        // use REX prefix for 64 bit operation
+        asm_x64_write_byte_3(as, REX_PREFIX | REX_W, OPCODE_SUB_I8_FROM_RM64, MODRM_R64(5) | MODRM_RM_REG | MODRM_RM_R64(dest_r64));
+        asm_x64_write_byte_1(as, src_i32 & 0xff);
+    } else {
+        // use REX prefix for 64 bit operation
+        asm_x64_write_byte_3(as, REX_PREFIX | REX_W, OPCODE_SUB_I32_FROM_RM64, MODRM_R64(5) | MODRM_RM_REG | MODRM_RM_R64(dest_r64));
+        asm_x64_write_word32(as, src_i32);
+    }
+}
+
+/*
+void asm_x64_shl_r32_by_imm(asm_x64_t *as, int r32, int imm) {
+    asm_x64_write_byte_2(as, OPCODE_SHL_RM32_BY_I8, MODRM_R64(4) | MODRM_RM_REG | MODRM_RM_R64(r32));
+    asm_x64_write_byte_1(as, imm);
+}
+
+void asm_x64_shr_r32_by_imm(asm_x64_t *as, int r32, int imm) {
+    asm_x64_write_byte_2(as, OPCODE_SHR_RM32_BY_I8, MODRM_R64(5) | MODRM_RM_REG | MODRM_RM_R64(r32));
+    asm_x64_write_byte_1(as, imm);
+}
+
+void asm_x64_sar_r32_by_imm(asm_x64_t *as, int r32, int imm) {
+    asm_x64_write_byte_2(as, OPCODE_SAR_RM32_BY_I8, MODRM_R64(7) | MODRM_RM_REG | MODRM_RM_R64(r32));
+    asm_x64_write_byte_1(as, imm);
+}
+*/
+
+void asm_x64_cmp_r64_with_r64(asm_x64_t *as, int src_r64_a, int src_r64_b) {
+    asm_x64_generic_r64_r64(as, src_r64_b, src_r64_a, OPCODE_CMP_R64_WITH_RM64);
+}
+
+/*
+void asm_x64_cmp_i32_with_r32(asm_x64_t *as, int src_i32, int src_r32) {
+    if (SIGNED_FIT8(src_i32)) {
+        asm_x64_write_byte_2(as, OPCODE_CMP_I8_WITH_RM32, MODRM_R64(7) | MODRM_RM_REG | MODRM_RM_R64(src_r32));
+        asm_x64_write_byte_1(as, src_i32 & 0xff);
+    } else {
+        asm_x64_write_byte_2(as, OPCODE_CMP_I32_WITH_RM32, MODRM_R64(7) | MODRM_RM_REG | MODRM_RM_R64(src_r32));
+        asm_x64_write_word32(as, src_i32);
+    }
+}
+*/
+
+void asm_x64_test_r8_with_r8(asm_x64_t *as, int src_r64_a, int src_r64_b) {
+    // TODO implement for other registers
+    assert(src_r64_a == ASM_X64_REG_RAX);
+    assert(src_r64_b == ASM_X64_REG_RAX);
+    asm_x64_write_byte_2(as, OPCODE_TEST_R8_WITH_RM8, MODRM_R64(src_r64_a) | MODRM_RM_REG | MODRM_RM_R64(src_r64_b));
+}
+
+void asm_x64_setcc_r8(asm_x64_t *as, int jcc_type, int dest_r8) {
+    assert(dest_r8 < 8);
+    asm_x64_write_byte_3(as, OPCODE_SETCC_RM8_A, OPCODE_SETCC_RM8_B | jcc_type, MODRM_R64(0) | MODRM_RM_REG | MODRM_RM_R64(dest_r8));
+}
+
+STATIC mp_uint_t get_label_dest(asm_x64_t *as, mp_uint_t label) {
+    assert(label < as->base.max_num_labels);
+    return as->base.label_offsets[label];
+}
+
+void asm_x64_jmp_label(asm_x64_t *as, mp_uint_t label) {
+    mp_uint_t dest = get_label_dest(as, label);
+    mp_int_t rel = dest - as->base.code_offset;
+    if (dest != (mp_uint_t)-1 && rel < 0) {
+        // is a backwards jump, so we know the size of the jump on the first pass
+        // calculate rel assuming 8 bit relative jump
+        rel -= 2;
+        if (SIGNED_FIT8(rel)) {
+            asm_x64_write_byte_2(as, OPCODE_JMP_REL8, rel & 0xff);
+        } else {
+            rel += 2;
+            goto large_jump;
+        }
+    } else {
+        // is a forwards jump, so need to assume it's large
+        large_jump:
+        rel -= 5;
+        asm_x64_write_byte_1(as, OPCODE_JMP_REL32);
+        asm_x64_write_word32(as, rel);
+    }
+}
+
+void asm_x64_jcc_label(asm_x64_t *as, int jcc_type, mp_uint_t label) {
+    mp_uint_t dest = get_label_dest(as, label);
+    mp_int_t rel = dest - as->base.code_offset;
+    if (dest != (mp_uint_t)-1 && rel < 0) {
+        // is a backwards jump, so we know the size of the jump on the first pass
+        // calculate rel assuming 8 bit relative jump
+        rel -= 2;
+        if (SIGNED_FIT8(rel)) {
+            asm_x64_write_byte_2(as, OPCODE_JCC_REL8 | jcc_type, rel & 0xff);
+        } else {
+            rel += 2;
+            goto large_jump;
+        }
+    } else {
+        // is a forwards jump, so need to assume it's large
+        large_jump:
+        rel -= 6;
+        asm_x64_write_byte_2(as, OPCODE_JCC_REL32_A, OPCODE_JCC_REL32_B | jcc_type);
+        asm_x64_write_word32(as, rel);
+    }
+}
+
+void asm_x64_entry(asm_x64_t *as, int num_locals) {
+    asm_x64_push_r64(as, ASM_X64_REG_RBP);
+    asm_x64_mov_r64_r64(as, ASM_X64_REG_RBP, ASM_X64_REG_RSP);
+    if (num_locals < 0) {
+        num_locals = 0;
+    }
+    num_locals |= 1; // make it odd so stack is aligned on 16 byte boundary
+    asm_x64_sub_r64_i32(as, ASM_X64_REG_RSP, num_locals * WORD_SIZE);
+    asm_x64_push_r64(as, ASM_X64_REG_RBX);
+    asm_x64_push_r64(as, ASM_X64_REG_R12);
+    asm_x64_push_r64(as, ASM_X64_REG_R13);
+    as->num_locals = num_locals;
+}
+
+void asm_x64_exit(asm_x64_t *as) {
+    asm_x64_pop_r64(as, ASM_X64_REG_R13);
+    asm_x64_pop_r64(as, ASM_X64_REG_R12);
+    asm_x64_pop_r64(as, ASM_X64_REG_RBX);
+    asm_x64_write_byte_1(as, OPCODE_LEAVE);
+    asm_x64_ret(as);
+}
+
+// locals:
+//  - stored on the stack in ascending order
+//  - numbered 0 through as->num_locals-1
+//  - RBP points above the last local
+//
+//                          | RBP
+//                          v
+//  l0  l1  l2  ...  l(n-1)
+//  ^                ^
+//  | low address    | high address in RAM
+//
+STATIC int asm_x64_local_offset_from_ebp(asm_x64_t *as, int local_num) {
+    return (-as->num_locals + local_num) * WORD_SIZE;
+}
+
+void asm_x64_mov_local_to_r64(asm_x64_t *as, int src_local_num, int dest_r64) {
+    asm_x64_mov_mem64_to_r64(as, ASM_X64_REG_RBP, asm_x64_local_offset_from_ebp(as, src_local_num), dest_r64);
+}
+
+void asm_x64_mov_r64_to_local(asm_x64_t *as, int src_r64, int dest_local_num) {
+    asm_x64_mov_r64_to_mem64(as, src_r64, ASM_X64_REG_RBP, asm_x64_local_offset_from_ebp(as, dest_local_num));
+}
+
+void asm_x64_mov_local_addr_to_r64(asm_x64_t *as, int local_num, int dest_r64) {
+    int offset = asm_x64_local_offset_from_ebp(as, local_num);
+    if (offset == 0) {
+        asm_x64_mov_r64_r64(as, dest_r64, ASM_X64_REG_RBP);
+    } else {
+        asm_x64_lea_disp_to_r64(as, ASM_X64_REG_RBP, offset, dest_r64);
+    }
+}
+
+/*
+void asm_x64_push_local(asm_x64_t *as, int local_num) {
+    asm_x64_push_disp(as, ASM_X64_REG_RBP, asm_x64_local_offset_from_ebp(as, local_num));
+}
+
+void asm_x64_push_local_addr(asm_x64_t *as, int local_num, int temp_r64) {
+    asm_x64_mov_r64_r64(as, temp_r64, ASM_X64_REG_RBP);
+    asm_x64_add_i32_to_r32(as, asm_x64_local_offset_from_ebp(as, local_num), temp_r64);
+    asm_x64_push_r64(as, temp_r64);
+}
+*/
+
+/*
+   can't use these because code might be relocated when resized
+
+void asm_x64_call(asm_x64_t *as, void* func) {
+    asm_x64_sub_i32_from_r32(as, 8, ASM_X64_REG_RSP);
+    asm_x64_write_byte_1(as, OPCODE_CALL_REL32);
+    asm_x64_write_word32(as, func - (void*)(as->code_cur + 4));
+    asm_x64_mov_r64_r64(as, ASM_X64_REG_RSP, ASM_X64_REG_RBP);
+}
+
+void asm_x64_call_i1(asm_x64_t *as, void* func, int i1) {
+    asm_x64_sub_i32_from_r32(as, 8, ASM_X64_REG_RSP);
+    asm_x64_sub_i32_from_r32(as, 12, ASM_X64_REG_RSP);
+    asm_x64_push_i32(as, i1);
+    asm_x64_write_byte_1(as, OPCODE_CALL_REL32);
+    asm_x64_write_word32(as, func - (void*)(as->code_cur + 4));
+    asm_x64_add_i32_to_r32(as, 16, ASM_X64_REG_RSP);
+    asm_x64_mov_r64_r64(as, ASM_X64_REG_RSP, ASM_X64_REG_RBP);
+}
+*/
+
+void asm_x64_call_ind(asm_x64_t *as, void *ptr, int temp_r64) {
+    assert(temp_r64 < 8);
+#ifdef __LP64__
+    asm_x64_mov_i64_to_r64_optimised(as, (int64_t)ptr, temp_r64);
+#else
+    // If we get here, sizeof(int) == sizeof(void*).
+    asm_x64_mov_i64_to_r64_optimised(as, (int64_t)(unsigned int)ptr, temp_r64);
+#endif
+    asm_x64_write_byte_2(as, OPCODE_CALL_RM32, MODRM_R64(2) | MODRM_RM_REG | MODRM_RM_R64(temp_r64));
+    // this reduces code size by 2 bytes per call, but doesn't seem to speed it up at all
+    // doesn't work anymore because calls are 64 bits away
+    /*
+    asm_x64_write_byte_1(as, OPCODE_CALL_REL32);
+    asm_x64_write_word32(as, ptr - (void*)(as->code_base + as->code_offset + 4));
+    */
+}
+
+#endif // MICROPY_EMIT_X64

+ 200 - 0
py/asmx64.h

@@ -0,0 +1,200 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_PY_ASMX64_H
+#define MICROPY_INCLUDED_PY_ASMX64_H
+
+#include "py/mpconfig.h"
+#include "py/misc.h"
+#include "py/asmbase.h"
+
+// AMD64 calling convention is:
+//  - args pass in: RDI, RSI, RDX, RCX, R08, R09
+//  - return value in RAX
+//  - stack must be aligned on a 16-byte boundary before all calls
+//  - RAX, RCX, RDX, RSI, RDI, R08, R09, R10, R11 are caller-save
+//  - RBX, RBP, R12, R13, R14, R15 are callee-save
+
+// In the functions below, argument order follows x86 docs and generally
+// the destination is the first argument.
+// NOTE: this is a change from the old convention used in this file and
+// some functions still use the old (reverse) convention.
+
+#define ASM_X64_REG_RAX (0)
+#define ASM_X64_REG_RCX (1)
+#define ASM_X64_REG_RDX (2)
+#define ASM_X64_REG_RBX (3)
+#define ASM_X64_REG_RSP (4)
+#define ASM_X64_REG_RBP (5)
+#define ASM_X64_REG_RSI (6)
+#define ASM_X64_REG_RDI (7)
+#define ASM_X64_REG_R08 (8)
+#define ASM_X64_REG_R09 (9)
+#define ASM_X64_REG_R10 (10)
+#define ASM_X64_REG_R11 (11)
+#define ASM_X64_REG_R12 (12)
+#define ASM_X64_REG_R13 (13)
+#define ASM_X64_REG_R14 (14)
+#define ASM_X64_REG_R15 (15)
+
+// condition codes, used for jcc and setcc (despite their j-name!)
+#define ASM_X64_CC_JB  (0x2) // below, unsigned
+#define ASM_X64_CC_JZ  (0x4)
+#define ASM_X64_CC_JE  (0x4)
+#define ASM_X64_CC_JNZ (0x5)
+#define ASM_X64_CC_JNE (0x5)
+#define ASM_X64_CC_JL  (0xc) // less, signed
+#define ASM_X64_CC_JGE (0xd) // greater or equal, signed
+#define ASM_X64_CC_JLE (0xe) // less or equal, signed
+#define ASM_X64_CC_JG  (0xf) // greater, signed
+
+typedef struct _asm_x64_t {
+    mp_asm_base_t base;
+    int num_locals;
+} asm_x64_t;
+
+static inline void asm_x64_end_pass(asm_x64_t *as) {
+    (void)as;
+}
+
+void asm_x64_nop(asm_x64_t* as);
+void asm_x64_push_r64(asm_x64_t* as, int src_r64);
+void asm_x64_pop_r64(asm_x64_t* as, int dest_r64);
+void asm_x64_mov_r64_r64(asm_x64_t* as, int dest_r64, int src_r64);
+void asm_x64_mov_i64_to_r64(asm_x64_t* as, int64_t src_i64, int dest_r64);
+void asm_x64_mov_i64_to_r64_optimised(asm_x64_t *as, int64_t src_i64, int dest_r64);
+void asm_x64_mov_i64_to_r64_aligned(asm_x64_t *as, int64_t src_i64, int dest_r64);
+void asm_x64_mov_r8_to_mem8(asm_x64_t *as, int src_r64, int dest_r64, int dest_disp);
+void asm_x64_mov_r16_to_mem16(asm_x64_t *as, int src_r64, int dest_r64, int dest_disp);
+void asm_x64_mov_r32_to_mem32(asm_x64_t *as, int src_r64, int dest_r64, int dest_disp);
+void asm_x64_mov_r64_to_mem64(asm_x64_t *as, int src_r64, int dest_r64, int dest_disp);
+void asm_x64_mov_mem8_to_r64zx(asm_x64_t *as, int src_r64, int src_disp, int dest_r64);
+void asm_x64_mov_mem16_to_r64zx(asm_x64_t *as, int src_r64, int src_disp, int dest_r64);
+void asm_x64_mov_mem32_to_r64zx(asm_x64_t *as, int src_r64, int src_disp, int dest_r64);
+void asm_x64_mov_mem64_to_r64(asm_x64_t *as, int src_r64, int src_disp, int dest_r64);
+void asm_x64_and_r64_r64(asm_x64_t *as, int dest_r64, int src_r64);
+void asm_x64_or_r64_r64(asm_x64_t *as, int dest_r64, int src_r64);
+void asm_x64_xor_r64_r64(asm_x64_t *as, int dest_r64, int src_r64);
+void asm_x64_shl_r64_cl(asm_x64_t* as, int dest_r64);
+void asm_x64_sar_r64_cl(asm_x64_t* as, int dest_r64);
+void asm_x64_add_r64_r64(asm_x64_t* as, int dest_r64, int src_r64);
+void asm_x64_sub_r64_r64(asm_x64_t* as, int dest_r64, int src_r64);
+void asm_x64_mul_r64_r64(asm_x64_t* as, int dest_r64, int src_r64);
+void asm_x64_cmp_r64_with_r64(asm_x64_t* as, int src_r64_a, int src_r64_b);
+void asm_x64_test_r8_with_r8(asm_x64_t* as, int src_r64_a, int src_r64_b);
+void asm_x64_setcc_r8(asm_x64_t* as, int jcc_type, int dest_r8);
+void asm_x64_jmp_label(asm_x64_t* as, mp_uint_t label);
+void asm_x64_jcc_label(asm_x64_t* as, int jcc_type, mp_uint_t label);
+void asm_x64_entry(asm_x64_t* as, int num_locals);
+void asm_x64_exit(asm_x64_t* as);
+void asm_x64_mov_local_to_r64(asm_x64_t* as, int src_local_num, int dest_r64);
+void asm_x64_mov_r64_to_local(asm_x64_t* as, int src_r64, int dest_local_num);
+void asm_x64_mov_local_addr_to_r64(asm_x64_t* as, int local_num, int dest_r64);
+void asm_x64_call_ind(asm_x64_t* as, void* ptr, int temp_r32);
+
+#if GENERIC_ASM_API
+
+// The following macros provide a (mostly) arch-independent API to
+// generate native code, and are used by the native emitter.
+
+#define ASM_WORD_SIZE (8)
+
+#define REG_RET ASM_X64_REG_RAX
+#define REG_ARG_1 ASM_X64_REG_RDI
+#define REG_ARG_2 ASM_X64_REG_RSI
+#define REG_ARG_3 ASM_X64_REG_RDX
+#define REG_ARG_4 ASM_X64_REG_RCX
+#define REG_ARG_5 ASM_X64_REG_R08
+
+// caller-save
+#define REG_TEMP0 ASM_X64_REG_RAX
+#define REG_TEMP1 ASM_X64_REG_RDI
+#define REG_TEMP2 ASM_X64_REG_RSI
+
+// callee-save
+#define REG_LOCAL_1 ASM_X64_REG_RBX
+#define REG_LOCAL_2 ASM_X64_REG_R12
+#define REG_LOCAL_3 ASM_X64_REG_R13
+#define REG_LOCAL_NUM (3)
+
+#define ASM_T               asm_x64_t
+#define ASM_END_PASS        asm_x64_end_pass
+#define ASM_ENTRY           asm_x64_entry
+#define ASM_EXIT            asm_x64_exit
+
+#define ASM_JUMP            asm_x64_jmp_label
+#define ASM_JUMP_IF_REG_ZERO(as, reg, label) \
+    do { \
+        asm_x64_test_r8_with_r8(as, reg, reg); \
+        asm_x64_jcc_label(as, ASM_X64_CC_JZ, label); \
+    } while (0)
+#define ASM_JUMP_IF_REG_NONZERO(as, reg, label) \
+    do { \
+        asm_x64_test_r8_with_r8(as, reg, reg); \
+        asm_x64_jcc_label(as, ASM_X64_CC_JNZ, label); \
+    } while (0)
+#define ASM_JUMP_IF_REG_EQ(as, reg1, reg2, label) \
+    do { \
+        asm_x64_cmp_r64_with_r64(as, reg1, reg2); \
+        asm_x64_jcc_label(as, ASM_X64_CC_JE, label); \
+    } while (0)
+#define ASM_CALL_IND(as, ptr, idx) asm_x64_call_ind(as, ptr, ASM_X64_REG_RAX)
+
+#define ASM_MOV_REG_TO_LOCAL        asm_x64_mov_r64_to_local
+#define ASM_MOV_IMM_TO_REG          asm_x64_mov_i64_to_r64_optimised
+#define ASM_MOV_ALIGNED_IMM_TO_REG  asm_x64_mov_i64_to_r64_aligned
+#define ASM_MOV_IMM_TO_LOCAL_USING(as, imm, local_num, reg_temp) \
+    do { \
+        asm_x64_mov_i64_to_r64_optimised(as, (imm), (reg_temp)); \
+        asm_x64_mov_r64_to_local(as, (reg_temp), (local_num)); \
+    } while (false)
+#define ASM_MOV_LOCAL_TO_REG        asm_x64_mov_local_to_r64
+#define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_x64_mov_r64_r64((as), (reg_dest), (reg_src))
+#define ASM_MOV_LOCAL_ADDR_TO_REG   asm_x64_mov_local_addr_to_r64
+
+#define ASM_LSL_REG(as, reg) asm_x64_shl_r64_cl((as), (reg))
+#define ASM_ASR_REG(as, reg) asm_x64_sar_r64_cl((as), (reg))
+#define ASM_OR_REG_REG(as, reg_dest, reg_src) asm_x64_or_r64_r64((as), (reg_dest), (reg_src))
+#define ASM_XOR_REG_REG(as, reg_dest, reg_src) asm_x64_xor_r64_r64((as), (reg_dest), (reg_src))
+#define ASM_AND_REG_REG(as, reg_dest, reg_src) asm_x64_and_r64_r64((as), (reg_dest), (reg_src))
+#define ASM_ADD_REG_REG(as, reg_dest, reg_src) asm_x64_add_r64_r64((as), (reg_dest), (reg_src))
+#define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_x64_sub_r64_r64((as), (reg_dest), (reg_src))
+#define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_x64_mul_r64_r64((as), (reg_dest), (reg_src))
+
+#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) asm_x64_mov_mem64_to_r64((as), (reg_base), 0, (reg_dest))
+#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_x64_mov_mem64_to_r64((as), (reg_base), 8 * (word_offset), (reg_dest))
+#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_x64_mov_mem8_to_r64zx((as), (reg_base), 0, (reg_dest))
+#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_x64_mov_mem16_to_r64zx((as), (reg_base), 0, (reg_dest))
+#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_x64_mov_mem32_to_r64zx((as), (reg_base), 0, (reg_dest))
+
+#define ASM_STORE_REG_REG(as, reg_src, reg_base) asm_x64_mov_r64_to_mem64((as), (reg_src), (reg_base), 0)
+#define ASM_STORE_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) asm_x64_mov_r64_to_mem64((as), (reg_src), (reg_base), 8 * (word_offset))
+#define ASM_STORE8_REG_REG(as, reg_src, reg_base) asm_x64_mov_r8_to_mem8((as), (reg_src), (reg_base), 0)
+#define ASM_STORE16_REG_REG(as, reg_src, reg_base) asm_x64_mov_r16_to_mem16((as), (reg_src), (reg_base), 0)
+#define ASM_STORE32_REG_REG(as, reg_src, reg_base) asm_x64_mov_r32_to_mem32((as), (reg_src), (reg_base), 0)
+
+#endif // GENERIC_ASM_API
+
+#endif // MICROPY_INCLUDED_PY_ASMX64_H

+ 511 - 0
py/asmx86.c

@@ -0,0 +1,511 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+#include "py/mpconfig.h"
+
+// wrapper around everything in this file
+#if MICROPY_EMIT_X86
+
+#include "py/asmx86.h"
+
+/* all offsets are measured in multiples of 4 bytes */
+#define WORD_SIZE                (4)
+
+#define OPCODE_NOP               (0x90)
+#define OPCODE_PUSH_R32          (0x50)
+//#define OPCODE_PUSH_I32          (0x68)
+//#define OPCODE_PUSH_M32          (0xff) /* /6 */
+#define OPCODE_POP_R32           (0x58)
+#define OPCODE_RET               (0xc3)
+//#define OPCODE_MOV_I8_TO_R8      (0xb0) /* +rb */
+#define OPCODE_MOV_I32_TO_R32    (0xb8)
+//#define OPCODE_MOV_I32_TO_RM32   (0xc7)
+#define OPCODE_MOV_R8_TO_RM8     (0x88) /* /r */
+#define OPCODE_MOV_R32_TO_RM32   (0x89) /* /r */
+#define OPCODE_MOV_RM32_TO_R32   (0x8b) /* /r */
+#define OPCODE_MOVZX_RM8_TO_R32  (0xb6) /* 0x0f 0xb6/r */
+#define OPCODE_MOVZX_RM16_TO_R32 (0xb7) /* 0x0f 0xb7/r */
+#define OPCODE_LEA_MEM_TO_R32    (0x8d) /* /r */
+#define OPCODE_AND_R32_TO_RM32   (0x21) /* /r */
+#define OPCODE_OR_R32_TO_RM32    (0x09) /* /r */
+#define OPCODE_XOR_R32_TO_RM32   (0x31) /* /r */
+#define OPCODE_ADD_R32_TO_RM32   (0x01)
+#define OPCODE_ADD_I32_TO_RM32   (0x81) /* /0 */
+#define OPCODE_ADD_I8_TO_RM32    (0x83) /* /0 */
+#define OPCODE_SUB_R32_FROM_RM32 (0x29)
+#define OPCODE_SUB_I32_FROM_RM32 (0x81) /* /5 */
+#define OPCODE_SUB_I8_FROM_RM32  (0x83) /* /5 */
+//#define OPCODE_SHL_RM32_BY_I8    (0xc1) /* /4 */
+//#define OPCODE_SHR_RM32_BY_I8    (0xc1) /* /5 */
+//#define OPCODE_SAR_RM32_BY_I8    (0xc1) /* /7 */
+#define OPCODE_SHL_RM32_CL       (0xd3) /* /4 */
+#define OPCODE_SAR_RM32_CL       (0xd3) /* /7 */
+//#define OPCODE_CMP_I32_WITH_RM32 (0x81) /* /7 */
+//#define OPCODE_CMP_I8_WITH_RM32  (0x83) /* /7 */
+#define OPCODE_CMP_R32_WITH_RM32 (0x39)
+//#define OPCODE_CMP_RM32_WITH_R32 (0x3b)
+#define OPCODE_TEST_R8_WITH_RM8  (0x84) /* /r */
+#define OPCODE_JMP_REL8          (0xeb)
+#define OPCODE_JMP_REL32         (0xe9)
+#define OPCODE_JCC_REL8          (0x70) /* | jcc type */
+#define OPCODE_JCC_REL32_A       (0x0f)
+#define OPCODE_JCC_REL32_B       (0x80) /* | jcc type */
+#define OPCODE_SETCC_RM8_A       (0x0f)
+#define OPCODE_SETCC_RM8_B       (0x90) /* | jcc type, /0 */
+#define OPCODE_CALL_REL32        (0xe8)
+#define OPCODE_CALL_RM32         (0xff) /* /2 */
+#define OPCODE_LEAVE             (0xc9)
+
+#define MODRM_R32(x)    ((x) << 3)
+#define MODRM_RM_DISP0  (0x00)
+#define MODRM_RM_DISP8  (0x40)
+#define MODRM_RM_DISP32 (0x80)
+#define MODRM_RM_REG    (0xc0)
+#define MODRM_RM_R32(x) (x)
+
+#define OP_SIZE_PREFIX (0x66)
+
+#define IMM32_L0(x) ((x) & 0xff)
+#define IMM32_L1(x) (((x) >> 8) & 0xff)
+#define IMM32_L2(x) (((x) >> 16) & 0xff)
+#define IMM32_L3(x) (((x) >> 24) & 0xff)
+
+#define SIGNED_FIT8(x) (((x) & 0xffffff80) == 0) || (((x) & 0xffffff80) == 0xffffff80)
+
+STATIC void asm_x86_write_byte_1(asm_x86_t *as, byte b1) {
+    byte* c = mp_asm_base_get_cur_to_write_bytes(&as->base, 1);
+    if (c != NULL) {
+        c[0] = b1;
+    }
+}
+
+STATIC void asm_x86_write_byte_2(asm_x86_t *as, byte b1, byte b2) {
+    byte* c = mp_asm_base_get_cur_to_write_bytes(&as->base, 2);
+    if (c != NULL) {
+        c[0] = b1;
+        c[1] = b2;
+    }
+}
+
+STATIC void asm_x86_write_byte_3(asm_x86_t *as, byte b1, byte b2, byte b3) {
+    byte* c = mp_asm_base_get_cur_to_write_bytes(&as->base, 3);
+    if (c != NULL) {
+        c[0] = b1;
+        c[1] = b2;
+        c[2] = b3;
+    }
+}
+
+STATIC void asm_x86_write_word32(asm_x86_t *as, int w32) {
+    byte* c = mp_asm_base_get_cur_to_write_bytes(&as->base, 4);
+    if (c != NULL) {
+        c[0] = IMM32_L0(w32);
+        c[1] = IMM32_L1(w32);
+        c[2] = IMM32_L2(w32);
+        c[3] = IMM32_L3(w32);
+    }
+}
+
+STATIC void asm_x86_write_r32_disp(asm_x86_t *as, int r32, int disp_r32, int disp_offset) {
+    assert(disp_r32 != ASM_X86_REG_ESP);
+
+    if (disp_offset == 0 && disp_r32 != ASM_X86_REG_EBP) {
+        asm_x86_write_byte_1(as, MODRM_R32(r32) | MODRM_RM_DISP0 | MODRM_RM_R32(disp_r32));
+    } else if (SIGNED_FIT8(disp_offset)) {
+        asm_x86_write_byte_2(as, MODRM_R32(r32) | MODRM_RM_DISP8 | MODRM_RM_R32(disp_r32), IMM32_L0(disp_offset));
+    } else {
+        asm_x86_write_byte_1(as, MODRM_R32(r32) | MODRM_RM_DISP32 | MODRM_RM_R32(disp_r32));
+        asm_x86_write_word32(as, disp_offset);
+    }
+}
+
+STATIC void asm_x86_generic_r32_r32(asm_x86_t *as, int dest_r32, int src_r32, int op) {
+    asm_x86_write_byte_2(as, op, MODRM_R32(src_r32) | MODRM_RM_REG | MODRM_RM_R32(dest_r32));
+}
+
+STATIC void asm_x86_nop(asm_x86_t *as) {
+    asm_x86_write_byte_1(as, OPCODE_NOP);
+}
+
+STATIC void asm_x86_push_r32(asm_x86_t *as, int src_r32) {
+    asm_x86_write_byte_1(as, OPCODE_PUSH_R32 | src_r32);
+}
+
+#if 0
+void asm_x86_push_i32(asm_x86_t *as, int src_i32) {
+    asm_x86_write_byte_1(as, OPCODE_PUSH_I32);
+    asm_x86_write_word32(as, src_i32);
+}
+
+void asm_x86_push_disp(asm_x86_t *as, int src_r32, int src_offset) {
+    asm_x86_write_byte_1(as, OPCODE_PUSH_M32);
+    asm_x86_write_r32_disp(as, 6, src_r32, src_offset);
+}
+#endif
+
+STATIC void asm_x86_pop_r32(asm_x86_t *as, int dest_r32) {
+    asm_x86_write_byte_1(as, OPCODE_POP_R32 | dest_r32);
+}
+
+STATIC void asm_x86_ret(asm_x86_t *as) {
+    asm_x86_write_byte_1(as, OPCODE_RET);
+}
+
+void asm_x86_mov_r32_r32(asm_x86_t *as, int dest_r32, int src_r32) {
+    asm_x86_generic_r32_r32(as, dest_r32, src_r32, OPCODE_MOV_R32_TO_RM32);
+}
+
+void asm_x86_mov_r8_to_mem8(asm_x86_t *as, int src_r32, int dest_r32, int dest_disp) {
+    asm_x86_write_byte_1(as, OPCODE_MOV_R8_TO_RM8);
+    asm_x86_write_r32_disp(as, src_r32, dest_r32, dest_disp);
+}
+
+void asm_x86_mov_r16_to_mem16(asm_x86_t *as, int src_r32, int dest_r32, int dest_disp) {
+    asm_x86_write_byte_2(as, OP_SIZE_PREFIX, OPCODE_MOV_R32_TO_RM32);
+    asm_x86_write_r32_disp(as, src_r32, dest_r32, dest_disp);
+}
+
+void asm_x86_mov_r32_to_mem32(asm_x86_t *as, int src_r32, int dest_r32, int dest_disp) {
+    asm_x86_write_byte_1(as, OPCODE_MOV_R32_TO_RM32);
+    asm_x86_write_r32_disp(as, src_r32, dest_r32, dest_disp);
+}
+
+void asm_x86_mov_mem8_to_r32zx(asm_x86_t *as, int src_r32, int src_disp, int dest_r32) {
+    asm_x86_write_byte_2(as, 0x0f, OPCODE_MOVZX_RM8_TO_R32);
+    asm_x86_write_r32_disp(as, dest_r32, src_r32, src_disp);
+}
+
+void asm_x86_mov_mem16_to_r32zx(asm_x86_t *as, int src_r32, int src_disp, int dest_r32) {
+    asm_x86_write_byte_2(as, 0x0f, OPCODE_MOVZX_RM16_TO_R32);
+    asm_x86_write_r32_disp(as, dest_r32, src_r32, src_disp);
+}
+
+void asm_x86_mov_mem32_to_r32(asm_x86_t *as, int src_r32, int src_disp, int dest_r32) {
+    asm_x86_write_byte_1(as, OPCODE_MOV_RM32_TO_R32);
+    asm_x86_write_r32_disp(as, dest_r32, src_r32, src_disp);
+}
+
+STATIC void asm_x86_lea_disp_to_r32(asm_x86_t *as, int src_r32, int src_disp, int dest_r32) {
+    asm_x86_write_byte_1(as, OPCODE_LEA_MEM_TO_R32);
+    asm_x86_write_r32_disp(as, dest_r32, src_r32, src_disp);
+}
+
+#if 0
+void asm_x86_mov_i8_to_r8(asm_x86_t *as, int src_i8, int dest_r32) {
+    asm_x86_write_byte_2(as, OPCODE_MOV_I8_TO_R8 | dest_r32, src_i8);
+}
+#endif
+
+void asm_x86_mov_i32_to_r32(asm_x86_t *as, int32_t src_i32, int dest_r32) {
+    asm_x86_write_byte_1(as, OPCODE_MOV_I32_TO_R32 | dest_r32);
+    asm_x86_write_word32(as, src_i32);
+}
+
+// src_i32 is stored as a full word in the code, and aligned to machine-word boundary
+void asm_x86_mov_i32_to_r32_aligned(asm_x86_t *as, int32_t src_i32, int dest_r32) {
+    // mov instruction uses 1 byte for the instruction, before the i32
+    while (((as->base.code_offset + 1) & (WORD_SIZE - 1)) != 0) {
+        asm_x86_nop(as);
+    }
+    asm_x86_mov_i32_to_r32(as, src_i32, dest_r32);
+}
+
+void asm_x86_and_r32_r32(asm_x86_t *as, int dest_r32, int src_r32) {
+    asm_x86_generic_r32_r32(as, dest_r32, src_r32, OPCODE_AND_R32_TO_RM32);
+}
+
+void asm_x86_or_r32_r32(asm_x86_t *as, int dest_r32, int src_r32) {
+    asm_x86_generic_r32_r32(as, dest_r32, src_r32, OPCODE_OR_R32_TO_RM32);
+}
+
+void asm_x86_xor_r32_r32(asm_x86_t *as, int dest_r32, int src_r32) {
+    asm_x86_generic_r32_r32(as, dest_r32, src_r32, OPCODE_XOR_R32_TO_RM32);
+}
+
+void asm_x86_shl_r32_cl(asm_x86_t* as, int dest_r32) {
+    asm_x86_generic_r32_r32(as, dest_r32, 4, OPCODE_SHL_RM32_CL);
+}
+
+void asm_x86_sar_r32_cl(asm_x86_t* as, int dest_r32) {
+    asm_x86_generic_r32_r32(as, dest_r32, 7, OPCODE_SAR_RM32_CL);
+}
+
+void asm_x86_add_r32_r32(asm_x86_t *as, int dest_r32, int src_r32) {
+    asm_x86_generic_r32_r32(as, dest_r32, src_r32, OPCODE_ADD_R32_TO_RM32);
+}
+
+STATIC void asm_x86_add_i32_to_r32(asm_x86_t *as, int src_i32, int dest_r32) {
+    if (SIGNED_FIT8(src_i32)) {
+        asm_x86_write_byte_2(as, OPCODE_ADD_I8_TO_RM32, MODRM_R32(0) | MODRM_RM_REG | MODRM_RM_R32(dest_r32));
+        asm_x86_write_byte_1(as, src_i32 & 0xff);
+    } else {
+        asm_x86_write_byte_2(as, OPCODE_ADD_I32_TO_RM32, MODRM_R32(0) | MODRM_RM_REG | MODRM_RM_R32(dest_r32));
+        asm_x86_write_word32(as, src_i32);
+    }
+}
+
+void asm_x86_sub_r32_r32(asm_x86_t *as, int dest_r32, int src_r32) {
+    asm_x86_generic_r32_r32(as, dest_r32, src_r32, OPCODE_SUB_R32_FROM_RM32);
+}
+
+STATIC void asm_x86_sub_r32_i32(asm_x86_t *as, int dest_r32, int src_i32) {
+    if (SIGNED_FIT8(src_i32)) {
+        // defaults to 32 bit operation
+        asm_x86_write_byte_2(as, OPCODE_SUB_I8_FROM_RM32, MODRM_R32(5) | MODRM_RM_REG | MODRM_RM_R32(dest_r32));
+        asm_x86_write_byte_1(as, src_i32 & 0xff);
+    } else {
+        // defaults to 32 bit operation
+        asm_x86_write_byte_2(as, OPCODE_SUB_I32_FROM_RM32, MODRM_R32(5) | MODRM_RM_REG | MODRM_RM_R32(dest_r32));
+        asm_x86_write_word32(as, src_i32);
+    }
+}
+
+void asm_x86_mul_r32_r32(asm_x86_t *as, int dest_r32, int src_r32) {
+    // imul reg32, reg/mem32 -- 0x0f 0xaf /r
+    asm_x86_write_byte_3(as, 0x0f, 0xaf, MODRM_R32(dest_r32) | MODRM_RM_REG | MODRM_RM_R32(src_r32));
+}
+
+#if 0
+/* shifts not tested */
+void asm_x86_shl_r32_by_imm(asm_x86_t *as, int r32, int imm) {
+    asm_x86_write_byte_2(as, OPCODE_SHL_RM32_BY_I8, MODRM_R32(4) | MODRM_RM_REG | MODRM_RM_R32(r32));
+    asm_x86_write_byte_1(as, imm);
+}
+
+void asm_x86_shr_r32_by_imm(asm_x86_t *as, int r32, int imm) {
+    asm_x86_write_byte_2(as, OPCODE_SHR_RM32_BY_I8, MODRM_R32(5) | MODRM_RM_REG | MODRM_RM_R32(r32));
+    asm_x86_write_byte_1(as, imm);
+}
+
+void asm_x86_sar_r32_by_imm(asm_x86_t *as, int r32, int imm) {
+    asm_x86_write_byte_2(as, OPCODE_SAR_RM32_BY_I8, MODRM_R32(7) | MODRM_RM_REG | MODRM_RM_R32(r32));
+    asm_x86_write_byte_1(as, imm);
+}
+#endif
+
+void asm_x86_cmp_r32_with_r32(asm_x86_t *as, int src_r32_a, int src_r32_b) {
+    asm_x86_write_byte_2(as, OPCODE_CMP_R32_WITH_RM32, MODRM_R32(src_r32_a) | MODRM_RM_REG | MODRM_RM_R32(src_r32_b));
+}
+
+#if 0
+void asm_x86_cmp_i32_with_r32(asm_x86_t *as, int src_i32, int src_r32) {
+    if (SIGNED_FIT8(src_i32)) {
+        asm_x86_write_byte_2(as, OPCODE_CMP_I8_WITH_RM32, MODRM_R32(7) | MODRM_RM_REG | MODRM_RM_R32(src_r32));
+        asm_x86_write_byte_1(as, src_i32 & 0xff);
+    } else {
+        asm_x86_write_byte_2(as, OPCODE_CMP_I32_WITH_RM32, MODRM_R32(7) | MODRM_RM_REG | MODRM_RM_R32(src_r32));
+        asm_x86_write_word32(as, src_i32);
+    }
+}
+#endif
+
+void asm_x86_test_r8_with_r8(asm_x86_t *as, int src_r32_a, int src_r32_b) {
+    // TODO implement for other registers
+    assert(src_r32_a == ASM_X86_REG_EAX);
+    assert(src_r32_b == ASM_X86_REG_EAX);
+    asm_x86_write_byte_2(as, OPCODE_TEST_R8_WITH_RM8, MODRM_R32(src_r32_a) | MODRM_RM_REG | MODRM_RM_R32(src_r32_b));
+}
+
+void asm_x86_setcc_r8(asm_x86_t *as, mp_uint_t jcc_type, int dest_r8) {
+    asm_x86_write_byte_3(as, OPCODE_SETCC_RM8_A, OPCODE_SETCC_RM8_B | jcc_type, MODRM_R32(0) | MODRM_RM_REG | MODRM_RM_R32(dest_r8));
+}
+
+STATIC mp_uint_t get_label_dest(asm_x86_t *as, mp_uint_t label) {
+    assert(label < as->base.max_num_labels);
+    return as->base.label_offsets[label];
+}
+
+void asm_x86_jmp_label(asm_x86_t *as, mp_uint_t label) {
+    mp_uint_t dest = get_label_dest(as, label);
+    mp_int_t rel = dest - as->base.code_offset;
+    if (dest != (mp_uint_t)-1 && rel < 0) {
+        // is a backwards jump, so we know the size of the jump on the first pass
+        // calculate rel assuming 8 bit relative jump
+        rel -= 2;
+        if (SIGNED_FIT8(rel)) {
+            asm_x86_write_byte_2(as, OPCODE_JMP_REL8, rel & 0xff);
+        } else {
+            rel += 2;
+            goto large_jump;
+        }
+    } else {
+        // is a forwards jump, so need to assume it's large
+        large_jump:
+        rel -= 5;
+        asm_x86_write_byte_1(as, OPCODE_JMP_REL32);
+        asm_x86_write_word32(as, rel);
+    }
+}
+
+void asm_x86_jcc_label(asm_x86_t *as, mp_uint_t jcc_type, mp_uint_t label) {
+    mp_uint_t dest = get_label_dest(as, label);
+    mp_int_t rel = dest - as->base.code_offset;
+    if (dest != (mp_uint_t)-1 && rel < 0) {
+        // is a backwards jump, so we know the size of the jump on the first pass
+        // calculate rel assuming 8 bit relative jump
+        rel -= 2;
+        if (SIGNED_FIT8(rel)) {
+            asm_x86_write_byte_2(as, OPCODE_JCC_REL8 | jcc_type, rel & 0xff);
+        } else {
+            rel += 2;
+            goto large_jump;
+        }
+    } else {
+        // is a forwards jump, so need to assume it's large
+        large_jump:
+        rel -= 6;
+        asm_x86_write_byte_2(as, OPCODE_JCC_REL32_A, OPCODE_JCC_REL32_B | jcc_type);
+        asm_x86_write_word32(as, rel);
+    }
+}
+
+void asm_x86_entry(asm_x86_t *as, mp_uint_t num_locals) {
+    asm_x86_push_r32(as, ASM_X86_REG_EBP);
+    asm_x86_mov_r32_r32(as, ASM_X86_REG_EBP, ASM_X86_REG_ESP);
+    if (num_locals > 0) {
+        asm_x86_sub_r32_i32(as, ASM_X86_REG_ESP, num_locals * WORD_SIZE);
+    }
+    asm_x86_push_r32(as, ASM_X86_REG_EBX);
+    asm_x86_push_r32(as, ASM_X86_REG_ESI);
+    asm_x86_push_r32(as, ASM_X86_REG_EDI);
+    // TODO align stack on 16-byte boundary
+    as->num_locals = num_locals;
+}
+
+void asm_x86_exit(asm_x86_t *as) {
+    asm_x86_pop_r32(as, ASM_X86_REG_EDI);
+    asm_x86_pop_r32(as, ASM_X86_REG_ESI);
+    asm_x86_pop_r32(as, ASM_X86_REG_EBX);
+    asm_x86_write_byte_1(as, OPCODE_LEAVE);
+    asm_x86_ret(as);
+}
+
+#if 0
+void asm_x86_push_arg(asm_x86_t *as, int src_arg_num) {
+    asm_x86_push_disp(as, ASM_X86_REG_EBP, 2 * WORD_SIZE + src_arg_num * WORD_SIZE);
+}
+#endif
+
+void asm_x86_mov_arg_to_r32(asm_x86_t *as, int src_arg_num, int dest_r32) {
+    asm_x86_mov_mem32_to_r32(as, ASM_X86_REG_EBP, 2 * WORD_SIZE + src_arg_num * WORD_SIZE, dest_r32);
+}
+
+#if 0
+void asm_x86_mov_r32_to_arg(asm_x86_t *as, int src_r32, int dest_arg_num) {
+    asm_x86_mov_r32_to_mem32(as, src_r32, ASM_X86_REG_EBP, 2 * WORD_SIZE + dest_arg_num * WORD_SIZE);
+}
+#endif
+
+// locals:
+//  - stored on the stack in ascending order
+//  - numbered 0 through as->num_locals-1
+//  - EBP points above the last local
+//
+//                          | EBP
+//                          v
+//  l0  l1  l2  ...  l(n-1)
+//  ^                ^
+//  | low address    | high address in RAM
+//
+STATIC int asm_x86_local_offset_from_ebp(asm_x86_t *as, int local_num) {
+    return (-as->num_locals + local_num) * WORD_SIZE;
+}
+
+void asm_x86_mov_local_to_r32(asm_x86_t *as, int src_local_num, int dest_r32) {
+    asm_x86_mov_mem32_to_r32(as, ASM_X86_REG_EBP, asm_x86_local_offset_from_ebp(as, src_local_num), dest_r32);
+}
+
+void asm_x86_mov_r32_to_local(asm_x86_t *as, int src_r32, int dest_local_num) {
+    asm_x86_mov_r32_to_mem32(as, src_r32, ASM_X86_REG_EBP, asm_x86_local_offset_from_ebp(as, dest_local_num));
+}
+
+void asm_x86_mov_local_addr_to_r32(asm_x86_t *as, int local_num, int dest_r32) {
+    int offset = asm_x86_local_offset_from_ebp(as, local_num);
+    if (offset == 0) {
+        asm_x86_mov_r32_r32(as, dest_r32, ASM_X86_REG_EBP);
+    } else {
+        asm_x86_lea_disp_to_r32(as, ASM_X86_REG_EBP, offset, dest_r32);
+    }
+}
+
+#if 0
+void asm_x86_push_local(asm_x86_t *as, int local_num) {
+    asm_x86_push_disp(as, ASM_X86_REG_EBP, asm_x86_local_offset_from_ebp(as, local_num));
+}
+
+void asm_x86_push_local_addr(asm_x86_t *as, int local_num, int temp_r32)
+{
+    asm_x86_mov_r32_r32(as, temp_r32, ASM_X86_REG_EBP);
+    asm_x86_add_i32_to_r32(as, asm_x86_local_offset_from_ebp(as, local_num), temp_r32);
+    asm_x86_push_r32(as, temp_r32);
+}
+#endif
+
+void asm_x86_call_ind(asm_x86_t *as, void *ptr, mp_uint_t n_args, int temp_r32) {
+    // TODO align stack on 16-byte boundary before the call
+    assert(n_args <= 5);
+    if (n_args > 4) {
+        asm_x86_push_r32(as, ASM_X86_REG_ARG_5);
+    }
+    if (n_args > 3) {
+        asm_x86_push_r32(as, ASM_X86_REG_ARG_4);
+    }
+    if (n_args > 2) {
+        asm_x86_push_r32(as, ASM_X86_REG_ARG_3);
+    }
+    if (n_args > 1) {
+        asm_x86_push_r32(as, ASM_X86_REG_ARG_2);
+    }
+    if (n_args > 0) {
+        asm_x86_push_r32(as, ASM_X86_REG_ARG_1);
+    }
+#ifdef __LP64__
+    // We wouldn't run x86 code on an x64 machine.  This is here to enable
+    // testing of the x86 emitter only.
+    asm_x86_mov_i32_to_r32(as, (int32_t)(int64_t)ptr, temp_r32);
+#else
+    // If we get here, sizeof(int) == sizeof(void*).
+    asm_x86_mov_i32_to_r32(as, (int32_t)ptr, temp_r32);
+#endif
+    asm_x86_write_byte_2(as, OPCODE_CALL_RM32, MODRM_R32(2) | MODRM_RM_REG | MODRM_RM_R32(temp_r32));
+    // this reduces code size by 2 bytes per call, but doesn't seem to speed it up at all
+    /*
+    asm_x86_write_byte_1(as, OPCODE_CALL_REL32);
+    asm_x86_write_word32(as, ptr - (void*)(as->code_base + as->base.code_offset + 4));
+    */
+
+    // the caller must clean up the stack
+    if (n_args > 0) {
+        asm_x86_add_i32_to_r32(as, WORD_SIZE * n_args, ASM_X86_REG_ESP);
+    }
+}
+
+#endif // MICROPY_EMIT_X86

+ 198 - 0
py/asmx86.h

@@ -0,0 +1,198 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_PY_ASMX86_H
+#define MICROPY_INCLUDED_PY_ASMX86_H
+
+#include "py/mpconfig.h"
+#include "py/misc.h"
+#include "py/asmbase.h"
+
+// x86 cdecl calling convention is:
+//  - args passed on the stack in reverse order
+//  - return value in EAX
+//  - caller cleans up the stack after a call
+//  - stack must be aligned to 16-byte boundary before all calls
+//  - EAX, ECX, EDX are caller-save
+//  - EBX, ESI, EDI, EBP, ESP, EIP are callee-save
+
+// In the functions below, argument order follows x86 docs and generally
+// the destination is the first argument.
+// NOTE: this is a change from the old convention used in this file and
+// some functions still use the old (reverse) convention.
+
+#define ASM_X86_REG_EAX (0)
+#define ASM_X86_REG_ECX (1)
+#define ASM_X86_REG_EDX (2)
+#define ASM_X86_REG_EBX (3)
+#define ASM_X86_REG_ESP (4)
+#define ASM_X86_REG_EBP (5)
+#define ASM_X86_REG_ESI (6)
+#define ASM_X86_REG_EDI (7)
+
+// x86 passes values on the stack, but the emitter is register based, so we need
+// to define registers that can temporarily hold the function arguments.  They
+// need to be defined here so that asm_x86_call_ind can push them onto the stack
+// before the call.
+#define ASM_X86_REG_ARG_1 ASM_X86_REG_EAX
+#define ASM_X86_REG_ARG_2 ASM_X86_REG_ECX
+#define ASM_X86_REG_ARG_3 ASM_X86_REG_EDX
+#define ASM_X86_REG_ARG_4 ASM_X86_REG_EBX
+#define ASM_X86_REG_ARG_5 ASM_X86_REG_ESI
+
+// condition codes, used for jcc and setcc (despite their j-name!)
+#define ASM_X86_CC_JB  (0x2) // below, unsigned
+#define ASM_X86_CC_JZ  (0x4)
+#define ASM_X86_CC_JE  (0x4)
+#define ASM_X86_CC_JNZ (0x5)
+#define ASM_X86_CC_JNE (0x5)
+#define ASM_X86_CC_JL  (0xc) // less, signed
+#define ASM_X86_CC_JGE (0xd) // greater or equal, signed
+#define ASM_X86_CC_JLE (0xe) // less or equal, signed
+#define ASM_X86_CC_JG  (0xf) // greater, signed
+
+typedef struct _asm_x86_t {
+    mp_asm_base_t base;
+    int num_locals;
+} asm_x86_t;
+
+static inline void asm_x86_end_pass(asm_x86_t *as) {
+    (void)as;
+}
+
+void asm_x86_mov_r32_r32(asm_x86_t* as, int dest_r32, int src_r32);
+void asm_x86_mov_i32_to_r32(asm_x86_t *as, int32_t src_i32, int dest_r32);
+void asm_x86_mov_i32_to_r32_aligned(asm_x86_t *as, int32_t src_i32, int dest_r32);
+void asm_x86_mov_r8_to_mem8(asm_x86_t *as, int src_r32, int dest_r32, int dest_disp);
+void asm_x86_mov_r16_to_mem16(asm_x86_t *as, int src_r32, int dest_r32, int dest_disp);
+void asm_x86_mov_r32_to_mem32(asm_x86_t *as, int src_r32, int dest_r32, int dest_disp);
+void asm_x86_mov_mem8_to_r32zx(asm_x86_t *as, int src_r32, int src_disp, int dest_r32);
+void asm_x86_mov_mem16_to_r32zx(asm_x86_t *as, int src_r32, int src_disp, int dest_r32);
+void asm_x86_mov_mem32_to_r32(asm_x86_t *as, int src_r32, int src_disp, int dest_r32);
+void asm_x86_and_r32_r32(asm_x86_t *as, int dest_r32, int src_r32);
+void asm_x86_or_r32_r32(asm_x86_t *as, int dest_r32, int src_r32);
+void asm_x86_xor_r32_r32(asm_x86_t *as, int dest_r32, int src_r32);
+void asm_x86_shl_r32_cl(asm_x86_t* as, int dest_r32);
+void asm_x86_sar_r32_cl(asm_x86_t* as, int dest_r32);
+void asm_x86_add_r32_r32(asm_x86_t* as, int dest_r32, int src_r32);
+void asm_x86_sub_r32_r32(asm_x86_t* as, int dest_r32, int src_r32);
+void asm_x86_mul_r32_r32(asm_x86_t* as, int dest_r32, int src_r32);
+void asm_x86_cmp_r32_with_r32(asm_x86_t* as, int src_r32_a, int src_r32_b);
+void asm_x86_test_r8_with_r8(asm_x86_t* as, int src_r32_a, int src_r32_b);
+void asm_x86_setcc_r8(asm_x86_t* as, mp_uint_t jcc_type, int dest_r8);
+void asm_x86_jmp_label(asm_x86_t* as, mp_uint_t label);
+void asm_x86_jcc_label(asm_x86_t* as, mp_uint_t jcc_type, mp_uint_t label);
+void asm_x86_entry(asm_x86_t* as, mp_uint_t num_locals);
+void asm_x86_exit(asm_x86_t* as);
+void asm_x86_mov_arg_to_r32(asm_x86_t *as, int src_arg_num, int dest_r32);
+void asm_x86_mov_local_to_r32(asm_x86_t* as, int src_local_num, int dest_r32);
+void asm_x86_mov_r32_to_local(asm_x86_t* as, int src_r32, int dest_local_num);
+void asm_x86_mov_local_addr_to_r32(asm_x86_t* as, int local_num, int dest_r32);
+void asm_x86_call_ind(asm_x86_t* as, void* ptr, mp_uint_t n_args, int temp_r32);
+
+#if GENERIC_ASM_API
+
+// The following macros provide a (mostly) arch-independent API to
+// generate native code, and are used by the native emitter.
+
+#define ASM_WORD_SIZE (4)
+
+#define REG_RET ASM_X86_REG_EAX
+#define REG_ARG_1 ASM_X86_REG_ARG_1
+#define REG_ARG_2 ASM_X86_REG_ARG_2
+#define REG_ARG_3 ASM_X86_REG_ARG_3
+#define REG_ARG_4 ASM_X86_REG_ARG_4
+#define REG_ARG_5 ASM_X86_REG_ARG_5
+
+// caller-save, so can be used as temporaries
+#define REG_TEMP0 ASM_X86_REG_EAX
+#define REG_TEMP1 ASM_X86_REG_ECX
+#define REG_TEMP2 ASM_X86_REG_EDX
+
+// callee-save, so can be used as locals
+#define REG_LOCAL_1 ASM_X86_REG_EBX
+#define REG_LOCAL_2 ASM_X86_REG_ESI
+#define REG_LOCAL_3 ASM_X86_REG_EDI
+#define REG_LOCAL_NUM (3)
+
+#define ASM_T               asm_x86_t
+#define ASM_END_PASS        asm_x86_end_pass
+#define ASM_ENTRY           asm_x86_entry
+#define ASM_EXIT            asm_x86_exit
+
+#define ASM_JUMP            asm_x86_jmp_label
+#define ASM_JUMP_IF_REG_ZERO(as, reg, label) \
+    do { \
+        asm_x86_test_r8_with_r8(as, reg, reg); \
+        asm_x86_jcc_label(as, ASM_X86_CC_JZ, label); \
+    } while (0)
+#define ASM_JUMP_IF_REG_NONZERO(as, reg, label) \
+    do { \
+        asm_x86_test_r8_with_r8(as, reg, reg); \
+        asm_x86_jcc_label(as, ASM_X86_CC_JNZ, label); \
+    } while (0)
+#define ASM_JUMP_IF_REG_EQ(as, reg1, reg2, label) \
+    do { \
+        asm_x86_cmp_r32_with_r32(as, reg1, reg2); \
+        asm_x86_jcc_label(as, ASM_X86_CC_JE, label); \
+    } while (0)
+#define ASM_CALL_IND(as, ptr, idx) asm_x86_call_ind(as, ptr, mp_f_n_args[idx], ASM_X86_REG_EAX)
+
+#define ASM_MOV_REG_TO_LOCAL        asm_x86_mov_r32_to_local
+#define ASM_MOV_IMM_TO_REG          asm_x86_mov_i32_to_r32
+#define ASM_MOV_ALIGNED_IMM_TO_REG  asm_x86_mov_i32_to_r32_aligned
+#define ASM_MOV_IMM_TO_LOCAL_USING(as, imm, local_num, reg_temp) \
+    do { \
+        asm_x86_mov_i32_to_r32(as, (imm), (reg_temp)); \
+        asm_x86_mov_r32_to_local(as, (reg_temp), (local_num)); \
+    } while (false)
+#define ASM_MOV_LOCAL_TO_REG        asm_x86_mov_local_to_r32
+#define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_x86_mov_r32_r32((as), (reg_dest), (reg_src))
+#define ASM_MOV_LOCAL_ADDR_TO_REG   asm_x86_mov_local_addr_to_r32
+
+#define ASM_LSL_REG(as, reg) asm_x86_shl_r32_cl((as), (reg))
+#define ASM_ASR_REG(as, reg) asm_x86_sar_r32_cl((as), (reg))
+#define ASM_OR_REG_REG(as, reg_dest, reg_src) asm_x86_or_r32_r32((as), (reg_dest), (reg_src))
+#define ASM_XOR_REG_REG(as, reg_dest, reg_src) asm_x86_xor_r32_r32((as), (reg_dest), (reg_src))
+#define ASM_AND_REG_REG(as, reg_dest, reg_src) asm_x86_and_r32_r32((as), (reg_dest), (reg_src))
+#define ASM_ADD_REG_REG(as, reg_dest, reg_src) asm_x86_add_r32_r32((as), (reg_dest), (reg_src))
+#define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_x86_sub_r32_r32((as), (reg_dest), (reg_src))
+#define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_x86_mul_r32_r32((as), (reg_dest), (reg_src))
+
+#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) asm_x86_mov_mem32_to_r32((as), (reg_base), 0, (reg_dest))
+#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_x86_mov_mem32_to_r32((as), (reg_base), 4 * (word_offset), (reg_dest))
+#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_x86_mov_mem8_to_r32zx((as), (reg_base), 0, (reg_dest))
+#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_x86_mov_mem16_to_r32zx((as), (reg_base), 0, (reg_dest))
+#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_x86_mov_mem32_to_r32((as), (reg_base), 0, (reg_dest))
+
+#define ASM_STORE_REG_REG(as, reg_src, reg_base) asm_x86_mov_r32_to_mem32((as), (reg_src), (reg_base), 0)
+#define ASM_STORE_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) asm_x86_mov_r32_to_mem32((as), (reg_src), (reg_base), 4 * (word_offset))
+#define ASM_STORE8_REG_REG(as, reg_src, reg_base) asm_x86_mov_r8_to_mem8((as), (reg_src), (reg_base), 0)
+#define ASM_STORE16_REG_REG(as, reg_src, reg_base) asm_x86_mov_r16_to_mem16((as), (reg_src), (reg_base), 0)
+#define ASM_STORE32_REG_REG(as, reg_src, reg_base) asm_x86_mov_r32_to_mem32((as), (reg_src), (reg_base), 0)
+
+#endif // GENERIC_ASM_API
+
+#endif // MICROPY_INCLUDED_PY_ASMX86_H

+ 174 - 0
py/asmxtensa.c

@@ -0,0 +1,174 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+
+#include "py/mpconfig.h"
+
+// wrapper around everything in this file
+#if MICROPY_EMIT_XTENSA || MICROPY_EMIT_INLINE_XTENSA
+
+#include "py/asmxtensa.h"
+
+#define WORD_SIZE (4)
+#define SIGNED_FIT8(x) ((((x) & 0xffffff80) == 0) || (((x) & 0xffffff80) == 0xffffff80))
+#define SIGNED_FIT12(x) ((((x) & 0xfffff800) == 0) || (((x) & 0xfffff800) == 0xfffff800))
+
+void asm_xtensa_end_pass(asm_xtensa_t *as) {
+    as->num_const = as->cur_const;
+    as->cur_const = 0;
+
+    #if 0
+    // make a hex dump of the machine code
+    if (as->base.pass == MP_ASM_PASS_EMIT) {
+        uint8_t *d = as->base.code_base;
+        printf("XTENSA ASM:");
+        for (int i = 0; i < ((as->base.code_size + 15) & ~15); ++i) {
+            if (i % 16 == 0) {
+                printf("\n%08x:", (uint32_t)&d[i]);
+            }
+            if (i % 2 == 0) {
+                printf(" ");
+            }
+            printf("%02x", d[i]);
+        }
+        printf("\n");
+    }
+    #endif
+}
+
+void asm_xtensa_entry(asm_xtensa_t *as, int num_locals) {
+    // jump over the constants
+    asm_xtensa_op_j(as, as->num_const * WORD_SIZE + 4 - 4);
+    mp_asm_base_get_cur_to_write_bytes(&as->base, 1); // padding/alignment byte
+    as->const_table = (uint32_t*)mp_asm_base_get_cur_to_write_bytes(&as->base, as->num_const * 4);
+
+    // adjust the stack-pointer to store a0, a12, a13, a14 and locals, 16-byte aligned
+    as->stack_adjust = (((4 + num_locals) * WORD_SIZE) + 15) & ~15;
+    asm_xtensa_op_addi(as, ASM_XTENSA_REG_A1, ASM_XTENSA_REG_A1, -as->stack_adjust);
+
+    // save return value (a0) and callee-save registers (a12, a13, a14)
+    asm_xtensa_op_s32i_n(as, ASM_XTENSA_REG_A0, ASM_XTENSA_REG_A1, 0);
+    asm_xtensa_op_s32i_n(as, ASM_XTENSA_REG_A12, ASM_XTENSA_REG_A1, 1);
+    asm_xtensa_op_s32i_n(as, ASM_XTENSA_REG_A13, ASM_XTENSA_REG_A1, 2);
+    asm_xtensa_op_s32i_n(as, ASM_XTENSA_REG_A14, ASM_XTENSA_REG_A1, 3);
+}
+
+void asm_xtensa_exit(asm_xtensa_t *as) {
+    // restore registers
+    asm_xtensa_op_l32i_n(as, ASM_XTENSA_REG_A14, ASM_XTENSA_REG_A1, 3);
+    asm_xtensa_op_l32i_n(as, ASM_XTENSA_REG_A13, ASM_XTENSA_REG_A1, 2);
+    asm_xtensa_op_l32i_n(as, ASM_XTENSA_REG_A12, ASM_XTENSA_REG_A1, 1);
+    asm_xtensa_op_l32i_n(as, ASM_XTENSA_REG_A0, ASM_XTENSA_REG_A1, 0);
+
+    // restore stack-pointer and return
+    asm_xtensa_op_addi(as, ASM_XTENSA_REG_A1, ASM_XTENSA_REG_A1, as->stack_adjust);
+    asm_xtensa_op_ret_n(as);
+}
+
+STATIC uint32_t get_label_dest(asm_xtensa_t *as, uint label) {
+    assert(label < as->base.max_num_labels);
+    return as->base.label_offsets[label];
+}
+
+void asm_xtensa_op16(asm_xtensa_t *as, uint16_t op) {
+    uint8_t *c = mp_asm_base_get_cur_to_write_bytes(&as->base, 2);
+    if (c != NULL) {
+        c[0] = op;
+        c[1] = op >> 8;
+    }
+}
+
+void asm_xtensa_op24(asm_xtensa_t *as, uint32_t op) {
+    uint8_t *c = mp_asm_base_get_cur_to_write_bytes(&as->base, 3);
+    if (c != NULL) {
+        c[0] = op;
+        c[1] = op >> 8;
+        c[2] = op >> 16;
+    }
+}
+
+void asm_xtensa_j_label(asm_xtensa_t *as, uint label) {
+    uint32_t dest = get_label_dest(as, label);
+    int32_t rel = dest - as->base.code_offset - 4;
+    // we assume rel, as a signed int, fits in 18-bits
+    asm_xtensa_op_j(as, rel);
+}
+
+void asm_xtensa_bccz_reg_label(asm_xtensa_t *as, uint cond, uint reg, uint label) {
+    uint32_t dest = get_label_dest(as, label);
+    int32_t rel = dest - as->base.code_offset - 4;
+    if (as->base.pass == MP_ASM_PASS_EMIT && !SIGNED_FIT12(rel)) {
+        printf("ERROR: xtensa bccz out of range\n");
+    }
+    asm_xtensa_op_bccz(as, cond, reg, rel);
+}
+
+void asm_xtensa_bcc_reg_reg_label(asm_xtensa_t *as, uint cond, uint reg1, uint reg2, uint label) {
+    uint32_t dest = get_label_dest(as, label);
+    int32_t rel = dest - as->base.code_offset - 4;
+    if (as->base.pass == MP_ASM_PASS_EMIT && !SIGNED_FIT8(rel)) {
+        printf("ERROR: xtensa bcc out of range\n");
+    }
+    asm_xtensa_op_bcc(as, cond, reg1, reg2, rel);
+}
+
+// convenience function; reg_dest must be different from reg_src[12]
+void asm_xtensa_setcc_reg_reg_reg(asm_xtensa_t *as, uint cond, uint reg_dest, uint reg_src1, uint reg_src2) {
+    asm_xtensa_op_movi_n(as, reg_dest, 1);
+    asm_xtensa_op_bcc(as, cond, reg_src1, reg_src2, 1);
+    asm_xtensa_op_movi_n(as, reg_dest, 0);
+}
+
+void asm_xtensa_mov_reg_i32(asm_xtensa_t *as, uint reg_dest, uint32_t i32) {
+    if (SIGNED_FIT12(i32)) {
+        asm_xtensa_op_movi(as, reg_dest, i32);
+    } else {
+        // load the constant
+        asm_xtensa_op_l32r(as, reg_dest, as->base.code_offset, 4 + as->cur_const * WORD_SIZE);
+        // store the constant in the table
+        if (as->const_table != NULL) {
+            as->const_table[as->cur_const] = i32;
+        }
+        ++as->cur_const;
+    }
+}
+
+void asm_xtensa_mov_local_reg(asm_xtensa_t *as, int local_num, uint reg_src) {
+    asm_xtensa_op_s32i(as, reg_src, ASM_XTENSA_REG_A1, 4 + local_num);
+}
+
+void asm_xtensa_mov_reg_local(asm_xtensa_t *as, uint reg_dest, int local_num) {
+    asm_xtensa_op_l32i(as, reg_dest, ASM_XTENSA_REG_A1, 4 + local_num);
+}
+
+void asm_xtensa_mov_reg_local_addr(asm_xtensa_t *as, uint reg_dest, int local_num) {
+    asm_xtensa_op_mov_n(as, reg_dest, ASM_XTENSA_REG_A1);
+    asm_xtensa_op_addi(as, reg_dest, reg_dest, (4 + local_num) * WORD_SIZE);
+}
+
+#endif // MICROPY_EMIT_XTENSA || MICROPY_EMIT_INLINE_XTENSA

+ 324 - 0
py/asmxtensa.h

@@ -0,0 +1,324 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_PY_ASMXTENSA_H
+#define MICROPY_INCLUDED_PY_ASMXTENSA_H
+
+#include "py/asmbase.h"
+
+// calling conventions:
+// up to 6 args in a2-a7
+// return value in a2
+// PC stored in a0
+// stack pointer is a1, stack full descending, is aligned to 16 bytes
+// callee save: a1, a12, a13, a14, a15
+// caller save: a3
+
+#define ASM_XTENSA_REG_A0  (0)
+#define ASM_XTENSA_REG_A1  (1)
+#define ASM_XTENSA_REG_A2  (2)
+#define ASM_XTENSA_REG_A3  (3)
+#define ASM_XTENSA_REG_A4  (4)
+#define ASM_XTENSA_REG_A5  (5)
+#define ASM_XTENSA_REG_A6  (6)
+#define ASM_XTENSA_REG_A7  (7)
+#define ASM_XTENSA_REG_A8  (8)
+#define ASM_XTENSA_REG_A9  (9)
+#define ASM_XTENSA_REG_A10 (10)
+#define ASM_XTENSA_REG_A11 (11)
+#define ASM_XTENSA_REG_A12 (12)
+#define ASM_XTENSA_REG_A13 (13)
+#define ASM_XTENSA_REG_A14 (14)
+#define ASM_XTENSA_REG_A15 (15)
+
+// for bccz
+#define ASM_XTENSA_CCZ_EQ (0)
+#define ASM_XTENSA_CCZ_NE (1)
+
+// for bcc and setcc
+#define ASM_XTENSA_CC_NONE  (0)
+#define ASM_XTENSA_CC_EQ    (1)
+#define ASM_XTENSA_CC_LT    (2)
+#define ASM_XTENSA_CC_LTU   (3)
+#define ASM_XTENSA_CC_ALL   (4)
+#define ASM_XTENSA_CC_BC    (5)
+#define ASM_XTENSA_CC_ANY   (8)
+#define ASM_XTENSA_CC_NE    (9)
+#define ASM_XTENSA_CC_GE    (10)
+#define ASM_XTENSA_CC_GEU   (11)
+#define ASM_XTENSA_CC_NALL  (12)
+#define ASM_XTENSA_CC_BS    (13)
+
+// macros for encoding instructions (little endian versions)
+#define ASM_XTENSA_ENCODE_RRR(op0, op1, op2, r, s, t) \
+    ((((uint32_t)op2) << 20) | (((uint32_t)op1) << 16) | ((r) << 12) | ((s) << 8) | ((t) << 4) | (op0))
+#define ASM_XTENSA_ENCODE_RRI4(op0, op1, r, s, t, imm4) \
+    (((imm4) << 20) | ((op1) << 16) | ((r) << 12) | ((s) << 8) | ((t) << 4) | (op0))
+#define ASM_XTENSA_ENCODE_RRI8(op0, r, s, t, imm8) \
+    ((((uint32_t)imm8) << 16) | ((r) << 12) | ((s) << 8) | ((t) << 4) | (op0))
+#define ASM_XTENSA_ENCODE_RI16(op0, t, imm16) \
+    (((imm16) << 8) | ((t) << 4) | (op0))
+#define ASM_XTENSA_ENCODE_RSR(op0, op1, op2, rs, t) \
+    (((op2) << 20) | ((op1) << 16) | ((rs) << 8) | ((t) << 4) | (op0))
+#define ASM_XTENSA_ENCODE_CALL(op0, n, offset) \
+    (((offset) << 6) | ((n) << 4) | (op0))
+#define ASM_XTENSA_ENCODE_CALLX(op0, op1, op2, r, s, m, n) \
+    ((((uint32_t)op2) << 20) | (((uint32_t)op1) << 16) | ((r) << 12) | ((s) << 8) | ((m) << 6) | ((n) << 4) | (op0))
+#define ASM_XTENSA_ENCODE_BRI8(op0, r, s, m, n, imm8) \
+    (((imm8) << 16) | ((r) << 12) | ((s) << 8) | ((m) << 6) | ((n) << 4) | (op0))
+#define ASM_XTENSA_ENCODE_BRI12(op0, s, m, n, imm12) \
+    (((imm12) << 12) | ((s) << 8) | ((m) << 6) | ((n) << 4) | (op0))
+#define ASM_XTENSA_ENCODE_RRRN(op0, r, s, t) \
+    (((r) << 12) | ((s) << 8) | ((t) << 4) | (op0))
+#define ASM_XTENSA_ENCODE_RI7(op0, s, imm7) \
+    ((((imm7) & 0xf) << 12) | ((s) << 8) | ((imm7) & 0x70) | (op0))
+
+typedef struct _asm_xtensa_t {
+    mp_asm_base_t base;
+    uint32_t cur_const;
+    uint32_t num_const;
+    uint32_t *const_table;
+    uint32_t stack_adjust;
+} asm_xtensa_t;
+
+void asm_xtensa_end_pass(asm_xtensa_t *as);
+
+void asm_xtensa_entry(asm_xtensa_t *as, int num_locals);
+void asm_xtensa_exit(asm_xtensa_t *as);
+
+void asm_xtensa_op16(asm_xtensa_t *as, uint16_t op);
+void asm_xtensa_op24(asm_xtensa_t *as, uint32_t op);
+
+// raw instructions
+
+static inline void asm_xtensa_op_add(asm_xtensa_t *as, uint reg_dest, uint reg_src_a, uint reg_src_b) {
+    asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 0, 8, reg_dest, reg_src_a, reg_src_b));
+}
+
+static inline void asm_xtensa_op_addi(asm_xtensa_t *as, uint reg_dest, uint reg_src, int imm8) {
+    asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 12, reg_dest, reg_src, imm8 & 0xff));
+}
+
+static inline void asm_xtensa_op_and(asm_xtensa_t *as, uint reg_dest, uint reg_src_a, uint reg_src_b) {
+    asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 0, 1, reg_dest, reg_src_a, reg_src_b));
+}
+
+static inline void asm_xtensa_op_bcc(asm_xtensa_t *as, uint cond, uint reg_src1, uint reg_src2, int32_t rel8) {
+    asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(7, cond, reg_src1, reg_src2, rel8 & 0xff));
+}
+
+static inline void asm_xtensa_op_bccz(asm_xtensa_t *as, uint cond, uint reg_src, int32_t rel12) {
+    asm_xtensa_op24(as, ASM_XTENSA_ENCODE_BRI12(6, reg_src, cond, 1, rel12 & 0xfff));
+}
+
+static inline void asm_xtensa_op_callx0(asm_xtensa_t *as, uint reg) {
+    asm_xtensa_op24(as, ASM_XTENSA_ENCODE_CALLX(0, 0, 0, 0, reg, 3, 0));
+}
+
+static inline void asm_xtensa_op_j(asm_xtensa_t *as, int32_t rel18) {
+    asm_xtensa_op24(as, ASM_XTENSA_ENCODE_CALL(6, 0, rel18 & 0x3ffff));
+}
+
+static inline void asm_xtensa_op_jx(asm_xtensa_t *as, uint reg) {
+    asm_xtensa_op24(as, ASM_XTENSA_ENCODE_CALLX(0, 0, 0, 0, reg, 2, 2));
+}
+
+static inline void asm_xtensa_op_l8ui(asm_xtensa_t *as, uint reg_dest, uint reg_base, uint byte_offset) {
+    asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 0, reg_base, reg_dest, byte_offset & 0xff));
+}
+
+static inline void asm_xtensa_op_l16ui(asm_xtensa_t *as, uint reg_dest, uint reg_base, uint half_word_offset) {
+    asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 1, reg_base, reg_dest, half_word_offset & 0xff));
+}
+
+static inline void asm_xtensa_op_l32i(asm_xtensa_t *as, uint reg_dest, uint reg_base, uint word_offset) {
+    asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 2, reg_base, reg_dest, word_offset & 0xff));
+}
+
+static inline void asm_xtensa_op_l32i_n(asm_xtensa_t *as, uint reg_dest, uint reg_base, uint word_offset) {
+    asm_xtensa_op16(as, ASM_XTENSA_ENCODE_RRRN(8, word_offset & 0xf, reg_base, reg_dest));
+}
+
+static inline void asm_xtensa_op_l32r(asm_xtensa_t *as, uint reg_dest, uint32_t op_off, uint32_t dest_off) {
+    asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RI16(1, reg_dest, ((dest_off - ((op_off + 3) & ~3)) >> 2) & 0xffff));
+}
+
+static inline void asm_xtensa_op_mov_n(asm_xtensa_t *as, uint reg_dest, uint reg_src) {
+    asm_xtensa_op16(as, ASM_XTENSA_ENCODE_RRRN(13, 0, reg_src, reg_dest));
+}
+
+static inline void asm_xtensa_op_movi(asm_xtensa_t *as, uint reg_dest, int32_t imm12) {
+    asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 10, (imm12 >> 8) & 0xf, reg_dest, imm12 & 0xff));
+}
+
+static inline void asm_xtensa_op_movi_n(asm_xtensa_t *as, uint reg_dest, int imm4) {
+    asm_xtensa_op16(as, ASM_XTENSA_ENCODE_RI7(12, reg_dest, imm4));
+}
+
+static inline void asm_xtensa_op_mull(asm_xtensa_t *as, uint reg_dest, uint reg_src_a, uint reg_src_b) {
+    asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 2, 8, reg_dest, reg_src_a, reg_src_b));
+}
+
+static inline void asm_xtensa_op_or(asm_xtensa_t *as, uint reg_dest, uint reg_src_a, uint reg_src_b) {
+    asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 0, 2, reg_dest, reg_src_a, reg_src_b));
+}
+
+static inline void asm_xtensa_op_ret_n(asm_xtensa_t *as) {
+    asm_xtensa_op16(as, ASM_XTENSA_ENCODE_RRRN(13, 15, 0, 0));
+}
+
+static inline void asm_xtensa_op_s8i(asm_xtensa_t *as, uint reg_src, uint reg_base, uint byte_offset) {
+    asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 4, reg_base, reg_src, byte_offset & 0xff));
+}
+
+static inline void asm_xtensa_op_s16i(asm_xtensa_t *as, uint reg_src, uint reg_base, uint half_word_offset) {
+    asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 5, reg_base, reg_src, half_word_offset & 0xff));
+}
+
+static inline void asm_xtensa_op_s32i(asm_xtensa_t *as, uint reg_src, uint reg_base, uint word_offset) {
+    asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 6, reg_base, reg_src, word_offset & 0xff));
+}
+
+static inline void asm_xtensa_op_s32i_n(asm_xtensa_t *as, uint reg_src, uint reg_base, uint word_offset) {
+    asm_xtensa_op16(as, ASM_XTENSA_ENCODE_RRRN(9, word_offset & 0xf, reg_base, reg_src));
+}
+
+static inline void asm_xtensa_op_sll(asm_xtensa_t *as, uint reg_dest, uint reg_src) {
+    asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 1, 10, reg_dest, reg_src, 0));
+}
+
+static inline void asm_xtensa_op_sra(asm_xtensa_t *as, uint reg_dest, uint reg_src) {
+    asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 1, 11, reg_dest, 0, reg_src));
+}
+
+static inline void asm_xtensa_op_ssl(asm_xtensa_t *as, uint reg_src) {
+    asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 0, 4, 1, reg_src, 0));
+}
+
+static inline void asm_xtensa_op_ssr(asm_xtensa_t *as, uint reg_src) {
+    asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 0, 4, 0, reg_src, 0));
+}
+
+static inline void asm_xtensa_op_sub(asm_xtensa_t *as, uint reg_dest, uint reg_src_a, uint reg_src_b) {
+    asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 0, 12, reg_dest, reg_src_a, reg_src_b));
+}
+
+static inline void asm_xtensa_op_xor(asm_xtensa_t *as, uint reg_dest, uint reg_src_a, uint reg_src_b) {
+    asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 0, 3, reg_dest, reg_src_a, reg_src_b));
+}
+
+// convenience functions
+void asm_xtensa_j_label(asm_xtensa_t *as, uint label);
+void asm_xtensa_bccz_reg_label(asm_xtensa_t *as, uint cond, uint reg, uint label);
+void asm_xtensa_bcc_reg_reg_label(asm_xtensa_t *as, uint cond, uint reg1, uint reg2, uint label);
+void asm_xtensa_setcc_reg_reg_reg(asm_xtensa_t *as, uint cond, uint reg_dest, uint reg_src1, uint reg_src2);
+void asm_xtensa_mov_reg_i32(asm_xtensa_t *as, uint reg_dest, uint32_t i32);
+void asm_xtensa_mov_local_reg(asm_xtensa_t *as, int local_num, uint reg_src);
+void asm_xtensa_mov_reg_local(asm_xtensa_t *as, uint reg_dest, int local_num);
+void asm_xtensa_mov_reg_local_addr(asm_xtensa_t *as, uint reg_dest, int local_num);
+
+#if GENERIC_ASM_API
+
+// The following macros provide a (mostly) arch-independent API to
+// generate native code, and are used by the native emitter.
+
+#define ASM_WORD_SIZE (4)
+
+#define REG_RET ASM_XTENSA_REG_A2
+#define REG_ARG_1 ASM_XTENSA_REG_A2
+#define REG_ARG_2 ASM_XTENSA_REG_A3
+#define REG_ARG_3 ASM_XTENSA_REG_A4
+#define REG_ARG_4 ASM_XTENSA_REG_A5
+#define REG_ARG_5 ASM_XTENSA_REG_A6
+
+#define REG_TEMP0 ASM_XTENSA_REG_A2
+#define REG_TEMP1 ASM_XTENSA_REG_A3
+#define REG_TEMP2 ASM_XTENSA_REG_A4
+
+#define REG_LOCAL_1 ASM_XTENSA_REG_A12
+#define REG_LOCAL_2 ASM_XTENSA_REG_A13
+#define REG_LOCAL_3 ASM_XTENSA_REG_A14
+#define REG_LOCAL_NUM (3)
+
+#define ASM_T               asm_xtensa_t
+#define ASM_END_PASS        asm_xtensa_end_pass
+#define ASM_ENTRY           asm_xtensa_entry
+#define ASM_EXIT            asm_xtensa_exit
+
+#define ASM_JUMP            asm_xtensa_j_label
+#define ASM_JUMP_IF_REG_ZERO(as, reg, label) \
+    asm_xtensa_bccz_reg_label(as, ASM_XTENSA_CCZ_EQ, reg, label)
+#define ASM_JUMP_IF_REG_NONZERO(as, reg, label) \
+    asm_xtensa_bccz_reg_label(as, ASM_XTENSA_CCZ_NE, reg, label)
+#define ASM_JUMP_IF_REG_EQ(as, reg1, reg2, label) \
+    asm_xtensa_bcc_reg_reg_label(as, ASM_XTENSA_CC_EQ, reg1, reg2, label)
+#define ASM_CALL_IND(as, ptr, idx) \
+    do { \
+        asm_xtensa_mov_reg_i32(as, ASM_XTENSA_REG_A0, (uint32_t)ptr); \
+        asm_xtensa_op_callx0(as, ASM_XTENSA_REG_A0); \
+    } while (0)
+
+#define ASM_MOV_REG_TO_LOCAL(as, reg, local_num) asm_xtensa_mov_local_reg(as, (local_num), (reg))
+#define ASM_MOV_IMM_TO_REG(as, imm, reg) asm_xtensa_mov_reg_i32(as, (reg), (imm))
+#define ASM_MOV_ALIGNED_IMM_TO_REG(as, imm, reg) asm_xtensa_mov_reg_i32(as, (reg), (imm))
+#define ASM_MOV_IMM_TO_LOCAL_USING(as, imm, local_num, reg_temp) \
+    do { \
+        asm_xtensa_mov_reg_i32(as, (reg_temp), (imm)); \
+        asm_xtensa_mov_local_reg(as, (local_num), (reg_temp)); \
+    } while (0)
+#define ASM_MOV_LOCAL_TO_REG(as, local_num, reg) asm_xtensa_mov_reg_local(as, (reg), (local_num))
+#define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_mov_n((as), (reg_dest), (reg_src))
+#define ASM_MOV_LOCAL_ADDR_TO_REG(as, local_num, reg) asm_xtensa_mov_reg_local_addr(as, (reg), (local_num))
+
+#define ASM_LSL_REG_REG(as, reg_dest, reg_shift) \
+    do { \
+        asm_xtensa_op_ssl((as), (reg_shift)); \
+        asm_xtensa_op_sll((as), (reg_dest), (reg_dest)); \
+    } while (0)
+#define ASM_ASR_REG_REG(as, reg_dest, reg_shift) \
+    do { \
+        asm_xtensa_op_ssr((as), (reg_shift)); \
+        asm_xtensa_op_sra((as), (reg_dest), (reg_dest)); \
+    } while (0)
+#define ASM_OR_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_or((as), (reg_dest), (reg_dest), (reg_src))
+#define ASM_XOR_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_xor((as), (reg_dest), (reg_dest), (reg_src))
+#define ASM_AND_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_and((as), (reg_dest), (reg_dest), (reg_src))
+#define ASM_ADD_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_add((as), (reg_dest), (reg_dest), (reg_src))
+#define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_sub((as), (reg_dest), (reg_dest), (reg_src))
+#define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_mull((as), (reg_dest), (reg_dest), (reg_src))
+
+#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_xtensa_op_l32i_n((as), (reg_dest), (reg_base), (word_offset))
+#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_xtensa_op_l8ui((as), (reg_dest), (reg_base), 0)
+#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_xtensa_op_l16ui((as), (reg_dest), (reg_base), 0)
+#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_xtensa_op_l32i_n((as), (reg_dest), (reg_base), 0)
+
+#define ASM_STORE_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_xtensa_op_s32i_n((as), (reg_dest), (reg_base), (word_offset))
+#define ASM_STORE8_REG_REG(as, reg_src, reg_base) asm_xtensa_op_s8i((as), (reg_src), (reg_base), 0)
+#define ASM_STORE16_REG_REG(as, reg_src, reg_base) asm_xtensa_op_s16i((as), (reg_src), (reg_base), 0)
+#define ASM_STORE32_REG_REG(as, reg_src, reg_base) asm_xtensa_op_s32i_n((as), (reg_src), (reg_base), 0)
+
+#endif // GENERIC_ASM_API
+
+#endif // MICROPY_INCLUDED_PY_ASMXTENSA_H

+ 415 - 0
py/bc.c

@@ -0,0 +1,415 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014 Damien P. George
+ * Copyright (c) 2014 Paul Sokolovsky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+
+#include "py/runtime.h"
+#include "py/bc0.h"
+#include "py/bc.h"
+
+#if MICROPY_DEBUG_VERBOSE // print debugging info
+#define DEBUG_PRINT (1)
+#else // don't print debugging info
+#define DEBUG_PRINT (0)
+#define DEBUG_printf(...) (void)0
+#endif
+
+mp_uint_t mp_decode_uint(const byte **ptr) {
+    mp_uint_t unum = 0;
+    byte val;
+    const byte *p = *ptr;
+    do {
+        val = *p++;
+        unum = (unum << 7) | (val & 0x7f);
+    } while ((val & 0x80) != 0);
+    *ptr = p;
+    return unum;
+}
+
+// This function is used to help reduce stack usage at the caller, for the case when
+// the caller doesn't need to increase the ptr argument.  If ptr is a local variable
+// and the caller uses mp_decode_uint(&ptr) instead of this function, then the compiler
+// must allocate a slot on the stack for ptr, and this slot cannot be reused for
+// anything else in the function because the pointer may have been stored in a global
+// and reused later in the function.
+mp_uint_t mp_decode_uint_value(const byte *ptr) {
+    return mp_decode_uint(&ptr);
+}
+
+// This function is used to help reduce stack usage at the caller, for the case when
+// the caller doesn't need the actual value and just wants to skip over it.
+const byte *mp_decode_uint_skip(const byte *ptr) {
+    while ((*ptr++) & 0x80) {
+    }
+    return ptr;
+}
+
+STATIC NORETURN void fun_pos_args_mismatch(mp_obj_fun_bc_t *f, size_t expected, size_t given) {
+#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE
+    // generic message, used also for other argument issues
+    (void)f;
+    (void)expected;
+    (void)given;
+    mp_arg_error_terse_mismatch();
+#elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NORMAL
+    (void)f;
+    nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
+        "function takes %d positional arguments but %d were given", expected, given));
+#elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED
+    nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
+        "%q() takes %d positional arguments but %d were given",
+        mp_obj_fun_get_name(MP_OBJ_FROM_PTR(f)), expected, given));
+#endif
+}
+
+#if DEBUG_PRINT
+STATIC void dump_args(const mp_obj_t *a, size_t sz) {
+    DEBUG_printf("%p: ", a);
+    for (size_t i = 0; i < sz; i++) {
+        DEBUG_printf("%p ", a[i]);
+    }
+    DEBUG_printf("\n");
+}
+#else
+#define dump_args(...) (void)0
+#endif
+
+// On entry code_state should be allocated somewhere (stack/heap) and
+// contain the following valid entries:
+//    - code_state->fun_bc should contain a pointer to the function object
+//    - code_state->ip should contain the offset in bytes from the pointer
+//      code_state->fun_bc->bytecode to the entry n_state (0 for bytecode, non-zero for native)
+void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+    // This function is pretty complicated.  It's main aim is to be efficient in speed and RAM
+    // usage for the common case of positional only args.
+
+    // get the function object that we want to set up (could be bytecode or native code)
+    mp_obj_fun_bc_t *self = code_state->fun_bc;
+
+    // ip comes in as an offset into bytecode, so turn it into a true pointer
+    code_state->ip = self->bytecode + (size_t)code_state->ip;
+
+    #if MICROPY_STACKLESS
+    code_state->prev = NULL;
+    #endif
+
+    // get params
+    size_t n_state = mp_decode_uint(&code_state->ip);
+    code_state->ip = mp_decode_uint_skip(code_state->ip); // skip n_exc_stack
+    size_t scope_flags = *code_state->ip++;
+    size_t n_pos_args = *code_state->ip++;
+    size_t n_kwonly_args = *code_state->ip++;
+    size_t n_def_pos_args = *code_state->ip++;
+
+    code_state->sp = &code_state->state[0] - 1;
+    code_state->exc_sp = (mp_exc_stack_t*)(code_state->state + n_state) - 1;
+
+    // zero out the local stack to begin with
+    memset(code_state->state, 0, n_state * sizeof(*code_state->state));
+
+    const mp_obj_t *kwargs = args + n_args;
+
+    // var_pos_kw_args points to the stack where the var-args tuple, and var-kw dict, should go (if they are needed)
+    mp_obj_t *var_pos_kw_args = &code_state->state[n_state - 1 - n_pos_args - n_kwonly_args];
+
+    // check positional arguments
+
+    if (n_args > n_pos_args) {
+        // given more than enough arguments
+        if ((scope_flags & MP_SCOPE_FLAG_VARARGS) == 0) {
+            fun_pos_args_mismatch(self, n_pos_args, n_args);
+        }
+        // put extra arguments in varargs tuple
+        *var_pos_kw_args-- = mp_obj_new_tuple(n_args - n_pos_args, args + n_pos_args);
+        n_args = n_pos_args;
+    } else {
+        if ((scope_flags & MP_SCOPE_FLAG_VARARGS) != 0) {
+            DEBUG_printf("passing empty tuple as *args\n");
+            *var_pos_kw_args-- = mp_const_empty_tuple;
+        }
+        // Apply processing and check below only if we don't have kwargs,
+        // otherwise, kw handling code below has own extensive checks.
+        if (n_kw == 0 && (scope_flags & MP_SCOPE_FLAG_DEFKWARGS) == 0) {
+            if (n_args >= (size_t)(n_pos_args - n_def_pos_args)) {
+                // given enough arguments, but may need to use some default arguments
+                for (size_t i = n_args; i < n_pos_args; i++) {
+                    code_state->state[n_state - 1 - i] = self->extra_args[i - (n_pos_args - n_def_pos_args)];
+                }
+            } else {
+                fun_pos_args_mismatch(self, n_pos_args - n_def_pos_args, n_args);
+            }
+        }
+    }
+
+    // copy positional args into state
+    for (size_t i = 0; i < n_args; i++) {
+        code_state->state[n_state - 1 - i] = args[i];
+    }
+
+    // check keyword arguments
+
+    if (n_kw != 0 || (scope_flags & MP_SCOPE_FLAG_DEFKWARGS) != 0) {
+        DEBUG_printf("Initial args: ");
+        dump_args(code_state->state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args);
+
+        mp_obj_t dict = MP_OBJ_NULL;
+        if ((scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0) {
+            dict = mp_obj_new_dict(n_kw); // TODO: better go conservative with 0?
+            *var_pos_kw_args = dict;
+        }
+
+        // get pointer to arg_names array
+        const mp_obj_t *arg_names = (const mp_obj_t*)self->const_table;
+
+        for (size_t i = 0; i < n_kw; i++) {
+            // the keys in kwargs are expected to be qstr objects
+            mp_obj_t wanted_arg_name = kwargs[2 * i];
+            for (size_t j = 0; j < n_pos_args + n_kwonly_args; j++) {
+                if (wanted_arg_name == arg_names[j]) {
+                    if (code_state->state[n_state - 1 - j] != MP_OBJ_NULL) {
+                        nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
+                            "function got multiple values for argument '%q'", MP_OBJ_QSTR_VALUE(wanted_arg_name)));
+                    }
+                    code_state->state[n_state - 1 - j] = kwargs[2 * i + 1];
+                    goto continue2;
+                }
+            }
+            // Didn't find name match with positional args
+            if ((scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) == 0) {
+                if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) {
+                    mp_raise_TypeError("unexpected keyword argument");
+                } else {
+                    nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
+                        "unexpected keyword argument '%q'", MP_OBJ_QSTR_VALUE(wanted_arg_name)));
+                }
+            }
+            mp_obj_dict_store(dict, kwargs[2 * i], kwargs[2 * i + 1]);
+continue2:;
+        }
+
+        DEBUG_printf("Args with kws flattened: ");
+        dump_args(code_state->state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args);
+
+        // fill in defaults for positional args
+        mp_obj_t *d = &code_state->state[n_state - n_pos_args];
+        mp_obj_t *s = &self->extra_args[n_def_pos_args - 1];
+        for (size_t i = n_def_pos_args; i > 0; i--, d++, s--) {
+            if (*d == MP_OBJ_NULL) {
+                *d = *s;
+            }
+        }
+
+        DEBUG_printf("Args after filling default positional: ");
+        dump_args(code_state->state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args);
+
+        // Check that all mandatory positional args are specified
+        while (d < &code_state->state[n_state]) {
+            if (*d++ == MP_OBJ_NULL) {
+                nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
+                    "function missing required positional argument #%d", &code_state->state[n_state] - d));
+            }
+        }
+
+        // Check that all mandatory keyword args are specified
+        // Fill in default kw args if we have them
+        for (size_t i = 0; i < n_kwonly_args; i++) {
+            if (code_state->state[n_state - 1 - n_pos_args - i] == MP_OBJ_NULL) {
+                mp_map_elem_t *elem = NULL;
+                if ((scope_flags & MP_SCOPE_FLAG_DEFKWARGS) != 0) {
+                    elem = mp_map_lookup(&((mp_obj_dict_t*)MP_OBJ_TO_PTR(self->extra_args[n_def_pos_args]))->map, arg_names[n_pos_args + i], MP_MAP_LOOKUP);
+                }
+                if (elem != NULL) {
+                    code_state->state[n_state - 1 - n_pos_args - i] = elem->value;
+                } else {
+                    nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
+                        "function missing required keyword argument '%q'", MP_OBJ_QSTR_VALUE(arg_names[n_pos_args + i])));
+                }
+            }
+        }
+
+    } else {
+        // no keyword arguments given
+        if (n_kwonly_args != 0) {
+            mp_raise_TypeError("function missing keyword-only argument");
+        }
+        if ((scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0) {
+            *var_pos_kw_args = mp_obj_new_dict(0);
+        }
+    }
+
+    // get the ip and skip argument names
+    const byte *ip = code_state->ip;
+
+    // jump over code info (source file and line-number mapping)
+    ip += mp_decode_uint_value(ip);
+
+    // bytecode prelude: initialise closed over variables
+    size_t local_num;
+    while ((local_num = *ip++) != 255) {
+        code_state->state[n_state - 1 - local_num] =
+            mp_obj_new_cell(code_state->state[n_state - 1 - local_num]);
+    }
+
+    // now that we skipped over the prelude, set the ip for the VM
+    code_state->ip = ip;
+
+    DEBUG_printf("Calling: n_pos_args=%d, n_kwonly_args=%d\n", n_pos_args, n_kwonly_args);
+    dump_args(code_state->state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args);
+    dump_args(code_state->state, n_state);
+}
+
+#if MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE
+
+// The following table encodes the number of bytes that a specific opcode
+// takes up.  There are 3 special opcodes that always have an extra byte:
+//     MP_BC_MAKE_CLOSURE
+//     MP_BC_MAKE_CLOSURE_DEFARGS
+//     MP_BC_RAISE_VARARGS
+// There are 4 special opcodes that have an extra byte only when
+// MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE is enabled:
+//     MP_BC_LOAD_NAME
+//     MP_BC_LOAD_GLOBAL
+//     MP_BC_LOAD_ATTR
+//     MP_BC_STORE_ATTR
+#define OC4(a, b, c, d) (a | (b << 2) | (c << 4) | (d << 6))
+#define U (0) // undefined opcode
+#define B (MP_OPCODE_BYTE) // single byte
+#define Q (MP_OPCODE_QSTR) // single byte plus 2-byte qstr
+#define V (MP_OPCODE_VAR_UINT) // single byte plus variable encoded unsigned int
+#define O (MP_OPCODE_OFFSET) // single byte plus 2-byte bytecode offset
+STATIC const byte opcode_format_table[64] = {
+    OC4(U, U, U, U), // 0x00-0x03
+    OC4(U, U, U, U), // 0x04-0x07
+    OC4(U, U, U, U), // 0x08-0x0b
+    OC4(U, U, U, U), // 0x0c-0x0f
+    OC4(B, B, B, U), // 0x10-0x13
+    OC4(V, U, Q, V), // 0x14-0x17
+    OC4(B, V, V, Q), // 0x18-0x1b
+    OC4(Q, Q, Q, Q), // 0x1c-0x1f
+    OC4(B, B, V, V), // 0x20-0x23
+    OC4(Q, Q, Q, B), // 0x24-0x27
+    OC4(V, V, Q, Q), // 0x28-0x2b
+    OC4(U, U, U, U), // 0x2c-0x2f
+    OC4(B, B, B, B), // 0x30-0x33
+    OC4(B, O, O, O), // 0x34-0x37
+    OC4(O, O, U, U), // 0x38-0x3b
+    OC4(U, O, B, O), // 0x3c-0x3f
+    OC4(O, B, B, O), // 0x40-0x43
+    OC4(B, B, O, B), // 0x44-0x47
+    OC4(U, U, U, U), // 0x48-0x4b
+    OC4(U, U, U, U), // 0x4c-0x4f
+    OC4(V, V, U, V), // 0x50-0x53
+    OC4(B, U, V, V), // 0x54-0x57
+    OC4(V, V, V, B), // 0x58-0x5b
+    OC4(B, B, B, U), // 0x5c-0x5f
+    OC4(V, V, V, V), // 0x60-0x63
+    OC4(V, V, V, V), // 0x64-0x67
+    OC4(Q, Q, B, U), // 0x68-0x6b
+    OC4(U, U, U, U), // 0x6c-0x6f
+
+    OC4(B, B, B, B), // 0x70-0x73
+    OC4(B, B, B, B), // 0x74-0x77
+    OC4(B, B, B, B), // 0x78-0x7b
+    OC4(B, B, B, B), // 0x7c-0x7f
+    OC4(B, B, B, B), // 0x80-0x83
+    OC4(B, B, B, B), // 0x84-0x87
+    OC4(B, B, B, B), // 0x88-0x8b
+    OC4(B, B, B, B), // 0x8c-0x8f
+    OC4(B, B, B, B), // 0x90-0x93
+    OC4(B, B, B, B), // 0x94-0x97
+    OC4(B, B, B, B), // 0x98-0x9b
+    OC4(B, B, B, B), // 0x9c-0x9f
+    OC4(B, B, B, B), // 0xa0-0xa3
+    OC4(B, B, B, B), // 0xa4-0xa7
+    OC4(B, B, B, B), // 0xa8-0xab
+    OC4(B, B, B, B), // 0xac-0xaf
+
+    OC4(B, B, B, B), // 0xb0-0xb3
+    OC4(B, B, B, B), // 0xb4-0xb7
+    OC4(B, B, B, B), // 0xb8-0xbb
+    OC4(B, B, B, B), // 0xbc-0xbf
+
+    OC4(B, B, B, B), // 0xc0-0xc3
+    OC4(B, B, B, B), // 0xc4-0xc7
+    OC4(B, B, B, B), // 0xc8-0xcb
+    OC4(B, B, B, B), // 0xcc-0xcf
+
+    OC4(B, B, B, B), // 0xd0-0xd3
+    OC4(U, U, U, B), // 0xd4-0xd7
+    OC4(B, B, B, B), // 0xd8-0xdb
+    OC4(B, B, B, B), // 0xdc-0xdf
+
+    OC4(B, B, B, B), // 0xe0-0xe3
+    OC4(B, B, B, B), // 0xe4-0xe7
+    OC4(B, B, B, B), // 0xe8-0xeb
+    OC4(B, B, B, B), // 0xec-0xef
+
+    OC4(B, B, B, B), // 0xf0-0xf3
+    OC4(B, B, B, B), // 0xf4-0xf7
+    OC4(U, U, U, U), // 0xf8-0xfb
+    OC4(U, U, U, U), // 0xfc-0xff
+};
+#undef OC4
+#undef U
+#undef B
+#undef Q
+#undef V
+#undef O
+
+uint mp_opcode_format(const byte *ip, size_t *opcode_size) {
+    uint f = (opcode_format_table[*ip >> 2] >> (2 * (*ip & 3))) & 3;
+    const byte *ip_start = ip;
+    if (f == MP_OPCODE_QSTR) {
+        ip += 3;
+    } else {
+        int extra_byte = (
+            *ip == MP_BC_RAISE_VARARGS
+            || *ip == MP_BC_MAKE_CLOSURE
+            || *ip == MP_BC_MAKE_CLOSURE_DEFARGS
+            #if MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE
+            || *ip == MP_BC_LOAD_NAME
+            || *ip == MP_BC_LOAD_GLOBAL
+            || *ip == MP_BC_LOAD_ATTR
+            || *ip == MP_BC_STORE_ATTR
+            #endif
+        );
+        ip += 1;
+        if (f == MP_OPCODE_VAR_UINT) {
+            while ((*ip++ & 0x80) != 0) {
+            }
+        } else if (f == MP_OPCODE_OFFSET) {
+            ip += 2;
+        }
+        ip += extra_byte;
+    }
+    *opcode_size = ip - ip_start;
+    return f;
+}
+
+#endif // MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE

+ 121 - 0
py/bc.h

@@ -0,0 +1,121 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_PY_BC_H
+#define MICROPY_INCLUDED_PY_BC_H
+
+#include "py/runtime.h"
+#include "py/objfun.h"
+
+// bytecode layout:
+//
+//  n_state         : var uint
+//  n_exc_stack     : var uint
+//  scope_flags     : byte
+//  n_pos_args      : byte          number of arguments this function takes
+//  n_kwonly_args   : byte          number of keyword-only arguments this function takes
+//  n_def_pos_args  : byte          number of default positional arguments
+//
+//  code_info_size  : var uint |    code_info_size counts bytes in this chunk
+//  simple_name     : var qstr |
+//  source_file     : var qstr |
+//  <line number info>         |
+//  <word alignment padding>   |    only needed if bytecode contains pointers
+//
+//  local_num0      : byte     |
+//  ...             : byte     |
+//  local_numN      : byte     |    N = num_cells
+//  255             : byte     |    end of list sentinel
+//  <bytecode>                 |
+//
+//
+// constant table layout:
+//
+//  argname0        : obj (qstr)
+//  ...             : obj (qstr)
+//  argnameN        : obj (qstr)    N = num_pos_args + num_kwonly_args
+//  const0          : obj
+//  constN          : obj
+
+// Exception stack entry
+typedef struct _mp_exc_stack_t {
+    const byte *handler;
+    // bit 0 is saved currently_in_except_block value
+    // bit 1 is whether the opcode was SETUP_WITH or SETUP_FINALLY
+    mp_obj_t *val_sp;
+    // Saved exception, valid if currently_in_except_block bit is 1
+    mp_obj_base_t *prev_exc;
+} mp_exc_stack_t;
+
+typedef struct _mp_code_state_t {
+    // The fun_bc entry points to the underlying function object that is being executed.
+    // It is needed to access the start of bytecode and the const_table.
+    // It is also needed to prevent the GC from reclaiming the bytecode during execution,
+    // because the ip pointer below will always point to the interior of the bytecode.
+    mp_obj_fun_bc_t *fun_bc;
+    const byte *ip;
+    mp_obj_t *sp;
+    // bit 0 is saved currently_in_except_block value
+    mp_exc_stack_t *exc_sp;
+    mp_obj_dict_t *old_globals;
+    #if MICROPY_STACKLESS
+    struct _mp_code_state_t *prev;
+    #endif
+    // Variable-length
+    mp_obj_t state[0];
+    // Variable-length, never accessed by name, only as (void*)(state + n_state)
+    //mp_exc_stack_t exc_state[0];
+} mp_code_state_t;
+
+mp_uint_t mp_decode_uint(const byte **ptr);
+mp_uint_t mp_decode_uint_value(const byte *ptr);
+const byte *mp_decode_uint_skip(const byte *ptr);
+
+mp_vm_return_kind_t mp_execute_bytecode(mp_code_state_t *code_state, volatile mp_obj_t inject_exc);
+mp_code_state_t *mp_obj_fun_bc_prepare_codestate(mp_obj_t func, size_t n_args, size_t n_kw, const mp_obj_t *args);
+void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args);
+void mp_bytecode_print(const void *descr, const byte *code, mp_uint_t len, const mp_uint_t *const_table);
+void mp_bytecode_print2(const byte *code, size_t len, const mp_uint_t *const_table);
+const byte *mp_bytecode_print_str(const byte *ip);
+#define mp_bytecode_print_inst(code, const_table) mp_bytecode_print2(code, 1, const_table)
+
+// Helper macros to access pointer with least significant bits holding flags
+#define MP_TAGPTR_PTR(x) ((void*)((uintptr_t)(x) & ~((uintptr_t)3)))
+#define MP_TAGPTR_TAG0(x) ((uintptr_t)(x) & 1)
+#define MP_TAGPTR_TAG1(x) ((uintptr_t)(x) & 2)
+#define MP_TAGPTR_MAKE(ptr, tag) ((void*)((uintptr_t)(ptr) | (tag)))
+
+#if MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE
+
+#define MP_OPCODE_BYTE (0)
+#define MP_OPCODE_QSTR (1)
+#define MP_OPCODE_VAR_UINT (2)
+#define MP_OPCODE_OFFSET (3)
+
+uint mp_opcode_format(const byte *ip, size_t *opcode_size);
+
+#endif
+
+#endif // MICROPY_INCLUDED_PY_BC_H

+ 119 - 0
py/bc0.h

@@ -0,0 +1,119 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_PY_BC0_H
+#define MICROPY_INCLUDED_PY_BC0_H
+
+// MicroPython byte-codes.
+// The comment at the end of the line (if it exists) tells the arguments to the byte-code.
+
+#define MP_BC_LOAD_CONST_FALSE   (0x10)
+#define MP_BC_LOAD_CONST_NONE    (0x11)
+#define MP_BC_LOAD_CONST_TRUE    (0x12)
+#define MP_BC_LOAD_CONST_SMALL_INT   (0x14) // signed var-int
+#define MP_BC_LOAD_CONST_STRING  (0x16) // qstr
+#define MP_BC_LOAD_CONST_OBJ     (0x17) // ptr
+#define MP_BC_LOAD_NULL          (0x18)
+
+#define MP_BC_LOAD_FAST_N        (0x19) // uint
+#define MP_BC_LOAD_DEREF         (0x1a) // uint
+#define MP_BC_LOAD_NAME          (0x1b) // qstr
+#define MP_BC_LOAD_GLOBAL        (0x1c) // qstr
+#define MP_BC_LOAD_ATTR          (0x1d) // qstr
+#define MP_BC_LOAD_METHOD        (0x1e) // qstr
+#define MP_BC_LOAD_SUPER_METHOD  (0x1f) // qstr
+#define MP_BC_LOAD_BUILD_CLASS   (0x20)
+#define MP_BC_LOAD_SUBSCR        (0x21)
+
+#define MP_BC_STORE_FAST_N       (0x22) // uint
+#define MP_BC_STORE_DEREF        (0x23) // uint
+#define MP_BC_STORE_NAME         (0x24) // qstr
+#define MP_BC_STORE_GLOBAL       (0x25) // qstr
+#define MP_BC_STORE_ATTR         (0x26) // qstr
+#define MP_BC_STORE_SUBSCR       (0x27)
+
+#define MP_BC_DELETE_FAST        (0x28) // uint
+#define MP_BC_DELETE_DEREF       (0x29) // uint
+#define MP_BC_DELETE_NAME        (0x2a) // qstr
+#define MP_BC_DELETE_GLOBAL      (0x2b) // qstr
+
+#define MP_BC_DUP_TOP            (0x30)
+#define MP_BC_DUP_TOP_TWO        (0x31)
+#define MP_BC_POP_TOP            (0x32)
+#define MP_BC_ROT_TWO            (0x33)
+#define MP_BC_ROT_THREE          (0x34)
+
+#define MP_BC_JUMP               (0x35) // rel byte code offset, 16-bit signed, in excess
+#define MP_BC_POP_JUMP_IF_TRUE   (0x36) // rel byte code offset, 16-bit signed, in excess
+#define MP_BC_POP_JUMP_IF_FALSE  (0x37) // rel byte code offset, 16-bit signed, in excess
+#define MP_BC_JUMP_IF_TRUE_OR_POP    (0x38) // rel byte code offset, 16-bit signed, in excess
+#define MP_BC_JUMP_IF_FALSE_OR_POP   (0x39) // rel byte code offset, 16-bit signed, in excess
+#define MP_BC_SETUP_WITH         (0x3d) // rel byte code offset, 16-bit unsigned
+#define MP_BC_WITH_CLEANUP       (0x3e)
+#define MP_BC_SETUP_EXCEPT       (0x3f) // rel byte code offset, 16-bit unsigned
+#define MP_BC_SETUP_FINALLY      (0x40) // rel byte code offset, 16-bit unsigned
+#define MP_BC_END_FINALLY        (0x41)
+#define MP_BC_GET_ITER           (0x42)
+#define MP_BC_FOR_ITER           (0x43) // rel byte code offset, 16-bit unsigned
+#define MP_BC_POP_BLOCK          (0x44)
+#define MP_BC_POP_EXCEPT         (0x45)
+#define MP_BC_UNWIND_JUMP        (0x46) // rel byte code offset, 16-bit signed, in excess; then a byte
+#define MP_BC_GET_ITER_STACK     (0x47)
+
+#define MP_BC_BUILD_TUPLE        (0x50) // uint
+#define MP_BC_BUILD_LIST         (0x51) // uint
+#define MP_BC_BUILD_MAP          (0x53) // uint
+#define MP_BC_STORE_MAP          (0x54)
+#define MP_BC_BUILD_SET          (0x56) // uint
+#define MP_BC_BUILD_SLICE        (0x58) // uint
+#define MP_BC_STORE_COMP         (0x57) // uint
+#define MP_BC_UNPACK_SEQUENCE    (0x59) // uint
+#define MP_BC_UNPACK_EX          (0x5a) // uint
+
+#define MP_BC_RETURN_VALUE       (0x5b)
+#define MP_BC_RAISE_VARARGS      (0x5c) // byte
+#define MP_BC_YIELD_VALUE        (0x5d)
+#define MP_BC_YIELD_FROM         (0x5e)
+
+#define MP_BC_MAKE_FUNCTION         (0x60) // uint
+#define MP_BC_MAKE_FUNCTION_DEFARGS (0x61) // uint
+#define MP_BC_MAKE_CLOSURE          (0x62) // uint
+#define MP_BC_MAKE_CLOSURE_DEFARGS  (0x63) // uint
+#define MP_BC_CALL_FUNCTION         (0x64) // uint
+#define MP_BC_CALL_FUNCTION_VAR_KW  (0x65) // uint
+#define MP_BC_CALL_METHOD           (0x66) // uint
+#define MP_BC_CALL_METHOD_VAR_KW    (0x67) // uint
+
+#define MP_BC_IMPORT_NAME        (0x68) // qstr
+#define MP_BC_IMPORT_FROM        (0x69) // qstr
+#define MP_BC_IMPORT_STAR        (0x6a)
+
+#define MP_BC_LOAD_CONST_SMALL_INT_MULTI (0x70) // + N(64)
+#define MP_BC_LOAD_FAST_MULTI            (0xb0) // + N(16)
+#define MP_BC_STORE_FAST_MULTI           (0xc0) // + N(16)
+#define MP_BC_UNARY_OP_MULTI             (0xd0) // + op(<MP_UNARY_OP_NUM_BYTECODE)
+#define MP_BC_BINARY_OP_MULTI            (0xd7) // + op(<MP_BINARY_OP_NUM_BYTECODE)
+
+#endif // MICROPY_INCLUDED_PY_BC0_H

+ 392 - 0
py/binary.c

@@ -0,0 +1,392 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <assert.h>
+
+#include "py/binary.h"
+#include "py/smallint.h"
+#include "py/objint.h"
+#include "py/runtime.h"
+
+// Helpers to work with binary-encoded data
+
+#ifndef alignof
+#define alignof(type) offsetof(struct { char c; type t; }, t)
+#endif
+
+size_t mp_binary_get_size(char struct_type, char val_type, mp_uint_t *palign) {
+    size_t size = 0;
+    int align = 1;
+    switch (struct_type) {
+        case '<': case '>':
+            switch (val_type) {
+                case 'b': case 'B':
+                    size = 1; break;
+                case 'h': case 'H':
+                    size = 2; break;
+                case 'i': case 'I':
+                    size = 4; break;
+                case 'l': case 'L':
+                    size = 4; break;
+                case 'q': case 'Q':
+                    size = 8; break;
+                case 'P': case 'O': case 'S':
+                    size = sizeof(void*); break;
+                case 'f':
+                    size = sizeof(float); break;
+                case 'd':
+                    size = sizeof(double); break;
+            }
+            break;
+        case '@': {
+            // TODO:
+            // The simplest heuristic for alignment is to align by value
+            // size, but that doesn't work for "bigger than int" types,
+            // for example, long long may very well have long alignment
+            // So, we introduce separate alignment handling, but having
+            // formal support for that is different from actually supporting
+            // particular (or any) ABI.
+            switch (val_type) {
+                case BYTEARRAY_TYPECODE:
+                case 'b': case 'B':
+                    align = size = 1; break;
+                case 'h': case 'H':
+                    align = alignof(short);
+                    size = sizeof(short); break;
+                case 'i': case 'I':
+                    align = alignof(int);
+                    size = sizeof(int); break;
+                case 'l': case 'L':
+                    align = alignof(long);
+                    size = sizeof(long); break;
+                case 'q': case 'Q':
+                    align = alignof(long long);
+                    size = sizeof(long long); break;
+                case 'P': case 'O': case 'S':
+                    align = alignof(void*);
+                    size = sizeof(void*); break;
+                case 'f':
+                    align = alignof(float);
+                    size = sizeof(float); break;
+                case 'd':
+                    align = alignof(double);
+                    size = sizeof(double); break;
+            }
+        }
+    }
+
+    if (size == 0) {
+        mp_raise_ValueError("bad typecode");
+    }
+
+    if (palign != NULL) {
+        *palign = align;
+    }
+    return size;
+}
+
+mp_obj_t mp_binary_get_val_array(char typecode, void *p, mp_uint_t index) {
+    mp_int_t val = 0;
+    switch (typecode) {
+        case 'b':
+            val = ((signed char*)p)[index];
+            break;
+        case BYTEARRAY_TYPECODE:
+        case 'B':
+            val = ((unsigned char*)p)[index];
+            break;
+        case 'h':
+            val = ((short*)p)[index];
+            break;
+        case 'H':
+            val = ((unsigned short*)p)[index];
+            break;
+        case 'i':
+            return mp_obj_new_int(((int*)p)[index]);
+        case 'I':
+            return mp_obj_new_int_from_uint(((unsigned int*)p)[index]);
+        case 'l':
+            return mp_obj_new_int(((long*)p)[index]);
+        case 'L':
+            return mp_obj_new_int_from_uint(((unsigned long*)p)[index]);
+        #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE
+        case 'q':
+            return mp_obj_new_int_from_ll(((long long*)p)[index]);
+        case 'Q':
+            return mp_obj_new_int_from_ull(((unsigned long long*)p)[index]);
+        #endif
+#if MICROPY_PY_BUILTINS_FLOAT
+        case 'f':
+            return mp_obj_new_float(((float*)p)[index]);
+        case 'd':
+            return mp_obj_new_float(((double*)p)[index]);
+#endif
+        // Extension to CPython: array of objects
+        case 'O':
+            return ((mp_obj_t*)p)[index];
+        // Extension to CPython: array of pointers
+        case 'P':
+            return mp_obj_new_int((mp_int_t)(uintptr_t)((void**)p)[index]);
+    }
+    return MP_OBJ_NEW_SMALL_INT(val);
+}
+
+// The long long type is guaranteed to hold at least 64 bits, and size is at
+// most 8 (for q and Q), so we will always be able to parse the given data
+// and fit it into a long long.
+long long mp_binary_get_int(mp_uint_t size, bool is_signed, bool big_endian, const byte *src) {
+    int delta;
+    if (!big_endian) {
+        delta = -1;
+        src += size - 1;
+    } else {
+        delta = 1;
+    }
+
+    long long val = 0;
+    if (is_signed && *src & 0x80) {
+        val = -1;
+    }
+    for (uint i = 0; i < size; i++) {
+        val <<= 8;
+        val |= *src;
+        src += delta;
+    }
+
+    return val;
+}
+
+#define is_signed(typecode) (typecode > 'Z')
+mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte **ptr) {
+    byte *p = *ptr;
+    mp_uint_t align;
+
+    size_t size = mp_binary_get_size(struct_type, val_type, &align);
+    if (struct_type == '@') {
+        // Make pointer aligned
+        p = (byte*)MP_ALIGN(p, (size_t)align);
+        #if MP_ENDIANNESS_LITTLE
+        struct_type = '<';
+        #else
+        struct_type = '>';
+        #endif
+    }
+    *ptr = p + size;
+
+    long long val = mp_binary_get_int(size, is_signed(val_type), (struct_type == '>'), p);
+
+    if (val_type == 'O') {
+        return (mp_obj_t)(mp_uint_t)val;
+    } else if (val_type == 'S') {
+        const char *s_val = (const char*)(uintptr_t)(mp_uint_t)val;
+        return mp_obj_new_str(s_val, strlen(s_val), false);
+#if MICROPY_PY_BUILTINS_FLOAT
+    } else if (val_type == 'f') {
+        union { uint32_t i; float f; } fpu = {val};
+        return mp_obj_new_float(fpu.f);
+    } else if (val_type == 'd') {
+        union { uint64_t i; double f; } fpu = {val};
+        return mp_obj_new_float(fpu.f);
+#endif
+    } else if (is_signed(val_type)) {
+        if ((long long)MP_SMALL_INT_MIN <= val && val <= (long long)MP_SMALL_INT_MAX) {
+            return mp_obj_new_int((mp_int_t)val);
+        } else {
+            return mp_obj_new_int_from_ll(val);
+        }
+    } else {
+        if ((unsigned long long)val <= (unsigned long long)MP_SMALL_INT_MAX) {
+            return mp_obj_new_int_from_uint((mp_uint_t)val);
+        } else {
+            return mp_obj_new_int_from_ull(val);
+        }
+    }
+}
+
+void mp_binary_set_int(mp_uint_t val_sz, bool big_endian, byte *dest, mp_uint_t val) {
+    if (MP_ENDIANNESS_LITTLE && !big_endian) {
+        memcpy(dest, &val, val_sz);
+    } else if (MP_ENDIANNESS_BIG && big_endian) {
+        // only copy the least-significant val_sz bytes
+        memcpy(dest, (byte*)&val + sizeof(mp_uint_t) - val_sz, val_sz);
+    } else {
+        const byte *src;
+        if (MP_ENDIANNESS_LITTLE) {
+            src = (const byte*)&val + val_sz;
+        } else {
+            src = (const byte*)&val + sizeof(mp_uint_t);
+        }
+        while (val_sz--) {
+            *dest++ = *--src;
+        }
+    }
+}
+
+void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte **ptr) {
+    byte *p = *ptr;
+    mp_uint_t align;
+
+    size_t size = mp_binary_get_size(struct_type, val_type, &align);
+    if (struct_type == '@') {
+        // Make pointer aligned
+        p = (byte*)MP_ALIGN(p, (size_t)align);
+        if (MP_ENDIANNESS_LITTLE) {
+            struct_type = '<';
+        } else {
+            struct_type = '>';
+        }
+    }
+    *ptr = p + size;
+
+    mp_uint_t val;
+    switch (val_type) {
+        case 'O':
+            val = (mp_uint_t)val_in;
+            break;
+#if MICROPY_PY_BUILTINS_FLOAT
+        case 'f': {
+            union { uint32_t i; float f; } fp_sp;
+            fp_sp.f = mp_obj_get_float(val_in);
+            val = fp_sp.i;
+            break;
+        }
+        case 'd': {
+            union { uint64_t i64; uint32_t i32[2]; double f; } fp_dp;
+            fp_dp.f = mp_obj_get_float(val_in);
+            if (BYTES_PER_WORD == 8) {
+                val = fp_dp.i64;
+            } else {
+                int be = struct_type == '>';
+                mp_binary_set_int(sizeof(uint32_t), be, p, fp_dp.i32[MP_ENDIANNESS_BIG ^ be]);
+                p += sizeof(uint32_t);
+                val = fp_dp.i32[MP_ENDIANNESS_LITTLE ^ be];
+            }
+            break;
+        }
+#endif
+        default:
+            #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE
+            if (MP_OBJ_IS_TYPE(val_in, &mp_type_int)) {
+                mp_obj_int_to_bytes_impl(val_in, struct_type == '>', size, p);
+                return;
+            } else
+            #endif
+            {
+                val = mp_obj_get_int(val_in);
+                // zero/sign extend if needed
+                if (BYTES_PER_WORD < 8 && size > sizeof(val)) {
+                    int c = (is_signed(val_type) && (mp_int_t)val < 0) ? 0xff : 0x00;
+                    memset(p, c, size);
+                    if (struct_type == '>') {
+                        p += size - sizeof(val);
+                    }
+                }
+            }
+    }
+
+    mp_binary_set_int(MIN((size_t)size, sizeof(val)), struct_type == '>', p, val);
+}
+
+void mp_binary_set_val_array(char typecode, void *p, mp_uint_t index, mp_obj_t val_in) {
+    switch (typecode) {
+#if MICROPY_PY_BUILTINS_FLOAT
+        case 'f':
+            ((float*)p)[index] = mp_obj_get_float(val_in);
+            break;
+        case 'd':
+            ((double*)p)[index] = mp_obj_get_float(val_in);
+            break;
+#endif
+        // Extension to CPython: array of objects
+        case 'O':
+            ((mp_obj_t*)p)[index] = val_in;
+            break;
+        default:
+            #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE
+            if (MP_OBJ_IS_TYPE(val_in, &mp_type_int)) {
+                size_t size = mp_binary_get_size('@', typecode, NULL);
+                mp_obj_int_to_bytes_impl(val_in, MP_ENDIANNESS_BIG,
+                    size, (uint8_t*)p + index * size);
+                return;
+            }
+            #endif
+            mp_binary_set_val_array_from_int(typecode, p, index, mp_obj_get_int(val_in));
+    }
+}
+
+void mp_binary_set_val_array_from_int(char typecode, void *p, mp_uint_t index, mp_int_t val) {
+    switch (typecode) {
+        case 'b':
+            ((signed char*)p)[index] = val;
+            break;
+        case BYTEARRAY_TYPECODE:
+        case 'B':
+            ((unsigned char*)p)[index] = val;
+            break;
+        case 'h':
+            ((short*)p)[index] = val;
+            break;
+        case 'H':
+            ((unsigned short*)p)[index] = val;
+            break;
+        case 'i':
+            ((int*)p)[index] = val;
+            break;
+        case 'I':
+            ((unsigned int*)p)[index] = val;
+            break;
+        case 'l':
+            ((long*)p)[index] = val;
+            break;
+        case 'L':
+            ((unsigned long*)p)[index] = val;
+            break;
+        #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE
+        case 'q':
+            ((long long*)p)[index] = val;
+            break;
+        case 'Q':
+            ((unsigned long long*)p)[index] = val;
+            break;
+        #endif
+#if MICROPY_PY_BUILTINS_FLOAT
+        case 'f':
+            ((float*)p)[index] = val;
+            break;
+        case 'd':
+            ((double*)p)[index] = val;
+            break;
+#endif
+        // Extension to CPython: array of pointers
+        case 'P':
+            ((void**)p)[index] = (void*)(uintptr_t)val;
+            break;
+    }
+}

+ 45 - 0
py/binary.h

@@ -0,0 +1,45 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_PY_BINARY_H
+#define MICROPY_INCLUDED_PY_BINARY_H
+
+#include "py/obj.h"
+
+// Use special typecode to differentiate repr() of bytearray vs array.array('B')
+// (underlyingly they're same).  Can't use 0 here because that's used to detect
+// type-specification errors due to end-of-string.
+#define BYTEARRAY_TYPECODE 1
+
+size_t mp_binary_get_size(char struct_type, char val_type, mp_uint_t *palign);
+mp_obj_t mp_binary_get_val_array(char typecode, void *p, mp_uint_t index);
+void mp_binary_set_val_array(char typecode, void *p, mp_uint_t index, mp_obj_t val_in);
+void mp_binary_set_val_array_from_int(char typecode, void *p, mp_uint_t index, mp_int_t val);
+mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte **ptr);
+void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte **ptr);
+long long mp_binary_get_int(mp_uint_t size, bool is_signed, bool big_endian, const byte *src);
+void mp_binary_set_int(mp_uint_t val_sz, bool big_endian, byte *dest, mp_uint_t val);
+
+#endif // MICROPY_INCLUDED_PY_BINARY_H

+ 123 - 0
py/builtin.h

@@ -0,0 +1,123 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_PY_BUILTIN_H
+#define MICROPY_INCLUDED_PY_BUILTIN_H
+
+#include "py/obj.h"
+
+mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args);
+mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs);
+mp_obj_t mp_micropython_mem_info(size_t n_args, const mp_obj_t *args);
+
+MP_DECLARE_CONST_FUN_OBJ_VAR(mp_builtin___build_class___obj);
+MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin___import___obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin___repl_print___obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_abs_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_all_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_any_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_bin_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_callable_obj);
+MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_compile_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_chr_obj);
+MP_DECLARE_CONST_FUN_OBJ_2(mp_builtin_delattr_obj);
+MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_dir_obj);
+MP_DECLARE_CONST_FUN_OBJ_2(mp_builtin_divmod_obj);
+MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_eval_obj);
+MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_exec_obj);
+MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_execfile_obj);
+MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_getattr_obj);
+MP_DECLARE_CONST_FUN_OBJ_3(mp_builtin_setattr_obj);
+MP_DECLARE_CONST_FUN_OBJ_0(mp_builtin_globals_obj);
+MP_DECLARE_CONST_FUN_OBJ_2(mp_builtin_hasattr_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_hash_obj);
+MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_help_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_hex_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_id_obj);
+MP_DECLARE_CONST_FUN_OBJ_2(mp_builtin_isinstance_obj);
+MP_DECLARE_CONST_FUN_OBJ_2(mp_builtin_issubclass_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_iter_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_len_obj);
+MP_DECLARE_CONST_FUN_OBJ_0(mp_builtin_locals_obj);
+MP_DECLARE_CONST_FUN_OBJ_KW(mp_builtin_max_obj);
+MP_DECLARE_CONST_FUN_OBJ_KW(mp_builtin_min_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_next_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_oct_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_ord_obj);
+MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_pow_obj);
+MP_DECLARE_CONST_FUN_OBJ_KW(mp_builtin_print_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_repr_obj);
+MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_round_obj);
+MP_DECLARE_CONST_FUN_OBJ_KW(mp_builtin_sorted_obj);
+MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_sum_obj);
+// Defined by a port, but declared here for simplicity
+MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_input_obj);
+MP_DECLARE_CONST_FUN_OBJ_KW(mp_builtin_open_obj);
+
+MP_DECLARE_CONST_FUN_OBJ_2(mp_namedtuple_obj);
+
+MP_DECLARE_CONST_FUN_OBJ_2(mp_op_contains_obj);
+MP_DECLARE_CONST_FUN_OBJ_2(mp_op_getitem_obj);
+MP_DECLARE_CONST_FUN_OBJ_3(mp_op_setitem_obj);
+MP_DECLARE_CONST_FUN_OBJ_2(mp_op_delitem_obj);
+
+extern const mp_obj_module_t mp_module___main__;
+extern const mp_obj_module_t mp_module_builtins;
+extern const mp_obj_module_t mp_module_array;
+extern const mp_obj_module_t mp_module_collections;
+extern const mp_obj_module_t mp_module_io;
+extern const mp_obj_module_t mp_module_math;
+extern const mp_obj_module_t mp_module_cmath;
+extern const mp_obj_module_t mp_module_micropython;
+extern const mp_obj_module_t mp_module_ustruct;
+extern const mp_obj_module_t mp_module_sys;
+extern const mp_obj_module_t mp_module_gc;
+extern const mp_obj_module_t mp_module_thread;
+
+extern const mp_obj_dict_t mp_module_builtins_globals;
+
+// extmod modules
+extern const mp_obj_module_t mp_module_uerrno;
+extern const mp_obj_module_t mp_module_uctypes;
+extern const mp_obj_module_t mp_module_uzlib;
+extern const mp_obj_module_t mp_module_ujson;
+extern const mp_obj_module_t mp_module_ure;
+extern const mp_obj_module_t mp_module_uheapq;
+extern const mp_obj_module_t mp_module_uhashlib;
+extern const mp_obj_module_t mp_module_ubinascii;
+extern const mp_obj_module_t mp_module_urandom;
+extern const mp_obj_module_t mp_module_uselect;
+extern const mp_obj_module_t mp_module_ussl;
+extern const mp_obj_module_t mp_module_utimeq;
+extern const mp_obj_module_t mp_module_machine;
+extern const mp_obj_module_t mp_module_lwip;
+extern const mp_obj_module_t mp_module_websocket;
+extern const mp_obj_module_t mp_module_webrepl;
+extern const mp_obj_module_t mp_module_framebuf;
+extern const mp_obj_module_t mp_module_btree;
+
+extern const char MICROPY_PY_BUILTINS_HELP_TEXT[];
+
+#endif // MICROPY_INCLUDED_PY_BUILTIN_H

+ 167 - 0
py/builtinevex.c

@@ -0,0 +1,167 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdint.h>
+
+#include "py/objfun.h"
+#include "py/compile.h"
+#include "py/runtime.h"
+#include "py/builtin.h"
+
+#if MICROPY_PY_BUILTINS_COMPILE
+
+typedef struct _mp_obj_code_t {
+    mp_obj_base_t base;
+    mp_obj_t module_fun;
+} mp_obj_code_t;
+
+STATIC const mp_obj_type_t mp_type_code = {
+    { &mp_type_type },
+    .name = MP_QSTR_code,
+};
+
+STATIC mp_obj_t code_execute(mp_obj_code_t *self, mp_obj_dict_t *globals, mp_obj_dict_t *locals) {
+    // save context and set new context
+    mp_obj_dict_t *old_globals = mp_globals_get();
+    mp_obj_dict_t *old_locals = mp_locals_get();
+    mp_globals_set(globals);
+    mp_locals_set(locals);
+
+    // a bit of a hack: fun_bc will re-set globals, so need to make sure it's
+    // the correct one
+    if (MP_OBJ_IS_TYPE(self->module_fun, &mp_type_fun_bc)) {
+        mp_obj_fun_bc_t *fun_bc = MP_OBJ_TO_PTR(self->module_fun);
+        fun_bc->globals = globals;
+    }
+
+    // execute code
+    nlr_buf_t nlr;
+    if (nlr_push(&nlr) == 0) {
+        mp_obj_t ret = mp_call_function_0(self->module_fun);
+        nlr_pop();
+        mp_globals_set(old_globals);
+        mp_locals_set(old_locals);
+        return ret;
+    } else {
+        // exception; restore context and re-raise same exception
+        mp_globals_set(old_globals);
+        mp_locals_set(old_locals);
+        nlr_jump(nlr.ret_val);
+    }
+}
+
+STATIC mp_obj_t mp_builtin_compile(size_t n_args, const mp_obj_t *args) {
+    (void)n_args;
+
+    // get the source
+    size_t str_len;
+    const char *str = mp_obj_str_get_data(args[0], &str_len);
+
+    // get the filename
+    qstr filename = mp_obj_str_get_qstr(args[1]);
+
+    // create the lexer
+    mp_lexer_t *lex = mp_lexer_new_from_str_len(filename, str, str_len, 0);
+
+    // get the compile mode
+    qstr mode = mp_obj_str_get_qstr(args[2]);
+    mp_parse_input_kind_t parse_input_kind;
+    switch (mode) {
+        case MP_QSTR_single: parse_input_kind = MP_PARSE_SINGLE_INPUT; break;
+        case MP_QSTR_exec: parse_input_kind = MP_PARSE_FILE_INPUT; break;
+        case MP_QSTR_eval: parse_input_kind = MP_PARSE_EVAL_INPUT; break;
+        default:
+            mp_raise_ValueError("bad compile mode");
+    }
+
+    mp_obj_code_t *code = m_new_obj(mp_obj_code_t);
+    code->base.type = &mp_type_code;
+    code->module_fun = mp_parse_compile_execute(lex, parse_input_kind, NULL, NULL);
+    return MP_OBJ_FROM_PTR(code);
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_compile_obj, 3, 6, mp_builtin_compile);
+
+#endif // MICROPY_PY_BUILTINS_COMPILE
+
+#if MICROPY_PY_BUILTINS_EVAL_EXEC
+
+STATIC mp_obj_t eval_exec_helper(size_t n_args, const mp_obj_t *args, mp_parse_input_kind_t parse_input_kind) {
+    // work out the context
+    mp_obj_dict_t *globals = mp_globals_get();
+    mp_obj_dict_t *locals = mp_locals_get();
+    for (size_t i = 1; i < 3 && i < n_args; ++i) {
+        if (args[i] != mp_const_none) {
+            if (!MP_OBJ_IS_TYPE(args[i], &mp_type_dict)) {
+                mp_raise_TypeError(NULL);
+            }
+            locals = MP_OBJ_TO_PTR(args[i]);
+            if (i == 1) {
+                globals = locals;
+            }
+        }
+    }
+
+    #if MICROPY_PY_BUILTINS_COMPILE
+    if (MP_OBJ_IS_TYPE(args[0], &mp_type_code)) {
+        return code_execute(MP_OBJ_TO_PTR(args[0]), globals, locals);
+    }
+    #endif
+
+    size_t str_len;
+    const char *str = mp_obj_str_get_data(args[0], &str_len);
+
+    // create the lexer
+    // MP_PARSE_SINGLE_INPUT is used to indicate a file input
+    mp_lexer_t *lex;
+    if (MICROPY_PY_BUILTINS_EXECFILE && parse_input_kind == MP_PARSE_SINGLE_INPUT) {
+        lex = mp_lexer_new_from_file(str);
+        parse_input_kind = MP_PARSE_FILE_INPUT;
+    } else {
+        lex = mp_lexer_new_from_str_len(MP_QSTR__lt_string_gt_, str, str_len, 0);
+    }
+
+    return mp_parse_compile_execute(lex, parse_input_kind, globals, locals);
+}
+
+STATIC mp_obj_t mp_builtin_eval(size_t n_args, const mp_obj_t *args) {
+    return eval_exec_helper(n_args, args, MP_PARSE_EVAL_INPUT);
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_eval_obj, 1, 3, mp_builtin_eval);
+
+STATIC mp_obj_t mp_builtin_exec(size_t n_args, const mp_obj_t *args) {
+    return eval_exec_helper(n_args, args, MP_PARSE_FILE_INPUT);
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_exec_obj, 1, 3, mp_builtin_exec);
+
+#endif // MICROPY_PY_BUILTINS_EVAL_EXEC
+
+#if MICROPY_PY_BUILTINS_EXECFILE
+STATIC mp_obj_t mp_builtin_execfile(size_t n_args, const mp_obj_t *args) {
+    // MP_PARSE_SINGLE_INPUT is used to indicate a file input
+    return eval_exec_helper(n_args, args, MP_PARSE_SINGLE_INPUT);
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_execfile_obj, 1, 3, mp_builtin_execfile);
+#endif

+ 179 - 0
py/builtinhelp.c

@@ -0,0 +1,179 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013-2016 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "py/builtin.h"
+#include "py/objmodule.h"
+
+#if MICROPY_PY_BUILTINS_HELP
+
+const char mp_help_default_text[] =
+"Welcome to MicroPython!\n"
+"\n"
+"For online docs please visit http://docs.micropython.org/\n"
+"\n"
+"Control commands:\n"
+"  CTRL-A        -- on a blank line, enter raw REPL mode\n"
+"  CTRL-B        -- on a blank line, enter normal REPL mode\n"
+"  CTRL-C        -- interrupt a running program\n"
+"  CTRL-D        -- on a blank line, exit or do a soft reset\n"
+"  CTRL-E        -- on a blank line, enter paste mode\n"
+"\n"
+"For further help on a specific object, type help(obj)\n"
+;
+
+STATIC void mp_help_print_info_about_object(mp_obj_t name_o, mp_obj_t value) {
+    mp_print_str(MP_PYTHON_PRINTER, "  ");
+    mp_obj_print(name_o, PRINT_STR);
+    mp_print_str(MP_PYTHON_PRINTER, " -- ");
+    mp_obj_print(value, PRINT_STR);
+    mp_print_str(MP_PYTHON_PRINTER, "\n");
+}
+
+#if MICROPY_PY_BUILTINS_HELP_MODULES
+STATIC void mp_help_add_from_map(mp_obj_t list, const mp_map_t *map) {
+    for (size_t i = 0; i < map->alloc; i++) {
+        if (MP_MAP_SLOT_IS_FILLED(map, i)) {
+            mp_obj_list_append(list, map->table[i].key);
+        }
+    }
+}
+
+#if MICROPY_MODULE_FROZEN
+STATIC void mp_help_add_from_names(mp_obj_t list, const char *name) {
+    while (*name) {
+        size_t l = strlen(name);
+        // name should end in '.py' and we strip it off
+        mp_obj_list_append(list, mp_obj_new_str(name, l - 3, false));
+        name += l + 1;
+    }
+}
+#endif
+
+STATIC void mp_help_print_modules(void) {
+    mp_obj_t list = mp_obj_new_list(0, NULL);
+
+    mp_help_add_from_map(list, &mp_builtin_module_map);
+
+    #if MICROPY_MODULE_WEAK_LINKS
+    mp_help_add_from_map(list, &mp_builtin_module_weak_links_map);
+    #endif
+
+    #if MICROPY_MODULE_FROZEN_STR
+    extern const char mp_frozen_str_names[];
+    mp_help_add_from_names(list, mp_frozen_str_names);
+    #endif
+
+    #if MICROPY_MODULE_FROZEN_MPY
+    extern const char mp_frozen_mpy_names[];
+    mp_help_add_from_names(list, mp_frozen_mpy_names);
+    #endif
+
+    // sort the list so it's printed in alphabetical order
+    mp_obj_list_sort(1, &list, (mp_map_t*)&mp_const_empty_map);
+
+    // print the list of modules in a column-first order
+    #define NUM_COLUMNS (4)
+    #define COLUMN_WIDTH (18)
+    mp_uint_t len;
+    mp_obj_t *items;
+    mp_obj_list_get(list, &len, &items);
+    unsigned int num_rows = (len + NUM_COLUMNS - 1) / NUM_COLUMNS;
+    for (unsigned int i = 0; i < num_rows; ++i) {
+        unsigned int j = i;
+        for (;;) {
+            int l = mp_print_str(MP_PYTHON_PRINTER, mp_obj_str_get_str(items[j]));
+            j += num_rows;
+            if (j >= len) {
+                break;
+            }
+            int gap = COLUMN_WIDTH - l;
+            while (gap < 1) {
+                gap += COLUMN_WIDTH;
+            }
+            while (gap--) {
+                mp_print_str(MP_PYTHON_PRINTER, " ");
+            }
+        }
+        mp_print_str(MP_PYTHON_PRINTER, "\n");
+    }
+
+    // let the user know there may be other modules available from the filesystem
+    mp_print_str(MP_PYTHON_PRINTER, "Plus any modules on the filesystem\n");
+}
+#endif
+
+STATIC void mp_help_print_obj(const mp_obj_t obj) {
+    #if MICROPY_PY_BUILTINS_HELP_MODULES
+    if (obj == MP_OBJ_NEW_QSTR(MP_QSTR_modules)) {
+        mp_help_print_modules();
+        return;
+    }
+    #endif
+
+    mp_obj_type_t *type = mp_obj_get_type(obj);
+
+    // try to print something sensible about the given object
+    mp_print_str(MP_PYTHON_PRINTER, "object ");
+    mp_obj_print(obj, PRINT_STR);
+    mp_printf(MP_PYTHON_PRINTER, " is of type %q\n", type->name);
+
+    mp_map_t *map = NULL;
+    if (type == &mp_type_module) {
+        map = mp_obj_dict_get_map(mp_obj_module_get_globals(obj));
+    } else {
+        if (type == &mp_type_type) {
+            type = MP_OBJ_TO_PTR(obj);
+        }
+        if (type->locals_dict != MP_OBJ_NULL && MP_OBJ_IS_TYPE(type->locals_dict, &mp_type_dict)) {
+            map = mp_obj_dict_get_map(type->locals_dict);
+        }
+    }
+    if (map != NULL) {
+        for (uint i = 0; i < map->alloc; i++) {
+            if (map->table[i].key != MP_OBJ_NULL) {
+                mp_help_print_info_about_object(map->table[i].key, map->table[i].value);
+            }
+        }
+    }
+}
+
+STATIC mp_obj_t mp_builtin_help(size_t n_args, const mp_obj_t *args) {
+    if (n_args == 0) {
+        // print a general help message
+        mp_print_str(MP_PYTHON_PRINTER, MICROPY_PY_BUILTINS_HELP_TEXT);
+    } else {
+        // try to print something sensible about the given object
+        mp_help_print_obj(args[0]);
+    }
+
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_help_obj, 0, 1, mp_builtin_help);
+
+#endif // MICROPY_PY_BUILTINS_HELP

+ 474 - 0
py/builtinimport.c

@@ -0,0 +1,474 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ * Copyright (c) 2014 Paul Sokolovsky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "py/compile.h"
+#include "py/objmodule.h"
+#include "py/persistentcode.h"
+#include "py/runtime.h"
+#include "py/builtin.h"
+#include "py/frozenmod.h"
+
+#if MICROPY_DEBUG_VERBOSE // print debugging info
+#define DEBUG_PRINT (1)
+#define DEBUG_printf DEBUG_printf
+#else // don't print debugging info
+#define DEBUG_PRINT (0)
+#define DEBUG_printf(...) (void)0
+#endif
+
+#define PATH_SEP_CHAR '/'
+
+bool mp_obj_is_package(mp_obj_t module) {
+    mp_obj_t dest[2];
+    mp_load_method_maybe(module, MP_QSTR___path__, dest);
+    return dest[0] != MP_OBJ_NULL;
+}
+
+// Stat either frozen or normal module by a given path
+// (whatever is available, if at all).
+STATIC mp_import_stat_t mp_import_stat_any(const char *path) {
+    #if MICROPY_MODULE_FROZEN
+    mp_import_stat_t st = mp_frozen_stat(path);
+    if (st != MP_IMPORT_STAT_NO_EXIST) {
+        return st;
+    }
+    #endif
+    return mp_import_stat(path);
+}
+
+STATIC mp_import_stat_t stat_file_py_or_mpy(vstr_t *path) {
+    mp_import_stat_t stat = mp_import_stat_any(vstr_null_terminated_str(path));
+    if (stat == MP_IMPORT_STAT_FILE) {
+        return stat;
+    }
+
+    #if MICROPY_PERSISTENT_CODE_LOAD
+    vstr_ins_byte(path, path->len - 2, 'm');
+    stat = mp_import_stat_any(vstr_null_terminated_str(path));
+    if (stat == MP_IMPORT_STAT_FILE) {
+        return stat;
+    }
+    #endif
+
+    return MP_IMPORT_STAT_NO_EXIST;
+}
+
+STATIC mp_import_stat_t stat_dir_or_file(vstr_t *path) {
+    mp_import_stat_t stat = mp_import_stat_any(vstr_null_terminated_str(path));
+    DEBUG_printf("stat %s: %d\n", vstr_str(path), stat);
+    if (stat == MP_IMPORT_STAT_DIR) {
+        return stat;
+    }
+
+    // not a directory, add .py and try as a file
+    vstr_add_str(path, ".py");
+    return stat_file_py_or_mpy(path);
+}
+
+STATIC mp_import_stat_t find_file(const char *file_str, uint file_len, vstr_t *dest) {
+#if MICROPY_PY_SYS
+    // extract the list of paths
+    size_t path_num;
+    mp_obj_t *path_items;
+    mp_obj_list_get(mp_sys_path, &path_num, &path_items);
+
+    if (path_num == 0) {
+#endif
+        // mp_sys_path is empty, so just use the given file name
+        vstr_add_strn(dest, file_str, file_len);
+        return stat_dir_or_file(dest);
+#if MICROPY_PY_SYS
+    } else {
+        // go through each path looking for a directory or file
+        for (size_t i = 0; i < path_num; i++) {
+            vstr_reset(dest);
+            size_t p_len;
+            const char *p = mp_obj_str_get_data(path_items[i], &p_len);
+            if (p_len > 0) {
+                vstr_add_strn(dest, p, p_len);
+                vstr_add_char(dest, PATH_SEP_CHAR);
+            }
+            vstr_add_strn(dest, file_str, file_len);
+            mp_import_stat_t stat = stat_dir_or_file(dest);
+            if (stat != MP_IMPORT_STAT_NO_EXIST) {
+                return stat;
+            }
+        }
+
+        // could not find a directory or file
+        return MP_IMPORT_STAT_NO_EXIST;
+    }
+#endif
+}
+
+#if MICROPY_ENABLE_COMPILER
+STATIC void do_load_from_lexer(mp_obj_t module_obj, mp_lexer_t *lex) {
+    #if MICROPY_PY___FILE__
+    qstr source_name = lex->source_name;
+    mp_store_attr(module_obj, MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name));
+    #endif
+
+    // parse, compile and execute the module in its context
+    mp_obj_dict_t *mod_globals = mp_obj_module_get_globals(module_obj);
+    mp_parse_compile_execute(lex, MP_PARSE_FILE_INPUT, mod_globals, mod_globals);
+}
+#endif
+
+#if MICROPY_PERSISTENT_CODE_LOAD || MICROPY_MODULE_FROZEN_MPY
+STATIC void do_execute_raw_code(mp_obj_t module_obj, mp_raw_code_t *raw_code) {
+    #if MICROPY_PY___FILE__
+    // TODO
+    //qstr source_name = lex->source_name;
+    //mp_store_attr(module_obj, MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name));
+    #endif
+
+    // execute the module in its context
+    mp_obj_dict_t *mod_globals = mp_obj_module_get_globals(module_obj);
+
+    // save context
+    mp_obj_dict_t *volatile old_globals = mp_globals_get();
+    mp_obj_dict_t *volatile old_locals = mp_locals_get();
+
+    // set new context
+    mp_globals_set(mod_globals);
+    mp_locals_set(mod_globals);
+
+    nlr_buf_t nlr;
+    if (nlr_push(&nlr) == 0) {
+        mp_obj_t module_fun = mp_make_function_from_raw_code(raw_code, MP_OBJ_NULL, MP_OBJ_NULL);
+        mp_call_function_0(module_fun);
+
+        // finish nlr block, restore context
+        nlr_pop();
+        mp_globals_set(old_globals);
+        mp_locals_set(old_locals);
+    } else {
+        // exception; restore context and re-raise same exception
+        mp_globals_set(old_globals);
+        mp_locals_set(old_locals);
+        nlr_jump(nlr.ret_val);
+    }
+}
+#endif
+
+STATIC void do_load(mp_obj_t module_obj, vstr_t *file) {
+    #if MICROPY_MODULE_FROZEN || MICROPY_PERSISTENT_CODE_LOAD || MICROPY_ENABLE_COMPILER
+    char *file_str = vstr_null_terminated_str(file);
+    #endif
+
+    // If we support frozen modules (either as str or mpy) then try to find the
+    // requested filename in the list of frozen module filenames.
+    #if MICROPY_MODULE_FROZEN
+    void *modref;
+    int frozen_type = mp_find_frozen_module(file_str, file->len, &modref);
+    #endif
+
+    // If we support frozen str modules and the compiler is enabled, and we
+    // found the filename in the list of frozen files, then load and execute it.
+    #if MICROPY_MODULE_FROZEN_STR
+    if (frozen_type == MP_FROZEN_STR) {
+        do_load_from_lexer(module_obj, modref);
+        return;
+    }
+    #endif
+
+    // If we support frozen mpy modules and we found a corresponding file (and
+    // its data) in the list of frozen files, execute it.
+    #if MICROPY_MODULE_FROZEN_MPY
+    if (frozen_type == MP_FROZEN_MPY) {
+        do_execute_raw_code(module_obj, modref);
+        return;
+    }
+    #endif
+
+    // If we support loading .mpy files then check if the file extension is of
+    // the correct format and, if so, load and execute the file.
+    #if MICROPY_PERSISTENT_CODE_LOAD
+    if (file_str[file->len - 3] == 'm') {
+        mp_raw_code_t *raw_code = mp_raw_code_load_file(file_str);
+        do_execute_raw_code(module_obj, raw_code);
+        return;
+    }
+    #endif
+
+    // If we can compile scripts then load the file and compile and execute it.
+    #if MICROPY_ENABLE_COMPILER
+    {
+        mp_lexer_t *lex = mp_lexer_new_from_file(file_str);
+        do_load_from_lexer(module_obj, lex);
+        return;
+    }
+    #else
+
+    // If we get here then the file was not frozen and we can't compile scripts.
+    mp_raise_msg(&mp_type_ImportError, "script compilation not supported");
+    #endif
+}
+
+STATIC void chop_component(const char *start, const char **end) {
+    const char *p = *end;
+    while (p > start) {
+        if (*--p == '.') {
+            *end = p;
+            return;
+        }
+    }
+    *end = p;
+}
+
+mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) {
+#if DEBUG_PRINT
+    DEBUG_printf("__import__:\n");
+    for (size_t i = 0; i < n_args; i++) {
+        DEBUG_printf("  ");
+        mp_obj_print(args[i], PRINT_REPR);
+        DEBUG_printf("\n");
+    }
+#endif
+
+    mp_obj_t module_name = args[0];
+    mp_obj_t fromtuple = mp_const_none;
+    mp_int_t level = 0;
+    if (n_args >= 4) {
+        fromtuple = args[3];
+        if (n_args >= 5) {
+            level = MP_OBJ_SMALL_INT_VALUE(args[4]);
+            if (level < 0) {
+                mp_raise_ValueError(NULL);
+            }
+        }
+    }
+
+    size_t mod_len;
+    const char *mod_str = mp_obj_str_get_data(module_name, &mod_len);
+
+    if (level != 0) {
+        // What we want to do here is to take name of current module,
+        // chop <level> trailing components, and concatenate with passed-in
+        // module name, thus resolving relative import name into absolute.
+        // This even appears to be correct per
+        // http://legacy.python.org/dev/peps/pep-0328/#relative-imports-and-name
+        // "Relative imports use a module's __name__ attribute to determine that
+        // module's position in the package hierarchy."
+        level--;
+        mp_obj_t this_name_q = mp_obj_dict_get(MP_OBJ_FROM_PTR(mp_globals_get()), MP_OBJ_NEW_QSTR(MP_QSTR___name__));
+        assert(this_name_q != MP_OBJ_NULL);
+        #if MICROPY_CPYTHON_COMPAT
+        if (MP_OBJ_QSTR_VALUE(this_name_q) == MP_QSTR___main__) {
+            // This is a module run by -m command-line switch, get its real name from backup attribute
+            this_name_q = mp_obj_dict_get(MP_OBJ_FROM_PTR(mp_globals_get()), MP_OBJ_NEW_QSTR(MP_QSTR___main__));
+        }
+        #endif
+        mp_map_t *globals_map = &mp_globals_get()->map;
+        mp_map_elem_t *elem = mp_map_lookup(globals_map, MP_OBJ_NEW_QSTR(MP_QSTR___path__), MP_MAP_LOOKUP);
+        bool is_pkg = (elem != NULL);
+
+#if DEBUG_PRINT
+        DEBUG_printf("Current module/package: ");
+        mp_obj_print(this_name_q, PRINT_REPR);
+        DEBUG_printf(", is_package: %d", is_pkg);
+        DEBUG_printf("\n");
+#endif
+
+        size_t this_name_l;
+        const char *this_name = mp_obj_str_get_data(this_name_q, &this_name_l);
+
+        const char *p = this_name + this_name_l;
+        if (!is_pkg) {
+            // We have module, but relative imports are anchored at package, so
+            // go there.
+            chop_component(this_name, &p);
+        }
+
+        while (level--) {
+            chop_component(this_name, &p);
+        }
+
+        // We must have some component left over to import from
+        if (p == this_name) {
+            mp_raise_ValueError("cannot perform relative import");
+        }
+
+        uint new_mod_l = (mod_len == 0 ? (size_t)(p - this_name) : (size_t)(p - this_name) + 1 + mod_len);
+        char *new_mod = alloca(new_mod_l);
+        memcpy(new_mod, this_name, p - this_name);
+        if (mod_len != 0) {
+            new_mod[p - this_name] = '.';
+            memcpy(new_mod + (p - this_name) + 1, mod_str, mod_len);
+        }
+
+        qstr new_mod_q = qstr_from_strn(new_mod, new_mod_l);
+        DEBUG_printf("Resolved base name for relative import: '%s'\n", qstr_str(new_mod_q));
+        module_name = MP_OBJ_NEW_QSTR(new_mod_q);
+        mod_str = new_mod;
+        mod_len = new_mod_l;
+    }
+
+    // check if module already exists
+    qstr module_name_qstr = mp_obj_str_get_qstr(module_name);
+    mp_obj_t module_obj = mp_module_get(module_name_qstr);
+    if (module_obj != MP_OBJ_NULL) {
+        DEBUG_printf("Module already loaded\n");
+        // If it's not a package, return module right away
+        char *p = strchr(mod_str, '.');
+        if (p == NULL) {
+            return module_obj;
+        }
+        // If fromlist is not empty, return leaf module
+        if (fromtuple != mp_const_none) {
+            return module_obj;
+        }
+        // Otherwise, we need to return top-level package
+        qstr pkg_name = qstr_from_strn(mod_str, p - mod_str);
+        return mp_module_get(pkg_name);
+    }
+    DEBUG_printf("Module not yet loaded\n");
+
+    uint last = 0;
+    VSTR_FIXED(path, MICROPY_ALLOC_PATH_MAX)
+    module_obj = MP_OBJ_NULL;
+    mp_obj_t top_module_obj = MP_OBJ_NULL;
+    mp_obj_t outer_module_obj = MP_OBJ_NULL;
+    uint i;
+    for (i = 1; i <= mod_len; i++) {
+        if (i == mod_len || mod_str[i] == '.') {
+            // create a qstr for the module name up to this depth
+            qstr mod_name = qstr_from_strn(mod_str, i);
+            DEBUG_printf("Processing module: %s\n", qstr_str(mod_name));
+            DEBUG_printf("Previous path: =%.*s=\n", vstr_len(&path), vstr_str(&path));
+
+            // find the file corresponding to the module name
+            mp_import_stat_t stat;
+            if (vstr_len(&path) == 0) {
+                // first module in the dotted-name; search for a directory or file
+                stat = find_file(mod_str, i, &path);
+            } else {
+                // latter module in the dotted-name; append to path
+                vstr_add_char(&path, PATH_SEP_CHAR);
+                vstr_add_strn(&path, mod_str + last, i - last);
+                stat = stat_dir_or_file(&path);
+            }
+            DEBUG_printf("Current path: %.*s\n", vstr_len(&path), vstr_str(&path));
+
+            if (stat == MP_IMPORT_STAT_NO_EXIST) {
+                #if MICROPY_MODULE_WEAK_LINKS
+                // check if there is a weak link to this module
+                if (i == mod_len) {
+                    mp_map_elem_t *el = mp_map_lookup((mp_map_t*)&mp_builtin_module_weak_links_map, MP_OBJ_NEW_QSTR(mod_name), MP_MAP_LOOKUP);
+                    if (el == NULL) {
+                        goto no_exist;
+                    }
+                    // found weak linked module
+                    module_obj = el->value;
+                } else {
+                    no_exist:
+                #else
+                {
+                #endif
+                    // couldn't find the file, so fail
+                    if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) {
+                        mp_raise_msg(&mp_type_ImportError, "module not found");
+                    } else {
+                        nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ImportError,
+                            "no module named '%q'", mod_name));
+                    }
+                }
+            } else {
+                // found the file, so get the module
+                module_obj = mp_module_get(mod_name);
+            }
+
+            if (module_obj == MP_OBJ_NULL) {
+                // module not already loaded, so load it!
+
+                module_obj = mp_obj_new_module(mod_name);
+
+                // if args[3] (fromtuple) has magic value False, set up
+                // this module for command-line "-m" option (set module's
+                // name to __main__ instead of real name). Do this only
+                // for *modules* however - packages never have their names
+                // replaced, instead they're -m'ed using a special __main__
+                // submodule in them. (This all apparently is done to not
+                // touch package name itself, which is important for future
+                // imports).
+                if (i == mod_len && fromtuple == mp_const_false && stat != MP_IMPORT_STAT_DIR) {
+                    mp_obj_module_t *o = MP_OBJ_TO_PTR(module_obj);
+                    mp_obj_dict_store(MP_OBJ_FROM_PTR(o->globals), MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR___main__));
+                    #if MICROPY_CPYTHON_COMPAT
+                    // Store module as "__main__" in the dictionary of loaded modules (returned by sys.modules).
+                    mp_obj_dict_store(MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_loaded_modules_dict)), MP_OBJ_NEW_QSTR(MP_QSTR___main__), module_obj);
+                    // Store real name in "__main__" attribute. Chosen semi-randonly, to reuse existing qstr's.
+                    mp_obj_dict_store(MP_OBJ_FROM_PTR(o->globals), MP_OBJ_NEW_QSTR(MP_QSTR___main__), MP_OBJ_NEW_QSTR(mod_name));
+                    #endif
+                }
+
+                if (stat == MP_IMPORT_STAT_DIR) {
+                    DEBUG_printf("%.*s is dir\n", vstr_len(&path), vstr_str(&path));
+                    // https://docs.python.org/3/reference/import.html
+                    // "Specifically, any module that contains a __path__ attribute is considered a package."
+                    mp_store_attr(module_obj, MP_QSTR___path__, mp_obj_new_str(vstr_str(&path), vstr_len(&path), false));
+                    size_t orig_path_len = path.len;
+                    vstr_add_char(&path, PATH_SEP_CHAR);
+                    vstr_add_str(&path, "__init__.py");
+                    if (stat_file_py_or_mpy(&path) != MP_IMPORT_STAT_FILE) {
+                        //mp_warning("%s is imported as namespace package", vstr_str(&path));
+                    } else {
+                        do_load(module_obj, &path);
+                    }
+                    path.len = orig_path_len;
+                } else { // MP_IMPORT_STAT_FILE
+                    do_load(module_obj, &path);
+                    // This should be the last component in the import path.  If there are
+                    // remaining components then it's an ImportError because the current path
+                    // (the module that was just loaded) is not a package.  This will be caught
+                    // on the next iteration because the file will not exist.
+                }
+            }
+            if (outer_module_obj != MP_OBJ_NULL) {
+                qstr s = qstr_from_strn(mod_str + last, i - last);
+                mp_store_attr(outer_module_obj, s, module_obj);
+            }
+            outer_module_obj = module_obj;
+            if (top_module_obj == MP_OBJ_NULL) {
+                top_module_obj = module_obj;
+            }
+            last = i + 1;
+        }
+    }
+
+    // If fromlist is not empty, return leaf module
+    if (fromtuple != mp_const_none) {
+        return module_obj;
+    }
+    // Otherwise, we need to return top-level package
+    return top_module_obj;
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin___import___obj, 1, 5, mp_builtin___import__);

+ 3517 - 0
py/compile.c

@@ -0,0 +1,3517 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013-2015 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "py/scope.h"
+#include "py/emit.h"
+#include "py/compile.h"
+#include "py/runtime.h"
+#include "py/asmbase.h"
+
+#if MICROPY_ENABLE_COMPILER
+
+// TODO need to mangle __attr names
+
+#define INVALID_LABEL (0xffff)
+
+typedef enum {
+// define rules with a compile function
+#define DEF_RULE(rule, comp, kind, ...) PN_##rule,
+#define DEF_RULE_NC(rule, kind, ...)
+#include "py/grammar.h"
+#undef DEF_RULE
+#undef DEF_RULE_NC
+    PN_const_object, // special node for a constant, generic Python object
+// define rules without a compile function
+#define DEF_RULE(rule, comp, kind, ...)
+#define DEF_RULE_NC(rule, kind, ...) PN_##rule,
+#include "py/grammar.h"
+#undef DEF_RULE
+#undef DEF_RULE_NC
+} pn_kind_t;
+
+#define NEED_METHOD_TABLE MICROPY_EMIT_NATIVE
+
+#if NEED_METHOD_TABLE
+
+// we need a method table to do the lookup for the emitter functions
+#define EMIT(fun) (comp->emit_method_table->fun(comp->emit))
+#define EMIT_ARG(fun, ...) (comp->emit_method_table->fun(comp->emit, __VA_ARGS__))
+#define EMIT_LOAD_FAST(qst, local_num) (comp->emit_method_table->load_id.fast(comp->emit, qst, local_num))
+#define EMIT_LOAD_GLOBAL(qst) (comp->emit_method_table->load_id.global(comp->emit, qst))
+
+#else
+
+// if we only have the bytecode emitter enabled then we can do a direct call to the functions
+#define EMIT(fun) (mp_emit_bc_##fun(comp->emit))
+#define EMIT_ARG(fun, ...) (mp_emit_bc_##fun(comp->emit, __VA_ARGS__))
+#define EMIT_LOAD_FAST(qst, local_num) (mp_emit_bc_load_fast(comp->emit, qst, local_num))
+#define EMIT_LOAD_GLOBAL(qst) (mp_emit_bc_load_global(comp->emit, qst))
+
+#endif
+
+#if MICROPY_EMIT_NATIVE
+// define a macro to access external native emitter
+#if MICROPY_EMIT_X64
+#define NATIVE_EMITTER(f) emit_native_x64_##f
+#elif MICROPY_EMIT_X86
+#define NATIVE_EMITTER(f) emit_native_x86_##f
+#elif MICROPY_EMIT_THUMB
+#define NATIVE_EMITTER(f) emit_native_thumb_##f
+#elif MICROPY_EMIT_ARM
+#define NATIVE_EMITTER(f) emit_native_arm_##f
+#elif MICROPY_EMIT_XTENSA
+#define NATIVE_EMITTER(f) emit_native_xtensa_##f
+#else
+#error "unknown native emitter"
+#endif
+#endif
+
+#if MICROPY_EMIT_INLINE_ASM
+// define macros for inline assembler
+#if MICROPY_EMIT_INLINE_THUMB
+#define ASM_DECORATOR_QSTR MP_QSTR_asm_thumb
+#define ASM_EMITTER(f) emit_inline_thumb_##f
+#elif MICROPY_EMIT_INLINE_XTENSA
+#define ASM_DECORATOR_QSTR MP_QSTR_asm_xtensa
+#define ASM_EMITTER(f) emit_inline_xtensa_##f
+#else
+#error "unknown asm emitter"
+#endif
+#endif
+
+#define EMIT_INLINE_ASM(fun) (comp->emit_inline_asm_method_table->fun(comp->emit_inline_asm))
+#define EMIT_INLINE_ASM_ARG(fun, ...) (comp->emit_inline_asm_method_table->fun(comp->emit_inline_asm, __VA_ARGS__))
+
+// elements in this struct are ordered to make it compact
+typedef struct _compiler_t {
+    qstr source_file;
+
+    uint8_t is_repl;
+    uint8_t pass; // holds enum type pass_kind_t
+    uint8_t have_star;
+
+    // try to keep compiler clean from nlr
+    mp_obj_t compile_error; // set to an exception object if there's an error
+    size_t compile_error_line; // set to best guess of line of error
+
+    uint next_label;
+
+    uint16_t num_dict_params;
+    uint16_t num_default_params;
+
+    uint16_t break_label; // highest bit set indicates we are breaking out of a for loop
+    uint16_t continue_label;
+    uint16_t cur_except_level; // increased for SETUP_EXCEPT, SETUP_FINALLY; decreased for POP_BLOCK, POP_EXCEPT
+    uint16_t break_continue_except_level;
+
+    scope_t *scope_head;
+    scope_t *scope_cur;
+
+    emit_t *emit;                                   // current emitter
+    #if NEED_METHOD_TABLE
+    const emit_method_table_t *emit_method_table;   // current emit method table
+    #endif
+
+    #if MICROPY_EMIT_INLINE_ASM
+    emit_inline_asm_t *emit_inline_asm;                                   // current emitter for inline asm
+    const emit_inline_asm_method_table_t *emit_inline_asm_method_table;   // current emit method table for inline asm
+    #endif
+} compiler_t;
+
+STATIC void compile_error_set_line(compiler_t *comp, mp_parse_node_t pn) {
+    // if the line of the error is unknown then try to update it from the pn
+    if (comp->compile_error_line == 0 && MP_PARSE_NODE_IS_STRUCT(pn)) {
+        comp->compile_error_line = ((mp_parse_node_struct_t*)pn)->source_line;
+    }
+}
+
+STATIC void compile_syntax_error(compiler_t *comp, mp_parse_node_t pn, const char *msg) {
+    // only register the error if there has been no other error
+    if (comp->compile_error == MP_OBJ_NULL) {
+        comp->compile_error = mp_obj_new_exception_msg(&mp_type_SyntaxError, msg);
+        compile_error_set_line(comp, pn);
+    }
+}
+
+STATIC void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_arglist, bool is_method_call, int n_positional_extra);
+STATIC void compile_comprehension(compiler_t *comp, mp_parse_node_struct_t *pns, scope_kind_t kind);
+STATIC void compile_node(compiler_t *comp, mp_parse_node_t pn);
+
+STATIC uint comp_next_label(compiler_t *comp) {
+    return comp->next_label++;
+}
+
+STATIC void compile_increase_except_level(compiler_t *comp) {
+    comp->cur_except_level += 1;
+    if (comp->cur_except_level > comp->scope_cur->exc_stack_size) {
+        comp->scope_cur->exc_stack_size = comp->cur_except_level;
+    }
+}
+
+STATIC void compile_decrease_except_level(compiler_t *comp) {
+    assert(comp->cur_except_level > 0);
+    comp->cur_except_level -= 1;
+}
+
+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, emit_options);
+    scope->parent = comp->scope_cur;
+    scope->next = NULL;
+    if (comp->scope_head == NULL) {
+        comp->scope_head = scope;
+    } else {
+        scope_t *s = comp->scope_head;
+        while (s->next != NULL) {
+            s = s->next;
+        }
+        s->next = scope;
+    }
+    return scope;
+}
+
+typedef void (*apply_list_fun_t)(compiler_t *comp, mp_parse_node_t pn);
+
+STATIC void apply_to_single_or_list(compiler_t *comp, mp_parse_node_t pn, pn_kind_t pn_list_kind, apply_list_fun_t f) {
+    if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, pn_list_kind)) {
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
+        int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
+        for (int i = 0; i < num_nodes; i++) {
+            f(comp, pns->nodes[i]);
+        }
+    } else if (!MP_PARSE_NODE_IS_NULL(pn)) {
+        f(comp, pn);
+    }
+}
+
+STATIC void compile_generic_all_nodes(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
+    for (int i = 0; i < num_nodes; i++) {
+        compile_node(comp, pns->nodes[i]);
+        if (comp->compile_error != MP_OBJ_NULL) {
+            // add line info for the error in case it didn't have a line number
+            compile_error_set_line(comp, pns->nodes[i]);
+            return;
+        }
+    }
+}
+
+STATIC void compile_load_id(compiler_t *comp, qstr qst) {
+    if (comp->pass == MP_PASS_SCOPE) {
+        mp_emit_common_get_id_for_load(comp->scope_cur, qst);
+    } else {
+        #if NEED_METHOD_TABLE
+        mp_emit_common_id_op(comp->emit, &comp->emit_method_table->load_id, comp->scope_cur, qst);
+        #else
+        mp_emit_common_id_op(comp->emit, &mp_emit_bc_method_table_load_id_ops, comp->scope_cur, qst);
+        #endif
+    }
+}
+
+STATIC void compile_store_id(compiler_t *comp, qstr qst) {
+    if (comp->pass == MP_PASS_SCOPE) {
+        mp_emit_common_get_id_for_modification(comp->scope_cur, qst);
+    } else {
+        #if NEED_METHOD_TABLE
+        mp_emit_common_id_op(comp->emit, &comp->emit_method_table->store_id, comp->scope_cur, qst);
+        #else
+        mp_emit_common_id_op(comp->emit, &mp_emit_bc_method_table_store_id_ops, comp->scope_cur, qst);
+        #endif
+    }
+}
+
+STATIC void compile_delete_id(compiler_t *comp, qstr qst) {
+    if (comp->pass == MP_PASS_SCOPE) {
+        mp_emit_common_get_id_for_modification(comp->scope_cur, qst);
+    } else {
+        #if NEED_METHOD_TABLE
+        mp_emit_common_id_op(comp->emit, &comp->emit_method_table->delete_id, comp->scope_cur, qst);
+        #else
+        mp_emit_common_id_op(comp->emit, &mp_emit_bc_method_table_delete_id_ops, comp->scope_cur, qst);
+        #endif
+    }
+}
+
+STATIC void c_tuple(compiler_t *comp, mp_parse_node_t pn, mp_parse_node_struct_t *pns_list) {
+    int total = 0;
+    if (!MP_PARSE_NODE_IS_NULL(pn)) {
+        compile_node(comp, pn);
+        total += 1;
+    }
+    if (pns_list != NULL) {
+        int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns_list);
+        for (int i = 0; i < n; i++) {
+            compile_node(comp, pns_list->nodes[i]);
+        }
+        total += n;
+    }
+    EMIT_ARG(build_tuple, total);
+}
+
+STATIC void compile_generic_tuple(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    // a simple tuple expression
+    c_tuple(comp, MP_PARSE_NODE_NULL, pns);
+}
+
+STATIC void c_if_cond(compiler_t *comp, mp_parse_node_t pn, bool jump_if, int label) {
+    if (mp_parse_node_is_const_false(pn)) {
+        if (jump_if == false) {
+            EMIT_ARG(jump, label);
+        }
+        return;
+    } else if (mp_parse_node_is_const_true(pn)) {
+        if (jump_if == true) {
+            EMIT_ARG(jump, label);
+        }
+        return;
+    } else if (MP_PARSE_NODE_IS_STRUCT(pn)) {
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
+        int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
+        if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_or_test) {
+            if (jump_if == false) {
+            and_or_logic1:;
+                uint label2 = comp_next_label(comp);
+                for (int i = 0; i < n - 1; i++) {
+                    c_if_cond(comp, pns->nodes[i], !jump_if, label2);
+                }
+                c_if_cond(comp, pns->nodes[n - 1], jump_if, label);
+                EMIT_ARG(label_assign, label2);
+            } else {
+            and_or_logic2:
+                for (int i = 0; i < n; i++) {
+                    c_if_cond(comp, pns->nodes[i], jump_if, label);
+                }
+            }
+            return;
+        } else if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_and_test) {
+            if (jump_if == false) {
+                goto and_or_logic2;
+            } else {
+                goto and_or_logic1;
+            }
+        } else if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_not_test_2) {
+            c_if_cond(comp, pns->nodes[0], !jump_if, label);
+            return;
+        } else if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_atom_paren) {
+            // cond is something in parenthesis
+            if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
+                // empty tuple, acts as false for the condition
+                if (jump_if == false) {
+                    EMIT_ARG(jump, label);
+                }
+            } else {
+                assert(MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp));
+                // non-empty tuple, acts as true for the condition
+                if (jump_if == true) {
+                    EMIT_ARG(jump, label);
+                }
+            }
+            return;
+        }
+    }
+
+    // nothing special, fall back to default compiling for node and jump
+    compile_node(comp, pn);
+    EMIT_ARG(pop_jump_if, jump_if, label);
+}
+
+typedef enum { ASSIGN_STORE, ASSIGN_AUG_LOAD, ASSIGN_AUG_STORE } assign_kind_t;
+STATIC void c_assign(compiler_t *comp, mp_parse_node_t pn, assign_kind_t kind);
+
+STATIC void c_assign_atom_expr(compiler_t *comp, mp_parse_node_struct_t *pns, assign_kind_t assign_kind) {
+    if (assign_kind != ASSIGN_AUG_STORE) {
+        compile_node(comp, pns->nodes[0]);
+    }
+
+    if (MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])) {
+        mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t*)pns->nodes[1];
+        if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_atom_expr_trailers) {
+            int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns1);
+            if (assign_kind != ASSIGN_AUG_STORE) {
+                for (int i = 0; i < n - 1; i++) {
+                    compile_node(comp, pns1->nodes[i]);
+                }
+            }
+            assert(MP_PARSE_NODE_IS_STRUCT(pns1->nodes[n - 1]));
+            pns1 = (mp_parse_node_struct_t*)pns1->nodes[n - 1];
+        }
+        if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_trailer_bracket) {
+            if (assign_kind == ASSIGN_AUG_STORE) {
+                EMIT(rot_three);
+                EMIT(store_subscr);
+            } else {
+                compile_node(comp, pns1->nodes[0]);
+                if (assign_kind == ASSIGN_AUG_LOAD) {
+                    EMIT(dup_top_two);
+                    EMIT(load_subscr);
+                } else {
+                    EMIT(store_subscr);
+                }
+            }
+        } else if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_trailer_period) {
+            assert(MP_PARSE_NODE_IS_ID(pns1->nodes[0]));
+            if (assign_kind == ASSIGN_AUG_LOAD) {
+                EMIT(dup_top);
+                EMIT_ARG(load_attr, MP_PARSE_NODE_LEAF_ARG(pns1->nodes[0]));
+            } else {
+                if (assign_kind == ASSIGN_AUG_STORE) {
+                    EMIT(rot_two);
+                }
+                EMIT_ARG(store_attr, MP_PARSE_NODE_LEAF_ARG(pns1->nodes[0]));
+            }
+        } else {
+            goto cannot_assign;
+        }
+    } else {
+        goto cannot_assign;
+    }
+
+    return;
+
+cannot_assign:
+    compile_syntax_error(comp, (mp_parse_node_t)pns, "can't assign to expression");
+}
+
+// we need to allow for a caller passing in 1 initial node (node_head) followed by an array of nodes (nodes_tail)
+STATIC void c_assign_tuple(compiler_t *comp, mp_parse_node_t node_head, uint num_tail, mp_parse_node_t *nodes_tail) {
+    uint num_head = (node_head == MP_PARSE_NODE_NULL) ? 0 : 1;
+
+    // look for star expression
+    uint have_star_index = -1;
+    if (num_head != 0 && MP_PARSE_NODE_IS_STRUCT_KIND(node_head, PN_star_expr)) {
+        EMIT_ARG(unpack_ex, 0, num_tail);
+        have_star_index = 0;
+    }
+    for (uint i = 0; i < num_tail; i++) {
+        if (MP_PARSE_NODE_IS_STRUCT_KIND(nodes_tail[i], PN_star_expr)) {
+            if (have_star_index == (uint)-1) {
+                EMIT_ARG(unpack_ex, num_head + i, num_tail - i - 1);
+                have_star_index = num_head + i;
+            } else {
+                compile_syntax_error(comp, nodes_tail[i], "multiple *x in assignment");
+                return;
+            }
+        }
+    }
+    if (have_star_index == (uint)-1) {
+        EMIT_ARG(unpack_sequence, num_head + num_tail);
+    }
+    if (num_head != 0) {
+        if (0 == have_star_index) {
+            c_assign(comp, ((mp_parse_node_struct_t*)node_head)->nodes[0], ASSIGN_STORE);
+        } else {
+            c_assign(comp, node_head, ASSIGN_STORE);
+        }
+    }
+    for (uint i = 0; i < num_tail; i++) {
+        if (num_head + i == have_star_index) {
+            c_assign(comp, ((mp_parse_node_struct_t*)nodes_tail[i])->nodes[0], ASSIGN_STORE);
+        } else {
+            c_assign(comp, nodes_tail[i], ASSIGN_STORE);
+        }
+    }
+}
+
+// assigns top of stack to pn
+STATIC void c_assign(compiler_t *comp, mp_parse_node_t pn, assign_kind_t assign_kind) {
+    assert(!MP_PARSE_NODE_IS_NULL(pn));
+    if (MP_PARSE_NODE_IS_LEAF(pn)) {
+        if (MP_PARSE_NODE_IS_ID(pn)) {
+            qstr arg = MP_PARSE_NODE_LEAF_ARG(pn);
+            switch (assign_kind) {
+                case ASSIGN_STORE:
+                case ASSIGN_AUG_STORE:
+                    compile_store_id(comp, arg);
+                    break;
+                case ASSIGN_AUG_LOAD:
+                default:
+                    compile_load_id(comp, arg);
+                    break;
+            }
+        } else {
+            goto cannot_assign;
+        }
+    } else {
+        // pn must be a struct
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
+        switch (MP_PARSE_NODE_STRUCT_KIND(pns)) {
+            case PN_atom_expr_normal:
+                // lhs is an index or attribute
+                c_assign_atom_expr(comp, pns, assign_kind);
+                break;
+
+            case PN_testlist_star_expr:
+            case PN_exprlist:
+                // lhs is a tuple
+                if (assign_kind != ASSIGN_STORE) {
+                    goto cannot_assign;
+                }
+                c_assign_tuple(comp, MP_PARSE_NODE_NULL, MP_PARSE_NODE_STRUCT_NUM_NODES(pns), pns->nodes);
+                break;
+
+            case PN_atom_paren:
+                // lhs is something in parenthesis
+                if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
+                    // empty tuple
+                    goto cannot_assign;
+                } else {
+                    assert(MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp));
+                    if (assign_kind != ASSIGN_STORE) {
+                        goto cannot_assign;
+                    }
+                    pns = (mp_parse_node_struct_t*)pns->nodes[0];
+                    goto testlist_comp;
+                }
+                break;
+
+            case PN_atom_bracket:
+                // lhs is something in brackets
+                if (assign_kind != ASSIGN_STORE) {
+                    goto cannot_assign;
+                }
+                if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
+                    // empty list, assignment allowed
+                    c_assign_tuple(comp, MP_PARSE_NODE_NULL, 0, NULL);
+                } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp)) {
+                    pns = (mp_parse_node_struct_t*)pns->nodes[0];
+                    goto testlist_comp;
+                } else {
+                    // brackets around 1 item
+                    c_assign_tuple(comp, pns->nodes[0], 0, NULL);
+                }
+                break;
+
+            default:
+                goto cannot_assign;
+        }
+        return;
+
+        testlist_comp:
+        // lhs is a sequence
+        if (MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])) {
+            mp_parse_node_struct_t *pns2 = (mp_parse_node_struct_t*)pns->nodes[1];
+            if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_testlist_comp_3b) {
+                // sequence of one item, with trailing comma
+                assert(MP_PARSE_NODE_IS_NULL(pns2->nodes[0]));
+                c_assign_tuple(comp, pns->nodes[0], 0, NULL);
+            } else if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_testlist_comp_3c) {
+                // sequence of many items
+                uint n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns2);
+                c_assign_tuple(comp, pns->nodes[0], n, pns2->nodes);
+            } else if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_comp_for) {
+                goto cannot_assign;
+            } else {
+                // sequence with 2 items
+                goto sequence_with_2_items;
+            }
+        } else {
+            // sequence with 2 items
+            sequence_with_2_items:
+            c_assign_tuple(comp, MP_PARSE_NODE_NULL, 2, pns->nodes);
+        }
+        return;
+    }
+    return;
+
+    cannot_assign:
+    compile_syntax_error(comp, pn, "can't assign to expression");
+}
+
+// stuff for lambda and comprehensions and generators:
+//  if n_pos_defaults > 0 then there is a tuple on the stack with the positional defaults
+//  if n_kw_defaults > 0 then there is a dictionary on the stack with the keyword defaults
+//  if both exist, the tuple is above the dictionary (ie the first pop gets the tuple)
+STATIC void close_over_variables_etc(compiler_t *comp, scope_t *this_scope, int n_pos_defaults, int n_kw_defaults) {
+    assert(n_pos_defaults >= 0);
+    assert(n_kw_defaults >= 0);
+
+    // set flags
+    if (n_kw_defaults > 0) {
+        this_scope->scope_flags |= MP_SCOPE_FLAG_DEFKWARGS;
+    }
+    this_scope->num_def_pos_args = n_pos_defaults;
+
+    // make closed over variables, if any
+    // ensure they are closed over in the order defined in the outer scope (mainly to agree with CPython)
+    int nfree = 0;
+    if (comp->scope_cur->kind != SCOPE_MODULE) {
+        for (int i = 0; i < comp->scope_cur->id_info_len; i++) {
+            id_info_t *id = &comp->scope_cur->id_info[i];
+            if (id->kind == ID_INFO_KIND_CELL || id->kind == ID_INFO_KIND_FREE) {
+                for (int j = 0; j < this_scope->id_info_len; j++) {
+                    id_info_t *id2 = &this_scope->id_info[j];
+                    if (id2->kind == ID_INFO_KIND_FREE && id->qst == id2->qst) {
+                        // in MicroPython we load closures using LOAD_FAST
+                        EMIT_LOAD_FAST(id->qst, id->local_num);
+                        nfree += 1;
+                    }
+                }
+            }
+        }
+    }
+
+    // make the function/closure
+    if (nfree == 0) {
+        EMIT_ARG(make_function, this_scope, n_pos_defaults, n_kw_defaults);
+    } else {
+        EMIT_ARG(make_closure, this_scope, nfree, n_pos_defaults, n_kw_defaults);
+    }
+}
+
+STATIC void compile_funcdef_lambdef_param(compiler_t *comp, mp_parse_node_t pn) {
+    // For efficiency of the code below we extract the parse-node kind here
+    int pn_kind;
+    if (MP_PARSE_NODE_IS_ID(pn)) {
+        pn_kind = -1;
+    } else {
+        assert(MP_PARSE_NODE_IS_STRUCT(pn));
+        pn_kind = MP_PARSE_NODE_STRUCT_KIND((mp_parse_node_struct_t*)pn);
+    }
+
+    if (pn_kind == PN_typedargslist_star || pn_kind == PN_varargslist_star) {
+        comp->have_star = true;
+        /* don't need to distinguish bare from named star
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
+        if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
+            // bare star
+        } else {
+            // named star
+        }
+        */
+
+    } else if (pn_kind == PN_typedargslist_dbl_star || pn_kind == PN_varargslist_dbl_star) {
+        // named double star
+        // TODO do we need to do anything with this?
+
+    } else {
+        mp_parse_node_t pn_id;
+        mp_parse_node_t pn_equal;
+        if (pn_kind == -1) {
+            // this parameter is just an id
+
+            pn_id = pn;
+            pn_equal = MP_PARSE_NODE_NULL;
+
+        } else if (pn_kind == PN_typedargslist_name) {
+            // this parameter has a colon and/or equal specifier
+
+            mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
+            pn_id = pns->nodes[0];
+            //pn_colon = pns->nodes[1]; // unused
+            pn_equal = pns->nodes[2];
+
+        } else {
+            assert(pn_kind == PN_varargslist_name); // should be
+            // this parameter has an equal specifier
+
+            mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
+            pn_id = pns->nodes[0];
+            pn_equal = pns->nodes[1];
+        }
+
+        if (MP_PARSE_NODE_IS_NULL(pn_equal)) {
+            // this parameter does not have a default value
+
+            // check for non-default parameters given after default parameters (allowed by parser, but not syntactically valid)
+            if (!comp->have_star && comp->num_default_params != 0) {
+                compile_syntax_error(comp, pn, "non-default argument follows default argument");
+                return;
+            }
+
+        } else {
+            // this parameter has a default value
+            // in CPython, None (and True, False?) as default parameters are loaded with LOAD_NAME; don't understandy why
+
+            if (comp->have_star) {
+                comp->num_dict_params += 1;
+                // in MicroPython we put the default dict parameters into a dictionary using the bytecode
+                if (comp->num_dict_params == 1) {
+                    // in MicroPython we put the default positional parameters into a tuple using the bytecode
+                    // we need to do this here before we start building the map for the default keywords
+                    if (comp->num_default_params > 0) {
+                        EMIT_ARG(build_tuple, comp->num_default_params);
+                    } else {
+                        EMIT(load_null); // sentinel indicating empty default positional args
+                    }
+                    // first default dict param, so make the map
+                    EMIT_ARG(build_map, 0);
+                }
+
+                // compile value then key, then store it to the dict
+                compile_node(comp, pn_equal);
+                EMIT_ARG(load_const_str, MP_PARSE_NODE_LEAF_ARG(pn_id));
+                EMIT(store_map);
+            } else {
+                comp->num_default_params += 1;
+                compile_node(comp, pn_equal);
+            }
+        }
+    }
+}
+
+STATIC void compile_funcdef_lambdef(compiler_t *comp, scope_t *scope, mp_parse_node_t pn_params, pn_kind_t pn_list_kind) {
+    // When we call compile_funcdef_lambdef_param below it can compile an arbitrary
+    // expression for default arguments, which may contain a lambda.  The lambda will
+    // call here in a nested way, so we must save and restore the relevant state.
+    bool orig_have_star = comp->have_star;
+    uint16_t orig_num_dict_params = comp->num_dict_params;
+    uint16_t orig_num_default_params = comp->num_default_params;
+
+    // compile default parameters
+    comp->have_star = false;
+    comp->num_dict_params = 0;
+    comp->num_default_params = 0;
+    apply_to_single_or_list(comp, pn_params, pn_list_kind, compile_funcdef_lambdef_param);
+
+    if (comp->compile_error != MP_OBJ_NULL) {
+        return;
+    }
+
+    // in MicroPython we put the default positional parameters into a tuple using the bytecode
+    // the default keywords args may have already made the tuple; if not, do it now
+    if (comp->num_default_params > 0 && comp->num_dict_params == 0) {
+        EMIT_ARG(build_tuple, comp->num_default_params);
+        EMIT(load_null); // sentinel indicating empty default keyword args
+    }
+
+    // make the function
+    close_over_variables_etc(comp, scope, comp->num_default_params, comp->num_dict_params);
+
+    // restore state
+    comp->have_star = orig_have_star;
+    comp->num_dict_params = orig_num_dict_params;
+    comp->num_default_params = orig_num_default_params;
+}
+
+// leaves function object on stack
+// returns function name
+STATIC qstr compile_funcdef_helper(compiler_t *comp, mp_parse_node_struct_t *pns, uint emit_options) {
+    if (comp->pass == MP_PASS_SCOPE) {
+        // create a new scope for this function
+        scope_t *s = scope_new_and_link(comp, SCOPE_FUNCTION, (mp_parse_node_t)pns, emit_options);
+        // store the function scope so the compiling function can use it at each pass
+        pns->nodes[4] = (mp_parse_node_t)s;
+    }
+
+    // get the scope for this function
+    scope_t *fscope = (scope_t*)pns->nodes[4];
+
+    // compile the function definition
+    compile_funcdef_lambdef(comp, fscope, pns->nodes[1], PN_typedargslist);
+
+    // return its name (the 'f' in "def f(...):")
+    return fscope->simple_name;
+}
+
+// leaves class object on stack
+// returns class name
+STATIC qstr compile_classdef_helper(compiler_t *comp, mp_parse_node_struct_t *pns, uint emit_options) {
+    if (comp->pass == MP_PASS_SCOPE) {
+        // create a new scope for this class
+        scope_t *s = scope_new_and_link(comp, SCOPE_CLASS, (mp_parse_node_t)pns, emit_options);
+        // store the class scope so the compiling function can use it at each pass
+        pns->nodes[3] = (mp_parse_node_t)s;
+    }
+
+    EMIT(load_build_class);
+
+    // scope for this class
+    scope_t *cscope = (scope_t*)pns->nodes[3];
+
+    // compile the class
+    close_over_variables_etc(comp, cscope, 0, 0);
+
+    // get its name
+    EMIT_ARG(load_const_str, cscope->simple_name);
+
+    // nodes[1] has parent classes, if any
+    // empty parenthesis (eg class C():) gets here as an empty PN_classdef_2 and needs special handling
+    mp_parse_node_t parents = pns->nodes[1];
+    if (MP_PARSE_NODE_IS_STRUCT_KIND(parents, PN_classdef_2)) {
+        parents = MP_PARSE_NODE_NULL;
+    }
+    compile_trailer_paren_helper(comp, parents, false, 2);
+
+    // return its name (the 'C' in class C(...):")
+    return cscope->simple_name;
+}
+
+// returns true if it was a built-in decorator (even if the built-in had an error)
+STATIC bool compile_built_in_decorator(compiler_t *comp, int name_len, mp_parse_node_t *name_nodes, uint *emit_options) {
+    if (MP_PARSE_NODE_LEAF_ARG(name_nodes[0]) != MP_QSTR_micropython) {
+        return false;
+    }
+
+    if (name_len != 2) {
+        compile_syntax_error(comp, name_nodes[0], "invalid micropython decorator");
+        return true;
+    }
+
+    qstr attr = MP_PARSE_NODE_LEAF_ARG(name_nodes[1]);
+    if (attr == MP_QSTR_bytecode) {
+        *emit_options = MP_EMIT_OPT_BYTECODE;
+#if MICROPY_EMIT_NATIVE
+    } else if (attr == MP_QSTR_native) {
+        *emit_options = MP_EMIT_OPT_NATIVE_PYTHON;
+    } else if (attr == MP_QSTR_viper) {
+        *emit_options = MP_EMIT_OPT_VIPER;
+#endif
+    #if MICROPY_EMIT_INLINE_ASM
+    } else if (attr == ASM_DECORATOR_QSTR) {
+        *emit_options = MP_EMIT_OPT_ASM;
+    #endif
+    } else {
+        compile_syntax_error(comp, name_nodes[1], "invalid micropython decorator");
+    }
+
+    return true;
+}
+
+STATIC void compile_decorated(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    // get the list of decorators
+    mp_parse_node_t *nodes;
+    int n = mp_parse_node_extract_list(&pns->nodes[0], PN_decorators, &nodes);
+
+    // inherit emit options for this function/class definition
+    uint emit_options = comp->scope_cur->emit_options;
+
+    // compile each decorator
+    int num_built_in_decorators = 0;
+    for (int i = 0; i < n; i++) {
+        assert(MP_PARSE_NODE_IS_STRUCT_KIND(nodes[i], PN_decorator)); // should be
+        mp_parse_node_struct_t *pns_decorator = (mp_parse_node_struct_t*)nodes[i];
+
+        // nodes[0] contains the decorator function, which is a dotted name
+        mp_parse_node_t *name_nodes;
+        int name_len = mp_parse_node_extract_list(&pns_decorator->nodes[0], PN_dotted_name, &name_nodes);
+
+        // check for built-in decorators
+        if (compile_built_in_decorator(comp, name_len, name_nodes, &emit_options)) {
+            // this was a built-in
+            num_built_in_decorators += 1;
+
+        } else {
+            // not a built-in, compile normally
+
+            // compile the decorator function
+            compile_node(comp, name_nodes[0]);
+            for (int j = 1; j < name_len; j++) {
+                assert(MP_PARSE_NODE_IS_ID(name_nodes[j])); // should be
+                EMIT_ARG(load_attr, MP_PARSE_NODE_LEAF_ARG(name_nodes[j]));
+            }
+
+            // nodes[1] contains arguments to the decorator function, if any
+            if (!MP_PARSE_NODE_IS_NULL(pns_decorator->nodes[1])) {
+                // call the decorator function with the arguments in nodes[1]
+                compile_node(comp, pns_decorator->nodes[1]);
+            }
+        }
+    }
+
+    // compile the body (funcdef, async funcdef or classdef) and get its name
+    mp_parse_node_struct_t *pns_body = (mp_parse_node_struct_t*)pns->nodes[1];
+    qstr body_name = 0;
+    if (MP_PARSE_NODE_STRUCT_KIND(pns_body) == PN_funcdef) {
+        body_name = compile_funcdef_helper(comp, pns_body, emit_options);
+    #if MICROPY_PY_ASYNC_AWAIT
+    } else if (MP_PARSE_NODE_STRUCT_KIND(pns_body) == PN_async_funcdef) {
+        assert(MP_PARSE_NODE_IS_STRUCT(pns_body->nodes[0]));
+        mp_parse_node_struct_t *pns0 = (mp_parse_node_struct_t*)pns_body->nodes[0];
+        body_name = compile_funcdef_helper(comp, pns0, emit_options);
+        scope_t *fscope = (scope_t*)pns0->nodes[4];
+        fscope->scope_flags |= MP_SCOPE_FLAG_GENERATOR;
+    #endif
+    } else {
+        assert(MP_PARSE_NODE_STRUCT_KIND(pns_body) == PN_classdef); // should be
+        body_name = compile_classdef_helper(comp, pns_body, emit_options);
+    }
+
+    // call each decorator
+    for (int i = 0; i < n - num_built_in_decorators; i++) {
+        EMIT_ARG(call_function, 1, 0, 0);
+    }
+
+    // store func/class object into name
+    compile_store_id(comp, body_name);
+}
+
+STATIC void compile_funcdef(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    qstr fname = compile_funcdef_helper(comp, pns, comp->scope_cur->emit_options);
+    // store function object into function name
+    compile_store_id(comp, fname);
+}
+
+STATIC void c_del_stmt(compiler_t *comp, mp_parse_node_t pn) {
+    if (MP_PARSE_NODE_IS_ID(pn)) {
+        compile_delete_id(comp, MP_PARSE_NODE_LEAF_ARG(pn));
+    } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_atom_expr_normal)) {
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
+
+        compile_node(comp, pns->nodes[0]); // base of the atom_expr_normal node
+
+        if (MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])) {
+            mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t*)pns->nodes[1];
+            if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_atom_expr_trailers) {
+                int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns1);
+                for (int i = 0; i < n - 1; i++) {
+                    compile_node(comp, pns1->nodes[i]);
+                }
+                assert(MP_PARSE_NODE_IS_STRUCT(pns1->nodes[n - 1]));
+                pns1 = (mp_parse_node_struct_t*)pns1->nodes[n - 1];
+            }
+            if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_trailer_bracket) {
+                compile_node(comp, pns1->nodes[0]);
+                EMIT(delete_subscr);
+            } else if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_trailer_period) {
+                assert(MP_PARSE_NODE_IS_ID(pns1->nodes[0]));
+                EMIT_ARG(delete_attr, MP_PARSE_NODE_LEAF_ARG(pns1->nodes[0]));
+            } else {
+                goto cannot_delete;
+            }
+        } else {
+            goto cannot_delete;
+        }
+
+    } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_atom_paren)) {
+        pn = ((mp_parse_node_struct_t*)pn)->nodes[0];
+        if (MP_PARSE_NODE_IS_NULL(pn)) {
+            goto cannot_delete;
+        } else {
+            assert(MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_testlist_comp));
+            mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
+            // TODO perhaps factorise testlist_comp code with other uses of PN_testlist_comp
+
+            if (MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])) {
+                mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t*)pns->nodes[1];
+                if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_testlist_comp_3b) {
+                    // sequence of one item, with trailing comma
+                    assert(MP_PARSE_NODE_IS_NULL(pns1->nodes[0]));
+                    c_del_stmt(comp, pns->nodes[0]);
+                } else if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_testlist_comp_3c) {
+                    // sequence of many items
+                    int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns1);
+                    c_del_stmt(comp, pns->nodes[0]);
+                    for (int i = 0; i < n; i++) {
+                        c_del_stmt(comp, pns1->nodes[i]);
+                    }
+                } else if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_comp_for) {
+                    goto cannot_delete;
+                } else {
+                    // sequence with 2 items
+                    goto sequence_with_2_items;
+                }
+            } else {
+                // sequence with 2 items
+                sequence_with_2_items:
+                c_del_stmt(comp, pns->nodes[0]);
+                c_del_stmt(comp, pns->nodes[1]);
+            }
+        }
+    } else {
+        // some arbitrary statement that we can't delete (eg del 1)
+        goto cannot_delete;
+    }
+
+    return;
+
+cannot_delete:
+    compile_syntax_error(comp, (mp_parse_node_t)pn, "can't delete expression");
+}
+
+STATIC void compile_del_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    apply_to_single_or_list(comp, pns->nodes[0], PN_exprlist, c_del_stmt);
+}
+
+STATIC void compile_break_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    if (comp->break_label == INVALID_LABEL) {
+        compile_syntax_error(comp, (mp_parse_node_t)pns, "'break' outside loop");
+    }
+    assert(comp->cur_except_level >= comp->break_continue_except_level);
+    EMIT_ARG(break_loop, comp->break_label, comp->cur_except_level - comp->break_continue_except_level);
+}
+
+STATIC void compile_continue_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    if (comp->continue_label == INVALID_LABEL) {
+        compile_syntax_error(comp, (mp_parse_node_t)pns, "'continue' outside loop");
+    }
+    assert(comp->cur_except_level >= comp->break_continue_except_level);
+    EMIT_ARG(continue_loop, comp->continue_label, comp->cur_except_level - comp->break_continue_except_level);
+}
+
+STATIC void compile_return_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    if (comp->scope_cur->kind != SCOPE_FUNCTION) {
+        compile_syntax_error(comp, (mp_parse_node_t)pns, "'return' outside function");
+        return;
+    }
+    if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
+        // no argument to 'return', so return None
+        EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE);
+    } else if (MICROPY_COMP_RETURN_IF_EXPR
+        && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_test_if_expr)) {
+        // special case when returning an if-expression; to match CPython optimisation
+        mp_parse_node_struct_t *pns_test_if_expr = (mp_parse_node_struct_t*)pns->nodes[0];
+        mp_parse_node_struct_t *pns_test_if_else = (mp_parse_node_struct_t*)pns_test_if_expr->nodes[1];
+
+        uint l_fail = comp_next_label(comp);
+        c_if_cond(comp, pns_test_if_else->nodes[0], false, l_fail); // condition
+        compile_node(comp, pns_test_if_expr->nodes[0]); // success value
+        EMIT(return_value);
+        EMIT_ARG(label_assign, l_fail);
+        compile_node(comp, pns_test_if_else->nodes[1]); // failure value
+    } else {
+        compile_node(comp, pns->nodes[0]);
+    }
+    EMIT(return_value);
+}
+
+STATIC void compile_yield_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    compile_node(comp, pns->nodes[0]);
+    EMIT(pop_top);
+}
+
+STATIC void compile_raise_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
+        // raise
+        EMIT_ARG(raise_varargs, 0);
+    } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_raise_stmt_arg)) {
+        // raise x from y
+        pns = (mp_parse_node_struct_t*)pns->nodes[0];
+        compile_node(comp, pns->nodes[0]);
+        compile_node(comp, pns->nodes[1]);
+        EMIT_ARG(raise_varargs, 2);
+    } else {
+        // raise x
+        compile_node(comp, pns->nodes[0]);
+        EMIT_ARG(raise_varargs, 1);
+    }
+}
+
+// q_base holds the base of the name
+// eg   a -> q_base=a
+//      a.b.c -> q_base=a
+STATIC void do_import_name(compiler_t *comp, mp_parse_node_t pn, qstr *q_base) {
+    bool is_as = false;
+    if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_dotted_as_name)) {
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
+        // a name of the form x as y; unwrap it
+        *q_base = MP_PARSE_NODE_LEAF_ARG(pns->nodes[1]);
+        pn = pns->nodes[0];
+        is_as = true;
+    }
+    if (MP_PARSE_NODE_IS_NULL(pn)) {
+        // empty name (eg, from . import x)
+        *q_base = MP_QSTR_;
+        EMIT_ARG(import_name, MP_QSTR_); // import the empty string
+    } else if (MP_PARSE_NODE_IS_ID(pn)) {
+        // just a simple name
+        qstr q_full = MP_PARSE_NODE_LEAF_ARG(pn);
+        if (!is_as) {
+            *q_base = q_full;
+        }
+        EMIT_ARG(import_name, q_full);
+    } else {
+        assert(MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_dotted_name)); // should be
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
+        {
+            // a name of the form a.b.c
+            if (!is_as) {
+                *q_base = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
+            }
+            int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
+            int len = n - 1;
+            for (int i = 0; i < n; i++) {
+                len += qstr_len(MP_PARSE_NODE_LEAF_ARG(pns->nodes[i]));
+            }
+            char *q_ptr = alloca(len);
+            char *str_dest = q_ptr;
+            for (int i = 0; i < n; i++) {
+                if (i > 0) {
+                    *str_dest++ = '.';
+                }
+                size_t str_src_len;
+                const byte *str_src = qstr_data(MP_PARSE_NODE_LEAF_ARG(pns->nodes[i]), &str_src_len);
+                memcpy(str_dest, str_src, str_src_len);
+                str_dest += str_src_len;
+            }
+            qstr q_full = qstr_from_strn(q_ptr, len);
+            EMIT_ARG(import_name, q_full);
+            if (is_as) {
+                for (int i = 1; i < n; i++) {
+                    EMIT_ARG(load_attr, MP_PARSE_NODE_LEAF_ARG(pns->nodes[i]));
+                }
+            }
+        }
+    }
+}
+
+STATIC void compile_dotted_as_name(compiler_t *comp, mp_parse_node_t pn) {
+    EMIT_ARG(load_const_small_int, 0); // level 0 import
+    EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); // not importing from anything
+    qstr q_base;
+    do_import_name(comp, pn, &q_base);
+    compile_store_id(comp, q_base);
+}
+
+STATIC void compile_import_name(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    apply_to_single_or_list(comp, pns->nodes[0], PN_dotted_as_names, compile_dotted_as_name);
+}
+
+STATIC void compile_import_from(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    mp_parse_node_t pn_import_source = pns->nodes[0];
+
+    // extract the preceding .'s (if any) for a relative import, to compute the import level
+    uint import_level = 0;
+    do {
+        mp_parse_node_t pn_rel;
+        if (MP_PARSE_NODE_IS_TOKEN(pn_import_source) || MP_PARSE_NODE_IS_STRUCT_KIND(pn_import_source, PN_one_or_more_period_or_ellipsis)) {
+            // This covers relative imports with dots only like "from .. import"
+            pn_rel = pn_import_source;
+            pn_import_source = MP_PARSE_NODE_NULL;
+        } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn_import_source, PN_import_from_2b)) {
+            // This covers relative imports starting with dot(s) like "from .foo import"
+            mp_parse_node_struct_t *pns_2b = (mp_parse_node_struct_t*)pn_import_source;
+            pn_rel = pns_2b->nodes[0];
+            pn_import_source = pns_2b->nodes[1];
+            assert(!MP_PARSE_NODE_IS_NULL(pn_import_source)); // should not be
+        } else {
+            // Not a relative import
+            break;
+        }
+
+        // get the list of . and/or ...'s
+        mp_parse_node_t *nodes;
+        int n = mp_parse_node_extract_list(&pn_rel, PN_one_or_more_period_or_ellipsis, &nodes);
+
+        // count the total number of .'s
+        for (int i = 0; i < n; i++) {
+            if (MP_PARSE_NODE_IS_TOKEN_KIND(nodes[i], MP_TOKEN_DEL_PERIOD)) {
+                import_level++;
+            } else {
+                // should be an MP_TOKEN_ELLIPSIS
+                import_level += 3;
+            }
+        }
+    } while (0);
+
+    if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_STAR)) {
+        EMIT_ARG(load_const_small_int, import_level);
+
+        // build the "fromlist" tuple
+        EMIT_ARG(load_const_str, MP_QSTR__star_);
+        EMIT_ARG(build_tuple, 1);
+
+        // do the import
+        qstr dummy_q;
+        do_import_name(comp, pn_import_source, &dummy_q);
+        EMIT(import_star);
+
+    } else {
+        EMIT_ARG(load_const_small_int, import_level);
+
+        // build the "fromlist" tuple
+        mp_parse_node_t *pn_nodes;
+        int n = mp_parse_node_extract_list(&pns->nodes[1], PN_import_as_names, &pn_nodes);
+        for (int i = 0; i < n; i++) {
+            assert(MP_PARSE_NODE_IS_STRUCT_KIND(pn_nodes[i], PN_import_as_name));
+            mp_parse_node_struct_t *pns3 = (mp_parse_node_struct_t*)pn_nodes[i];
+            qstr id2 = MP_PARSE_NODE_LEAF_ARG(pns3->nodes[0]); // should be id
+            EMIT_ARG(load_const_str, id2);
+        }
+        EMIT_ARG(build_tuple, n);
+
+        // do the import
+        qstr dummy_q;
+        do_import_name(comp, pn_import_source, &dummy_q);
+        for (int i = 0; i < n; i++) {
+            assert(MP_PARSE_NODE_IS_STRUCT_KIND(pn_nodes[i], PN_import_as_name));
+            mp_parse_node_struct_t *pns3 = (mp_parse_node_struct_t*)pn_nodes[i];
+            qstr id2 = MP_PARSE_NODE_LEAF_ARG(pns3->nodes[0]); // should be id
+            EMIT_ARG(import_from, id2);
+            if (MP_PARSE_NODE_IS_NULL(pns3->nodes[1])) {
+                compile_store_id(comp, id2);
+            } else {
+                compile_store_id(comp, MP_PARSE_NODE_LEAF_ARG(pns3->nodes[1]));
+            }
+        }
+        EMIT(pop_top);
+    }
+}
+
+STATIC void compile_declare_global(compiler_t *comp, mp_parse_node_t pn, qstr qst) {
+    bool added;
+    id_info_t *id_info = scope_find_or_add_id(comp->scope_cur, qst, &added);
+    if (!added && id_info->kind != ID_INFO_KIND_GLOBAL_EXPLICIT) {
+        compile_syntax_error(comp, pn, "identifier redefined as global");
+        return;
+    }
+    id_info->kind = ID_INFO_KIND_GLOBAL_EXPLICIT;
+
+    // if the id exists in the global scope, set its kind to EXPLICIT_GLOBAL
+    id_info = scope_find_global(comp->scope_cur, qst);
+    if (id_info != NULL) {
+        id_info->kind = ID_INFO_KIND_GLOBAL_EXPLICIT;
+    }
+}
+
+STATIC void compile_global_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    if (comp->pass == MP_PASS_SCOPE) {
+        mp_parse_node_t *nodes;
+        int n = mp_parse_node_extract_list(&pns->nodes[0], PN_name_list, &nodes);
+        for (int i = 0; i < n; i++) {
+            compile_declare_global(comp, (mp_parse_node_t)pns, MP_PARSE_NODE_LEAF_ARG(nodes[i]));
+        }
+    }
+}
+
+STATIC void compile_declare_nonlocal(compiler_t *comp, mp_parse_node_t pn, qstr qst) {
+    bool added;
+    id_info_t *id_info = scope_find_or_add_id(comp->scope_cur, qst, &added);
+    if (added) {
+        scope_find_local_and_close_over(comp->scope_cur, id_info, qst);
+        if (id_info->kind == ID_INFO_KIND_GLOBAL_IMPLICIT) {
+            compile_syntax_error(comp, pn, "no binding for nonlocal found");
+        }
+    } else if (id_info->kind != ID_INFO_KIND_FREE) {
+        compile_syntax_error(comp, pn, "identifier redefined as nonlocal");
+    }
+}
+
+STATIC void compile_nonlocal_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    if (comp->pass == MP_PASS_SCOPE) {
+        if (comp->scope_cur->kind == SCOPE_MODULE) {
+            compile_syntax_error(comp, (mp_parse_node_t)pns, "can't declare nonlocal in outer code");
+            return;
+        }
+        mp_parse_node_t *nodes;
+        int n = mp_parse_node_extract_list(&pns->nodes[0], PN_name_list, &nodes);
+        for (int i = 0; i < n; i++) {
+            compile_declare_nonlocal(comp, (mp_parse_node_t)pns, MP_PARSE_NODE_LEAF_ARG(nodes[i]));
+        }
+    }
+}
+
+STATIC void compile_assert_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    // with optimisations enabled we don't compile assertions
+    if (MP_STATE_VM(mp_optimise_value) != 0) {
+        return;
+    }
+
+    uint l_end = comp_next_label(comp);
+    c_if_cond(comp, pns->nodes[0], true, l_end);
+    EMIT_LOAD_GLOBAL(MP_QSTR_AssertionError); // we load_global instead of load_id, to be consistent with CPython
+    if (!MP_PARSE_NODE_IS_NULL(pns->nodes[1])) {
+        // assertion message
+        compile_node(comp, pns->nodes[1]);
+        EMIT_ARG(call_function, 1, 0, 0);
+    }
+    EMIT_ARG(raise_varargs, 1);
+    EMIT_ARG(label_assign, l_end);
+}
+
+STATIC void compile_if_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    uint l_end = comp_next_label(comp);
+
+    // optimisation: don't emit anything when "if False"
+    if (!mp_parse_node_is_const_false(pns->nodes[0])) {
+        uint l_fail = comp_next_label(comp);
+        c_if_cond(comp, pns->nodes[0], false, l_fail); // if condition
+
+        compile_node(comp, pns->nodes[1]); // if block
+
+        // optimisation: skip everything else when "if True"
+        if (mp_parse_node_is_const_true(pns->nodes[0])) {
+            goto done;
+        }
+
+        if (
+            // optimisation: don't jump over non-existent elif/else blocks
+            !(MP_PARSE_NODE_IS_NULL(pns->nodes[2]) && MP_PARSE_NODE_IS_NULL(pns->nodes[3]))
+            // optimisation: don't jump if last instruction was return
+            && !EMIT(last_emit_was_return_value)
+            ) {
+            // jump over elif/else blocks
+            EMIT_ARG(jump, l_end);
+        }
+
+        EMIT_ARG(label_assign, l_fail);
+    }
+
+    // compile elif blocks (if any)
+    mp_parse_node_t *pn_elif;
+    int n_elif = mp_parse_node_extract_list(&pns->nodes[2], PN_if_stmt_elif_list, &pn_elif);
+    for (int i = 0; i < n_elif; i++) {
+        assert(MP_PARSE_NODE_IS_STRUCT_KIND(pn_elif[i], PN_if_stmt_elif)); // should be
+        mp_parse_node_struct_t *pns_elif = (mp_parse_node_struct_t*)pn_elif[i];
+
+        // optimisation: don't emit anything when "if False"
+        if (!mp_parse_node_is_const_false(pns_elif->nodes[0])) {
+            uint l_fail = comp_next_label(comp);
+            c_if_cond(comp, pns_elif->nodes[0], false, l_fail); // elif condition
+
+            compile_node(comp, pns_elif->nodes[1]); // elif block
+
+            // optimisation: skip everything else when "elif True"
+            if (mp_parse_node_is_const_true(pns_elif->nodes[0])) {
+                goto done;
+            }
+
+            // optimisation: don't jump if last instruction was return
+            if (!EMIT(last_emit_was_return_value)) {
+                EMIT_ARG(jump, l_end);
+            }
+            EMIT_ARG(label_assign, l_fail);
+        }
+    }
+
+    // compile else block
+    compile_node(comp, pns->nodes[3]); // can be null
+
+done:
+    EMIT_ARG(label_assign, l_end);
+}
+
+#define START_BREAK_CONTINUE_BLOCK \
+    uint16_t old_break_label = comp->break_label; \
+    uint16_t old_continue_label = comp->continue_label; \
+    uint16_t old_break_continue_except_level = comp->break_continue_except_level; \
+    uint break_label = comp_next_label(comp); \
+    uint continue_label = comp_next_label(comp); \
+    comp->break_label = break_label; \
+    comp->continue_label = continue_label; \
+    comp->break_continue_except_level = comp->cur_except_level;
+
+#define END_BREAK_CONTINUE_BLOCK \
+    comp->break_label = old_break_label; \
+    comp->continue_label = old_continue_label; \
+    comp->break_continue_except_level = old_break_continue_except_level;
+
+STATIC void compile_while_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    START_BREAK_CONTINUE_BLOCK
+
+    if (!mp_parse_node_is_const_false(pns->nodes[0])) { // optimisation: don't emit anything for "while False"
+        uint top_label = comp_next_label(comp);
+        if (!mp_parse_node_is_const_true(pns->nodes[0])) { // optimisation: don't jump to cond for "while True"
+            EMIT_ARG(jump, continue_label);
+        }
+        EMIT_ARG(label_assign, top_label);
+        compile_node(comp, pns->nodes[1]); // body
+        EMIT_ARG(label_assign, continue_label);
+        c_if_cond(comp, pns->nodes[0], true, top_label); // condition
+    }
+
+    // break/continue apply to outer loop (if any) in the else block
+    END_BREAK_CONTINUE_BLOCK
+
+    compile_node(comp, pns->nodes[2]); // else
+
+    EMIT_ARG(label_assign, break_label);
+}
+
+// This function compiles an optimised for-loop of the form:
+//      for <var> in range(<start>, <end>, <step>):
+//          <body>
+//      else:
+//          <else>
+// <var> must be an identifier and <step> must be a small-int.
+//
+// Semantics of for-loop require:
+//  - final failing value should not be stored in the loop variable
+//  - if the loop never runs, the loop variable should never be assigned
+//  - assignments to <var>, <end> or <step> in the body do not alter the loop
+//    (<step> is a constant for us, so no need to worry about it changing)
+//
+// If <end> is a small-int, then the stack during the for-loop contains just
+// the current value of <var>.  Otherwise, the stack contains <end> then the
+// current value of <var>.
+STATIC void compile_for_stmt_optimised_range(compiler_t *comp, mp_parse_node_t pn_var, mp_parse_node_t pn_start, mp_parse_node_t pn_end, mp_parse_node_t pn_step, mp_parse_node_t pn_body, mp_parse_node_t pn_else) {
+    START_BREAK_CONTINUE_BLOCK
+
+    uint top_label = comp_next_label(comp);
+    uint entry_label = comp_next_label(comp);
+
+    // put the end value on the stack if it's not a small-int constant
+    bool end_on_stack = !MP_PARSE_NODE_IS_SMALL_INT(pn_end);
+    if (end_on_stack) {
+        compile_node(comp, pn_end);
+    }
+
+    // compile: start
+    compile_node(comp, pn_start);
+
+    EMIT_ARG(jump, entry_label);
+    EMIT_ARG(label_assign, top_label);
+
+    // duplicate next value and store it to var
+    EMIT(dup_top);
+    c_assign(comp, pn_var, ASSIGN_STORE);
+
+    // compile body
+    compile_node(comp, pn_body);
+
+    EMIT_ARG(label_assign, continue_label);
+
+    // compile: var + step
+    compile_node(comp, pn_step);
+    EMIT_ARG(binary_op, MP_BINARY_OP_INPLACE_ADD);
+
+    EMIT_ARG(label_assign, entry_label);
+
+    // compile: if var <cond> end: goto top
+    if (end_on_stack) {
+        EMIT(dup_top_two);
+        EMIT(rot_two);
+    } else {
+        EMIT(dup_top);
+        compile_node(comp, pn_end);
+    }
+    assert(MP_PARSE_NODE_IS_SMALL_INT(pn_step));
+    if (MP_PARSE_NODE_LEAF_SMALL_INT(pn_step) >= 0) {
+        EMIT_ARG(binary_op, MP_BINARY_OP_LESS);
+    } else {
+        EMIT_ARG(binary_op, MP_BINARY_OP_MORE);
+    }
+    EMIT_ARG(pop_jump_if, true, top_label);
+
+    // break/continue apply to outer loop (if any) in the else block
+    END_BREAK_CONTINUE_BLOCK
+
+    // Compile the else block.  We must pop the iterator variables before
+    // executing the else code because it may contain break/continue statements.
+    uint end_label = 0;
+    if (!MP_PARSE_NODE_IS_NULL(pn_else)) {
+        // discard final value of "var", and possible "end" value
+        EMIT(pop_top);
+        if (end_on_stack) {
+            EMIT(pop_top);
+        }
+        compile_node(comp, pn_else);
+        end_label = comp_next_label(comp);
+        EMIT_ARG(jump, end_label);
+        EMIT_ARG(adjust_stack_size, 1 + end_on_stack);
+    }
+
+    EMIT_ARG(label_assign, break_label);
+
+    // discard final value of var that failed the loop condition
+    EMIT(pop_top);
+
+    // discard <end> value if it's on the stack
+    if (end_on_stack) {
+        EMIT(pop_top);
+    }
+
+    if (!MP_PARSE_NODE_IS_NULL(pn_else)) {
+        EMIT_ARG(label_assign, end_label);
+    }
+}
+
+STATIC void compile_for_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    // this bit optimises: for <x> in range(...), turning it into an explicitly incremented variable
+    // this is actually slower, but uses no heap memory
+    // for viper it will be much, much faster
+    if (/*comp->scope_cur->emit_options == MP_EMIT_OPT_VIPER &&*/ MP_PARSE_NODE_IS_ID(pns->nodes[0]) && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_atom_expr_normal)) {
+        mp_parse_node_struct_t *pns_it = (mp_parse_node_struct_t*)pns->nodes[1];
+        if (MP_PARSE_NODE_IS_ID(pns_it->nodes[0])
+            && MP_PARSE_NODE_LEAF_ARG(pns_it->nodes[0]) == MP_QSTR_range
+            && MP_PARSE_NODE_STRUCT_KIND((mp_parse_node_struct_t*)pns_it->nodes[1]) == PN_trailer_paren) {
+            mp_parse_node_t pn_range_args = ((mp_parse_node_struct_t*)pns_it->nodes[1])->nodes[0];
+            mp_parse_node_t *args;
+            int n_args = mp_parse_node_extract_list(&pn_range_args, PN_arglist, &args);
+            mp_parse_node_t pn_range_start;
+            mp_parse_node_t pn_range_end;
+            mp_parse_node_t pn_range_step;
+            bool optimize = false;
+            if (1 <= n_args && n_args <= 3) {
+                optimize = true;
+                if (n_args == 1) {
+                    pn_range_start = mp_parse_node_new_small_int(0);
+                    pn_range_end = args[0];
+                    pn_range_step = mp_parse_node_new_small_int(1);
+                } else if (n_args == 2) {
+                    pn_range_start = args[0];
+                    pn_range_end = args[1];
+                    pn_range_step = mp_parse_node_new_small_int(1);
+                } else {
+                    pn_range_start = args[0];
+                    pn_range_end = args[1];
+                    pn_range_step = args[2];
+                    // the step must be a non-zero constant integer to do the optimisation
+                    if (!MP_PARSE_NODE_IS_SMALL_INT(pn_range_step)
+                        || MP_PARSE_NODE_LEAF_SMALL_INT(pn_range_step) == 0) {
+                        optimize = false;
+                    }
+                }
+                // arguments must be able to be compiled as standard expressions
+                if (optimize && MP_PARSE_NODE_IS_STRUCT(pn_range_start)) {
+                    int k = MP_PARSE_NODE_STRUCT_KIND((mp_parse_node_struct_t*)pn_range_start);
+                    if (k == PN_arglist_star || k == PN_arglist_dbl_star || k == PN_argument) {
+                        optimize = false;
+                    }
+                }
+                if (optimize && MP_PARSE_NODE_IS_STRUCT(pn_range_end)) {
+                    int k = MP_PARSE_NODE_STRUCT_KIND((mp_parse_node_struct_t*)pn_range_end);
+                    if (k == PN_arglist_star || k == PN_arglist_dbl_star || k == PN_argument) {
+                        optimize = false;
+                    }
+                }
+            }
+            if (optimize) {
+                compile_for_stmt_optimised_range(comp, pns->nodes[0], pn_range_start, pn_range_end, pn_range_step, pns->nodes[2], pns->nodes[3]);
+                return;
+            }
+        }
+    }
+
+    START_BREAK_CONTINUE_BLOCK
+    comp->break_label |= MP_EMIT_BREAK_FROM_FOR;
+
+    uint pop_label = comp_next_label(comp);
+
+    compile_node(comp, pns->nodes[1]); // iterator
+    EMIT_ARG(get_iter, true);
+    EMIT_ARG(label_assign, continue_label);
+    EMIT_ARG(for_iter, pop_label);
+    c_assign(comp, pns->nodes[0], ASSIGN_STORE); // variable
+    compile_node(comp, pns->nodes[2]); // body
+    if (!EMIT(last_emit_was_return_value)) {
+        EMIT_ARG(jump, continue_label);
+    }
+    EMIT_ARG(label_assign, pop_label);
+    EMIT(for_iter_end);
+
+    // break/continue apply to outer loop (if any) in the else block
+    END_BREAK_CONTINUE_BLOCK
+
+    compile_node(comp, pns->nodes[3]); // else (may be empty)
+
+    EMIT_ARG(label_assign, break_label);
+}
+
+STATIC void compile_try_except(compiler_t *comp, mp_parse_node_t pn_body, int n_except, mp_parse_node_t *pn_excepts, mp_parse_node_t pn_else) {
+    // setup code
+    uint l1 = comp_next_label(comp);
+    uint success_label = comp_next_label(comp);
+
+    EMIT_ARG(setup_except, l1);
+    compile_increase_except_level(comp);
+
+    compile_node(comp, pn_body); // body
+    EMIT(pop_block);
+    EMIT_ARG(jump, success_label); // jump over exception handler
+
+    EMIT_ARG(label_assign, l1); // start of exception handler
+    EMIT(start_except_handler);
+
+    // at this point the top of the stack contains the exception instance that was raised
+
+    uint l2 = comp_next_label(comp);
+
+    for (int i = 0; i < n_except; i++) {
+        assert(MP_PARSE_NODE_IS_STRUCT_KIND(pn_excepts[i], PN_try_stmt_except)); // should be
+        mp_parse_node_struct_t *pns_except = (mp_parse_node_struct_t*)pn_excepts[i];
+
+        qstr qstr_exception_local = 0;
+        uint end_finally_label = comp_next_label(comp);
+
+        if (MP_PARSE_NODE_IS_NULL(pns_except->nodes[0])) {
+            // this is a catch all exception handler
+            if (i + 1 != n_except) {
+                compile_syntax_error(comp, pn_excepts[i], "default 'except' must be last");
+                compile_decrease_except_level(comp);
+                return;
+            }
+        } else {
+            // this exception handler requires a match to a certain type of exception
+            mp_parse_node_t pns_exception_expr = pns_except->nodes[0];
+            if (MP_PARSE_NODE_IS_STRUCT(pns_exception_expr)) {
+                mp_parse_node_struct_t *pns3 = (mp_parse_node_struct_t*)pns_exception_expr;
+                if (MP_PARSE_NODE_STRUCT_KIND(pns3) == PN_try_stmt_as_name) {
+                    // handler binds the exception to a local
+                    pns_exception_expr = pns3->nodes[0];
+                    qstr_exception_local = MP_PARSE_NODE_LEAF_ARG(pns3->nodes[1]);
+                }
+            }
+            EMIT(dup_top);
+            compile_node(comp, pns_exception_expr);
+            EMIT_ARG(binary_op, MP_BINARY_OP_EXCEPTION_MATCH);
+            EMIT_ARG(pop_jump_if, false, end_finally_label);
+        }
+
+        // either discard or store the exception instance
+        if (qstr_exception_local == 0) {
+            EMIT(pop_top);
+        } else {
+            compile_store_id(comp, qstr_exception_local);
+        }
+
+        uint l3 = 0;
+        if (qstr_exception_local != 0) {
+            l3 = comp_next_label(comp);
+            EMIT_ARG(setup_finally, l3);
+            compile_increase_except_level(comp);
+        }
+        compile_node(comp, pns_except->nodes[1]);
+        if (qstr_exception_local != 0) {
+            EMIT(pop_block);
+        }
+        EMIT(pop_except);
+        if (qstr_exception_local != 0) {
+            EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE);
+            EMIT_ARG(label_assign, l3);
+            EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE);
+            compile_store_id(comp, qstr_exception_local);
+            compile_delete_id(comp, qstr_exception_local);
+
+            compile_decrease_except_level(comp);
+            EMIT(end_finally);
+        }
+        EMIT_ARG(jump, l2);
+        EMIT_ARG(label_assign, end_finally_label);
+        EMIT_ARG(adjust_stack_size, 1); // stack adjust for the exception instance
+    }
+
+    compile_decrease_except_level(comp);
+    EMIT(end_finally);
+    EMIT(end_except_handler);
+
+    EMIT_ARG(label_assign, success_label);
+    compile_node(comp, pn_else); // else block, can be null
+    EMIT_ARG(label_assign, l2);
+}
+
+STATIC void compile_try_finally(compiler_t *comp, mp_parse_node_t pn_body, int n_except, mp_parse_node_t *pn_except, mp_parse_node_t pn_else, mp_parse_node_t pn_finally) {
+    uint l_finally_block = comp_next_label(comp);
+
+    EMIT_ARG(setup_finally, l_finally_block);
+    compile_increase_except_level(comp);
+
+    if (n_except == 0) {
+        assert(MP_PARSE_NODE_IS_NULL(pn_else));
+        EMIT_ARG(adjust_stack_size, 3); // stack adjust for possible UNWIND_JUMP state
+        compile_node(comp, pn_body);
+        EMIT_ARG(adjust_stack_size, -3);
+    } else {
+        compile_try_except(comp, pn_body, n_except, pn_except, pn_else);
+    }
+    EMIT(pop_block);
+    EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE);
+    EMIT_ARG(label_assign, l_finally_block);
+    compile_node(comp, pn_finally);
+
+    compile_decrease_except_level(comp);
+    EMIT(end_finally);
+}
+
+STATIC void compile_try_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    assert(MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])); // should be
+    {
+        mp_parse_node_struct_t *pns2 = (mp_parse_node_struct_t*)pns->nodes[1];
+        if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_try_stmt_finally) {
+            // just try-finally
+            compile_try_finally(comp, pns->nodes[0], 0, NULL, MP_PARSE_NODE_NULL, pns2->nodes[0]);
+        } else if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_try_stmt_except_and_more) {
+            // try-except and possibly else and/or finally
+            mp_parse_node_t *pn_excepts;
+            int n_except = mp_parse_node_extract_list(&pns2->nodes[0], PN_try_stmt_except_list, &pn_excepts);
+            if (MP_PARSE_NODE_IS_NULL(pns2->nodes[2])) {
+                // no finally
+                compile_try_except(comp, pns->nodes[0], n_except, pn_excepts, pns2->nodes[1]);
+            } else {
+                // have finally
+                compile_try_finally(comp, pns->nodes[0], n_except, pn_excepts, pns2->nodes[1], ((mp_parse_node_struct_t*)pns2->nodes[2])->nodes[0]);
+            }
+        } else {
+            // just try-except
+            mp_parse_node_t *pn_excepts;
+            int n_except = mp_parse_node_extract_list(&pns->nodes[1], PN_try_stmt_except_list, &pn_excepts);
+            compile_try_except(comp, pns->nodes[0], n_except, pn_excepts, MP_PARSE_NODE_NULL);
+        }
+    }
+}
+
+STATIC void compile_with_stmt_helper(compiler_t *comp, int n, mp_parse_node_t *nodes, mp_parse_node_t body) {
+    if (n == 0) {
+        // no more pre-bits, compile the body of the with
+        compile_node(comp, body);
+    } else {
+        uint l_end = comp_next_label(comp);
+        if (MICROPY_EMIT_NATIVE && comp->scope_cur->emit_options != MP_EMIT_OPT_BYTECODE) {
+            // we need to allocate an extra label for the native emitter
+            // it will use l_end+1 as an auxiliary label
+            comp_next_label(comp);
+        }
+        if (MP_PARSE_NODE_IS_STRUCT_KIND(nodes[0], PN_with_item)) {
+            // this pre-bit is of the form "a as b"
+            mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)nodes[0];
+            compile_node(comp, pns->nodes[0]);
+            EMIT_ARG(setup_with, l_end);
+            c_assign(comp, pns->nodes[1], ASSIGN_STORE);
+        } else {
+            // this pre-bit is just an expression
+            compile_node(comp, nodes[0]);
+            EMIT_ARG(setup_with, l_end);
+            EMIT(pop_top);
+        }
+        compile_increase_except_level(comp);
+        // compile additional pre-bits and the body
+        compile_with_stmt_helper(comp, n - 1, nodes + 1, body);
+        // finish this with block
+        EMIT_ARG(with_cleanup, l_end);
+        compile_decrease_except_level(comp);
+        EMIT(end_finally);
+    }
+}
+
+STATIC void compile_with_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    // get the nodes for the pre-bit of the with (the a as b, c as d, ... bit)
+    mp_parse_node_t *nodes;
+    int n = mp_parse_node_extract_list(&pns->nodes[0], PN_with_stmt_list, &nodes);
+    assert(n > 0);
+
+    // compile in a nested fashion
+    compile_with_stmt_helper(comp, n, nodes, pns->nodes[1]);
+}
+
+STATIC void compile_yield_from(compiler_t *comp) {
+    EMIT_ARG(get_iter, false);
+    EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE);
+    EMIT(yield_from);
+}
+
+#if MICROPY_PY_ASYNC_AWAIT
+STATIC void compile_await_object_method(compiler_t *comp, qstr method) {
+    EMIT_ARG(load_method, method, false);
+    EMIT_ARG(call_method, 0, 0, 0);
+    compile_yield_from(comp);
+}
+
+STATIC void compile_async_for_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    // comp->break_label |= MP_EMIT_BREAK_FROM_FOR;
+
+    qstr context = MP_PARSE_NODE_LEAF_ARG(pns->nodes[1]);
+    uint while_else_label = comp_next_label(comp);
+    uint try_exception_label = comp_next_label(comp);
+    uint try_else_label = comp_next_label(comp);
+    uint try_finally_label = comp_next_label(comp);
+
+    compile_node(comp, pns->nodes[1]); // iterator
+    compile_await_object_method(comp, MP_QSTR___aiter__);
+    compile_store_id(comp, context);
+
+    START_BREAK_CONTINUE_BLOCK
+
+    EMIT_ARG(label_assign, continue_label);
+
+    EMIT_ARG(setup_except, try_exception_label);
+    compile_increase_except_level(comp);
+
+    compile_load_id(comp, context);
+    compile_await_object_method(comp, MP_QSTR___anext__);
+    c_assign(comp, pns->nodes[0], ASSIGN_STORE); // variable
+    EMIT(pop_block);
+    EMIT_ARG(jump, try_else_label);
+
+    EMIT_ARG(label_assign, try_exception_label);
+    EMIT(start_except_handler);
+    EMIT(dup_top);
+    EMIT_LOAD_GLOBAL(MP_QSTR_StopAsyncIteration);
+    EMIT_ARG(binary_op, MP_BINARY_OP_EXCEPTION_MATCH);
+    EMIT_ARG(pop_jump_if, false, try_finally_label);
+    EMIT(pop_top); // pop exception instance
+    EMIT(pop_except);
+    EMIT_ARG(jump, while_else_label);
+
+    EMIT_ARG(label_assign, try_finally_label);
+    EMIT_ARG(adjust_stack_size, 1); // if we jump here, the exc is on the stack
+    compile_decrease_except_level(comp);
+    EMIT(end_finally);
+    EMIT(end_except_handler);
+
+    EMIT_ARG(label_assign, try_else_label);
+    compile_node(comp, pns->nodes[2]); // body
+
+    EMIT_ARG(jump, continue_label);
+    // break/continue apply to outer loop (if any) in the else block
+    END_BREAK_CONTINUE_BLOCK
+
+    EMIT_ARG(label_assign, while_else_label);
+    compile_node(comp, pns->nodes[3]); // else
+
+    EMIT_ARG(label_assign, break_label);
+}
+
+STATIC void compile_async_with_stmt_helper(compiler_t *comp, int n, mp_parse_node_t *nodes, mp_parse_node_t body) {
+    if (n == 0) {
+        // no more pre-bits, compile the body of the with
+        compile_node(comp, body);
+    } else {
+        uint try_exception_label = comp_next_label(comp);
+        uint no_reraise_label = comp_next_label(comp);
+        uint try_else_label = comp_next_label(comp);
+        uint end_label = comp_next_label(comp);
+        qstr context;
+
+        if (MP_PARSE_NODE_IS_STRUCT_KIND(nodes[0], PN_with_item)) {
+            // this pre-bit is of the form "a as b"
+            mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)nodes[0];
+            compile_node(comp, pns->nodes[0]);
+            context = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
+            compile_store_id(comp, context);
+            compile_load_id(comp, context);
+            compile_await_object_method(comp, MP_QSTR___aenter__);
+            c_assign(comp, pns->nodes[1], ASSIGN_STORE);
+        } else {
+            // this pre-bit is just an expression
+            compile_node(comp, nodes[0]);
+            context = MP_PARSE_NODE_LEAF_ARG(nodes[0]);
+            compile_store_id(comp, context);
+            compile_load_id(comp, context);
+            compile_await_object_method(comp, MP_QSTR___aenter__);
+            EMIT(pop_top);
+        }
+
+        compile_load_id(comp, context);
+        EMIT_ARG(load_method, MP_QSTR___aexit__, false);
+
+        EMIT_ARG(setup_except, try_exception_label);
+        compile_increase_except_level(comp);
+        // compile additional pre-bits and the body
+        compile_async_with_stmt_helper(comp, n - 1, nodes + 1, body);
+        // finish this with block
+        EMIT(pop_block);
+        EMIT_ARG(jump, try_else_label); // jump over exception handler
+
+        EMIT_ARG(label_assign, try_exception_label); // start of exception handler
+        EMIT(start_except_handler);
+
+        // at this point the stack contains: ..., __aexit__, self, exc
+        EMIT(dup_top);
+        #if MICROPY_CPYTHON_COMPAT
+        EMIT_ARG(load_attr, MP_QSTR___class__); // get type(exc)
+        #else
+        compile_load_id(comp, MP_QSTR_type);
+        EMIT(rot_two);
+        EMIT_ARG(call_function, 1, 0, 0); // get type(exc)
+        #endif
+        EMIT(rot_two);
+        EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); // dummy traceback value
+        // at this point the stack contains: ..., __aexit__, self, type(exc), exc, None
+        EMIT_ARG(call_method, 3, 0, 0);
+
+        compile_yield_from(comp);
+        EMIT_ARG(pop_jump_if, true, no_reraise_label);
+        EMIT_ARG(raise_varargs, 0);
+
+        EMIT_ARG(label_assign, no_reraise_label);
+        EMIT(pop_except);
+        EMIT_ARG(jump, end_label);
+
+        EMIT_ARG(adjust_stack_size, 3); // adjust for __aexit__, self, exc
+        compile_decrease_except_level(comp);
+        EMIT(end_finally);
+        EMIT(end_except_handler);
+
+        EMIT_ARG(label_assign, try_else_label); // start of try-else handler
+        EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE);
+        EMIT(dup_top);
+        EMIT(dup_top);
+        EMIT_ARG(call_method, 3, 0, 0);
+        compile_yield_from(comp);
+        EMIT(pop_top);
+
+        EMIT_ARG(label_assign, end_label);
+
+    }
+}
+
+STATIC void compile_async_with_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    // get the nodes for the pre-bit of the with (the a as b, c as d, ... bit)
+    mp_parse_node_t *nodes;
+    int n = mp_parse_node_extract_list(&pns->nodes[0], PN_with_stmt_list, &nodes);
+    assert(n > 0);
+
+    // compile in a nested fashion
+    compile_async_with_stmt_helper(comp, n, nodes, pns->nodes[1]);
+}
+
+STATIC void compile_async_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    assert(MP_PARSE_NODE_IS_STRUCT(pns->nodes[0]));
+    mp_parse_node_struct_t *pns0 = (mp_parse_node_struct_t*)pns->nodes[0];
+    if (MP_PARSE_NODE_STRUCT_KIND(pns0) == PN_funcdef) {
+        // async def
+        compile_funcdef(comp, pns0);
+        scope_t *fscope = (scope_t*)pns0->nodes[4];
+        fscope->scope_flags |= MP_SCOPE_FLAG_GENERATOR;
+    } else if (MP_PARSE_NODE_STRUCT_KIND(pns0) == PN_for_stmt) {
+        // async for
+        compile_async_for_stmt(comp, pns0);
+    } else {
+        // async with
+        assert(MP_PARSE_NODE_STRUCT_KIND(pns0) == PN_with_stmt);
+        compile_async_with_stmt(comp, pns0);
+    }
+}
+#endif
+
+STATIC void compile_expr_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    if (MP_PARSE_NODE_IS_NULL(pns->nodes[1])) {
+        if (comp->is_repl && comp->scope_cur->kind == SCOPE_MODULE) {
+            // for REPL, evaluate then print the expression
+            compile_load_id(comp, MP_QSTR___repl_print__);
+            compile_node(comp, pns->nodes[0]);
+            EMIT_ARG(call_function, 1, 0, 0);
+            EMIT(pop_top);
+
+        } else {
+            // for non-REPL, evaluate then discard the expression
+            if ((MP_PARSE_NODE_IS_LEAF(pns->nodes[0]) && !MP_PARSE_NODE_IS_ID(pns->nodes[0]))
+                || MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_const_object)) {
+                // do nothing with a lonely constant
+            } else {
+                compile_node(comp, pns->nodes[0]); // just an expression
+                EMIT(pop_top); // discard last result since this is a statement and leaves nothing on the stack
+            }
+        }
+    } else if (MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])) {
+        mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t*)pns->nodes[1];
+        int kind = MP_PARSE_NODE_STRUCT_KIND(pns1);
+        if (kind == PN_expr_stmt_augassign) {
+            c_assign(comp, pns->nodes[0], ASSIGN_AUG_LOAD); // lhs load for aug assign
+            compile_node(comp, pns1->nodes[1]); // rhs
+            assert(MP_PARSE_NODE_IS_TOKEN(pns1->nodes[0]));
+            mp_binary_op_t op;
+            switch (MP_PARSE_NODE_LEAF_ARG(pns1->nodes[0])) {
+                case MP_TOKEN_DEL_PIPE_EQUAL: op = MP_BINARY_OP_INPLACE_OR; break;
+                case MP_TOKEN_DEL_CARET_EQUAL: op = MP_BINARY_OP_INPLACE_XOR; break;
+                case MP_TOKEN_DEL_AMPERSAND_EQUAL: op = MP_BINARY_OP_INPLACE_AND; break;
+                case MP_TOKEN_DEL_DBL_LESS_EQUAL: op = MP_BINARY_OP_INPLACE_LSHIFT; break;
+                case MP_TOKEN_DEL_DBL_MORE_EQUAL: op = MP_BINARY_OP_INPLACE_RSHIFT; break;
+                case MP_TOKEN_DEL_PLUS_EQUAL: op = MP_BINARY_OP_INPLACE_ADD; break;
+                case MP_TOKEN_DEL_MINUS_EQUAL: op = MP_BINARY_OP_INPLACE_SUBTRACT; break;
+                case MP_TOKEN_DEL_STAR_EQUAL: op = MP_BINARY_OP_INPLACE_MULTIPLY; break;
+                case MP_TOKEN_DEL_DBL_SLASH_EQUAL: op = MP_BINARY_OP_INPLACE_FLOOR_DIVIDE; break;
+                case MP_TOKEN_DEL_SLASH_EQUAL: op = MP_BINARY_OP_INPLACE_TRUE_DIVIDE; break;
+                case MP_TOKEN_DEL_PERCENT_EQUAL: op = MP_BINARY_OP_INPLACE_MODULO; break;
+                case MP_TOKEN_DEL_DBL_STAR_EQUAL: default: op = MP_BINARY_OP_INPLACE_POWER; break;
+            }
+            EMIT_ARG(binary_op, op);
+            c_assign(comp, pns->nodes[0], ASSIGN_AUG_STORE); // lhs store for aug assign
+        } else if (kind == PN_expr_stmt_assign_list) {
+            int rhs = MP_PARSE_NODE_STRUCT_NUM_NODES(pns1) - 1;
+            compile_node(comp, pns1->nodes[rhs]); // rhs
+            // following CPython, we store left-most first
+            if (rhs > 0) {
+                EMIT(dup_top);
+            }
+            c_assign(comp, pns->nodes[0], ASSIGN_STORE); // lhs store
+            for (int i = 0; i < rhs; i++) {
+                if (i + 1 < rhs) {
+                    EMIT(dup_top);
+                }
+                c_assign(comp, pns1->nodes[i], ASSIGN_STORE); // middle store
+            }
+        } else {
+        plain_assign:
+            if (MICROPY_COMP_DOUBLE_TUPLE_ASSIGN
+                && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_testlist_star_expr)
+                && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_star_expr)
+                && MP_PARSE_NODE_STRUCT_NUM_NODES((mp_parse_node_struct_t*)pns->nodes[1]) == 2
+                && MP_PARSE_NODE_STRUCT_NUM_NODES((mp_parse_node_struct_t*)pns->nodes[0]) == 2) {
+                // optimisation for a, b = c, d
+                mp_parse_node_struct_t *pns10 = (mp_parse_node_struct_t*)pns->nodes[1];
+                mp_parse_node_struct_t *pns0 = (mp_parse_node_struct_t*)pns->nodes[0];
+                if (MP_PARSE_NODE_IS_STRUCT_KIND(pns0->nodes[0], PN_star_expr)
+                    || MP_PARSE_NODE_IS_STRUCT_KIND(pns0->nodes[1], PN_star_expr)) {
+                    // can't optimise when it's a star expression on the lhs
+                    goto no_optimisation;
+                }
+                compile_node(comp, pns10->nodes[0]); // rhs
+                compile_node(comp, pns10->nodes[1]); // rhs
+                EMIT(rot_two);
+                c_assign(comp, pns0->nodes[0], ASSIGN_STORE); // lhs store
+                c_assign(comp, pns0->nodes[1], ASSIGN_STORE); // lhs store
+            } else if (MICROPY_COMP_TRIPLE_TUPLE_ASSIGN
+                && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_testlist_star_expr)
+                && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_star_expr)
+                && MP_PARSE_NODE_STRUCT_NUM_NODES((mp_parse_node_struct_t*)pns->nodes[1]) == 3
+                && MP_PARSE_NODE_STRUCT_NUM_NODES((mp_parse_node_struct_t*)pns->nodes[0]) == 3) {
+                // optimisation for a, b, c = d, e, f
+                mp_parse_node_struct_t *pns10 = (mp_parse_node_struct_t*)pns->nodes[1];
+                mp_parse_node_struct_t *pns0 = (mp_parse_node_struct_t*)pns->nodes[0];
+                if (MP_PARSE_NODE_IS_STRUCT_KIND(pns0->nodes[0], PN_star_expr)
+                    || MP_PARSE_NODE_IS_STRUCT_KIND(pns0->nodes[1], PN_star_expr)
+                    || MP_PARSE_NODE_IS_STRUCT_KIND(pns0->nodes[2], PN_star_expr)) {
+                    // can't optimise when it's a star expression on the lhs
+                    goto no_optimisation;
+                }
+                compile_node(comp, pns10->nodes[0]); // rhs
+                compile_node(comp, pns10->nodes[1]); // rhs
+                compile_node(comp, pns10->nodes[2]); // rhs
+                EMIT(rot_three);
+                EMIT(rot_two);
+                c_assign(comp, pns0->nodes[0], ASSIGN_STORE); // lhs store
+                c_assign(comp, pns0->nodes[1], ASSIGN_STORE); // lhs store
+                c_assign(comp, pns0->nodes[2], ASSIGN_STORE); // lhs store
+            } else {
+                no_optimisation:
+                compile_node(comp, pns->nodes[1]); // rhs
+                c_assign(comp, pns->nodes[0], ASSIGN_STORE); // lhs store
+            }
+        }
+    } else {
+        goto plain_assign;
+    }
+}
+
+STATIC void c_binary_op(compiler_t *comp, mp_parse_node_struct_t *pns, mp_binary_op_t binary_op) {
+    int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
+    compile_node(comp, pns->nodes[0]);
+    for (int i = 1; i < num_nodes; i += 1) {
+        compile_node(comp, pns->nodes[i]);
+        EMIT_ARG(binary_op, binary_op);
+    }
+}
+
+STATIC void compile_test_if_expr(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    assert(MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_test_if_else));
+    mp_parse_node_struct_t *pns_test_if_else = (mp_parse_node_struct_t*)pns->nodes[1];
+
+    uint l_fail = comp_next_label(comp);
+    uint l_end = comp_next_label(comp);
+    c_if_cond(comp, pns_test_if_else->nodes[0], false, l_fail); // condition
+    compile_node(comp, pns->nodes[0]); // success value
+    EMIT_ARG(jump, l_end);
+    EMIT_ARG(label_assign, l_fail);
+    EMIT_ARG(adjust_stack_size, -1); // adjust stack size
+    compile_node(comp, pns_test_if_else->nodes[1]); // failure value
+    EMIT_ARG(label_assign, l_end);
+}
+
+STATIC void compile_lambdef(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    if (comp->pass == MP_PASS_SCOPE) {
+        // create a new scope for this lambda
+        scope_t *s = scope_new_and_link(comp, SCOPE_LAMBDA, (mp_parse_node_t)pns, comp->scope_cur->emit_options);
+        // store the lambda scope so the compiling function (this one) can use it at each pass
+        pns->nodes[2] = (mp_parse_node_t)s;
+    }
+
+    // get the scope for this lambda
+    scope_t *this_scope = (scope_t*)pns->nodes[2];
+
+    // compile the lambda definition
+    compile_funcdef_lambdef(comp, this_scope, pns->nodes[0], PN_varargslist);
+}
+
+STATIC void compile_or_and_test(compiler_t *comp, mp_parse_node_struct_t *pns, bool cond) {
+    uint l_end = comp_next_label(comp);
+    int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
+    for (int i = 0; i < n; i += 1) {
+        compile_node(comp, pns->nodes[i]);
+        if (i + 1 < n) {
+            EMIT_ARG(jump_if_or_pop, cond, l_end);
+        }
+    }
+    EMIT_ARG(label_assign, l_end);
+}
+
+STATIC void compile_or_test(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    compile_or_and_test(comp, pns, true);
+}
+
+STATIC void compile_and_test(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    compile_or_and_test(comp, pns, false);
+}
+
+STATIC void compile_not_test_2(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    compile_node(comp, pns->nodes[0]);
+    EMIT_ARG(unary_op, MP_UNARY_OP_NOT);
+}
+
+STATIC void compile_comparison(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
+    compile_node(comp, pns->nodes[0]);
+    bool multi = (num_nodes > 3);
+    uint l_fail = 0;
+    if (multi) {
+        l_fail = comp_next_label(comp);
+    }
+    for (int i = 1; i + 1 < num_nodes; i += 2) {
+        compile_node(comp, pns->nodes[i + 1]);
+        if (i + 2 < num_nodes) {
+            EMIT(dup_top);
+            EMIT(rot_three);
+        }
+        if (MP_PARSE_NODE_IS_TOKEN(pns->nodes[i])) {
+            mp_binary_op_t op;
+            switch (MP_PARSE_NODE_LEAF_ARG(pns->nodes[i])) {
+                case MP_TOKEN_OP_LESS: op = MP_BINARY_OP_LESS; break;
+                case MP_TOKEN_OP_MORE: op = MP_BINARY_OP_MORE; break;
+                case MP_TOKEN_OP_DBL_EQUAL: op = MP_BINARY_OP_EQUAL; break;
+                case MP_TOKEN_OP_LESS_EQUAL: op = MP_BINARY_OP_LESS_EQUAL; break;
+                case MP_TOKEN_OP_MORE_EQUAL: op = MP_BINARY_OP_MORE_EQUAL; break;
+                case MP_TOKEN_OP_NOT_EQUAL: op = MP_BINARY_OP_NOT_EQUAL; break;
+                case MP_TOKEN_KW_IN: default: op = MP_BINARY_OP_IN; break;
+            }
+            EMIT_ARG(binary_op, op);
+        } else {
+            assert(MP_PARSE_NODE_IS_STRUCT(pns->nodes[i])); // should be
+            mp_parse_node_struct_t *pns2 = (mp_parse_node_struct_t*)pns->nodes[i];
+            int kind = MP_PARSE_NODE_STRUCT_KIND(pns2);
+            if (kind == PN_comp_op_not_in) {
+                EMIT_ARG(binary_op, MP_BINARY_OP_NOT_IN);
+            } else {
+                assert(kind == PN_comp_op_is); // should be
+                if (MP_PARSE_NODE_IS_NULL(pns2->nodes[0])) {
+                    EMIT_ARG(binary_op, MP_BINARY_OP_IS);
+                } else {
+                    EMIT_ARG(binary_op, MP_BINARY_OP_IS_NOT);
+                }
+            }
+        }
+        if (i + 2 < num_nodes) {
+            EMIT_ARG(jump_if_or_pop, false, l_fail);
+        }
+    }
+    if (multi) {
+        uint l_end = comp_next_label(comp);
+        EMIT_ARG(jump, l_end);
+        EMIT_ARG(label_assign, l_fail);
+        EMIT_ARG(adjust_stack_size, 1);
+        EMIT(rot_two);
+        EMIT(pop_top);
+        EMIT_ARG(label_assign, l_end);
+    }
+}
+
+STATIC void compile_star_expr(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    compile_syntax_error(comp, (mp_parse_node_t)pns, "*x must be assignment target");
+}
+
+STATIC void compile_expr(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    c_binary_op(comp, pns, MP_BINARY_OP_OR);
+}
+
+STATIC void compile_xor_expr(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    c_binary_op(comp, pns, MP_BINARY_OP_XOR);
+}
+
+STATIC void compile_and_expr(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    c_binary_op(comp, pns, MP_BINARY_OP_AND);
+}
+
+STATIC void compile_term(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
+    compile_node(comp, pns->nodes[0]);
+    for (int i = 1; i + 1 < num_nodes; i += 2) {
+        compile_node(comp, pns->nodes[i + 1]);
+        mp_binary_op_t op;
+        mp_token_kind_t tok = MP_PARSE_NODE_LEAF_ARG(pns->nodes[i]);
+        switch (tok) {
+            case MP_TOKEN_OP_PLUS:      op = MP_BINARY_OP_ADD; break;
+            case MP_TOKEN_OP_MINUS:     op = MP_BINARY_OP_SUBTRACT; break;
+            case MP_TOKEN_OP_STAR:      op = MP_BINARY_OP_MULTIPLY; break;
+            case MP_TOKEN_OP_DBL_SLASH: op = MP_BINARY_OP_FLOOR_DIVIDE; break;
+            case MP_TOKEN_OP_SLASH:     op = MP_BINARY_OP_TRUE_DIVIDE; break;
+            case MP_TOKEN_OP_PERCENT:   op = MP_BINARY_OP_MODULO; break;
+            case MP_TOKEN_OP_DBL_LESS:  op = MP_BINARY_OP_LSHIFT; break;
+            default:
+                assert(tok == MP_TOKEN_OP_DBL_MORE);
+                op = MP_BINARY_OP_RSHIFT;
+                break;
+        }
+        EMIT_ARG(binary_op, op);
+    }
+}
+
+STATIC void compile_factor_2(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    compile_node(comp, pns->nodes[1]);
+    mp_unary_op_t op;
+    mp_token_kind_t tok = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
+    switch (tok) {
+        case MP_TOKEN_OP_PLUS:  op = MP_UNARY_OP_POSITIVE; break;
+        case MP_TOKEN_OP_MINUS: op = MP_UNARY_OP_NEGATIVE; break;
+        default:
+            assert(tok == MP_TOKEN_OP_TILDE);
+            op = MP_UNARY_OP_INVERT;
+            break;
+    }
+    EMIT_ARG(unary_op, op);
+}
+
+STATIC void compile_atom_expr_normal(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    // compile the subject of the expression
+    compile_node(comp, pns->nodes[0]);
+
+    // compile_atom_expr_await may call us with a NULL node
+    if (MP_PARSE_NODE_IS_NULL(pns->nodes[1])) {
+        return;
+    }
+
+    // get the array of trailers (known to be an array of PARSE_NODE_STRUCT)
+    size_t num_trail = 1;
+    mp_parse_node_struct_t **pns_trail = (mp_parse_node_struct_t**)&pns->nodes[1];
+    if (MP_PARSE_NODE_STRUCT_KIND(pns_trail[0]) == PN_atom_expr_trailers) {
+        num_trail = MP_PARSE_NODE_STRUCT_NUM_NODES(pns_trail[0]);
+        pns_trail = (mp_parse_node_struct_t**)&pns_trail[0]->nodes[0];
+    }
+
+    // the current index into the array of trailers
+    size_t i = 0;
+
+    // handle special super() call
+    if (comp->scope_cur->kind == SCOPE_FUNCTION
+        && MP_PARSE_NODE_IS_ID(pns->nodes[0])
+        && MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]) == MP_QSTR_super
+        && MP_PARSE_NODE_STRUCT_KIND(pns_trail[0]) == PN_trailer_paren
+        && MP_PARSE_NODE_IS_NULL(pns_trail[0]->nodes[0])) {
+        // at this point we have matched "super()" within a function
+
+        // load the class for super to search for a parent
+        compile_load_id(comp, MP_QSTR___class__);
+
+        // look for first argument to function (assumes it's "self")
+        bool found = false;
+        id_info_t *id = &comp->scope_cur->id_info[0];
+        for (size_t n = comp->scope_cur->id_info_len; n > 0; --n, ++id) {
+            if (id->flags & ID_FLAG_IS_PARAM) {
+                // first argument found; load it
+                compile_load_id(comp, id->qst);
+                found = true;
+                break;
+            }
+        }
+        if (!found) {
+            compile_syntax_error(comp, (mp_parse_node_t)pns_trail[0],
+                "super() can't find self"); // really a TypeError
+            return;
+        }
+
+        if (num_trail >= 3
+            && MP_PARSE_NODE_STRUCT_KIND(pns_trail[1]) == PN_trailer_period
+            && MP_PARSE_NODE_STRUCT_KIND(pns_trail[2]) == PN_trailer_paren) {
+            // optimisation for method calls super().f(...), to eliminate heap allocation
+            mp_parse_node_struct_t *pns_period = pns_trail[1];
+            mp_parse_node_struct_t *pns_paren = pns_trail[2];
+            EMIT_ARG(load_method, MP_PARSE_NODE_LEAF_ARG(pns_period->nodes[0]), true);
+            compile_trailer_paren_helper(comp, pns_paren->nodes[0], true, 0);
+            i = 3;
+        } else {
+            // a super() call
+            EMIT_ARG(call_function, 2, 0, 0);
+            i = 1;
+        }
+    }
+
+    // compile the remaining trailers
+    for (; i < num_trail; i++) {
+        if (i + 1 < num_trail
+            && MP_PARSE_NODE_STRUCT_KIND(pns_trail[i]) == PN_trailer_period
+            && MP_PARSE_NODE_STRUCT_KIND(pns_trail[i + 1]) == PN_trailer_paren) {
+            // optimisation for method calls a.f(...), following PyPy
+            mp_parse_node_struct_t *pns_period = pns_trail[i];
+            mp_parse_node_struct_t *pns_paren = pns_trail[i + 1];
+            EMIT_ARG(load_method, MP_PARSE_NODE_LEAF_ARG(pns_period->nodes[0]), false);
+            compile_trailer_paren_helper(comp, pns_paren->nodes[0], true, 0);
+            i += 1;
+        } else {
+            // node is one of: trailer_paren, trailer_bracket, trailer_period
+            compile_node(comp, (mp_parse_node_t)pns_trail[i]);
+        }
+    }
+}
+
+STATIC void compile_power(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    compile_generic_all_nodes(comp, pns); // 2 nodes, arguments of power
+    EMIT_ARG(binary_op, MP_BINARY_OP_POWER);
+}
+
+STATIC void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_arglist, bool is_method_call, int n_positional_extra) {
+    // function to call is on top of stack
+
+    // get the list of arguments
+    mp_parse_node_t *args;
+    int n_args = mp_parse_node_extract_list(&pn_arglist, PN_arglist, &args);
+
+    // compile the arguments
+    // Rather than calling compile_node on the list, we go through the list of args
+    // explicitly here so that we can count the number of arguments and give sensible
+    // error messages.
+    int n_positional = n_positional_extra;
+    uint n_keyword = 0;
+    uint star_flags = 0;
+    mp_parse_node_struct_t *star_args_node = NULL, *dblstar_args_node = NULL;
+    for (int i = 0; i < n_args; i++) {
+        if (MP_PARSE_NODE_IS_STRUCT(args[i])) {
+            mp_parse_node_struct_t *pns_arg = (mp_parse_node_struct_t*)args[i];
+            if (MP_PARSE_NODE_STRUCT_KIND(pns_arg) == PN_arglist_star) {
+                if (star_flags & MP_EMIT_STAR_FLAG_SINGLE) {
+                    compile_syntax_error(comp, (mp_parse_node_t)pns_arg, "can't have multiple *x");
+                    return;
+                }
+                star_flags |= MP_EMIT_STAR_FLAG_SINGLE;
+                star_args_node = pns_arg;
+            } else if (MP_PARSE_NODE_STRUCT_KIND(pns_arg) == PN_arglist_dbl_star) {
+                if (star_flags & MP_EMIT_STAR_FLAG_DOUBLE) {
+                    compile_syntax_error(comp, (mp_parse_node_t)pns_arg, "can't have multiple **x");
+                    return;
+                }
+                star_flags |= MP_EMIT_STAR_FLAG_DOUBLE;
+                dblstar_args_node = pns_arg;
+            } else if (MP_PARSE_NODE_STRUCT_KIND(pns_arg) == PN_argument) {
+                if (!MP_PARSE_NODE_IS_STRUCT_KIND(pns_arg->nodes[1], PN_comp_for)) {
+                    if (!MP_PARSE_NODE_IS_ID(pns_arg->nodes[0])) {
+                        compile_syntax_error(comp, (mp_parse_node_t)pns_arg, "LHS of keyword arg must be an id");
+                        return;
+                    }
+                    EMIT_ARG(load_const_str, MP_PARSE_NODE_LEAF_ARG(pns_arg->nodes[0]));
+                    compile_node(comp, pns_arg->nodes[1]);
+                    n_keyword += 1;
+                } else {
+                    compile_comprehension(comp, pns_arg, SCOPE_GEN_EXPR);
+                    n_positional++;
+                }
+            } else {
+                goto normal_argument;
+            }
+        } else {
+            normal_argument:
+            if (star_flags) {
+                compile_syntax_error(comp, args[i], "non-keyword arg after */**");
+                return;
+            }
+            if (n_keyword > 0) {
+                compile_syntax_error(comp, args[i], "non-keyword arg after keyword arg");
+                return;
+            }
+            compile_node(comp, args[i]);
+            n_positional++;
+        }
+    }
+
+    // compile the star/double-star arguments if we had them
+    // if we had one but not the other then we load "null" as a place holder
+    if (star_flags != 0) {
+        if (star_args_node == NULL) {
+            EMIT(load_null);
+        } else {
+            compile_node(comp, star_args_node->nodes[0]);
+        }
+        if (dblstar_args_node == NULL) {
+            EMIT(load_null);
+        } else {
+            compile_node(comp, dblstar_args_node->nodes[0]);
+        }
+    }
+
+    // emit the function/method call
+    if (is_method_call) {
+        EMIT_ARG(call_method, n_positional, n_keyword, star_flags);
+    } else {
+        EMIT_ARG(call_function, n_positional, n_keyword, star_flags);
+    }
+}
+
+// pns needs to have 2 nodes, first is lhs of comprehension, second is PN_comp_for node
+STATIC void compile_comprehension(compiler_t *comp, mp_parse_node_struct_t *pns, scope_kind_t kind) {
+    assert(MP_PARSE_NODE_STRUCT_NUM_NODES(pns) == 2);
+    assert(MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_comp_for));
+    mp_parse_node_struct_t *pns_comp_for = (mp_parse_node_struct_t*)pns->nodes[1];
+
+    if (comp->pass == MP_PASS_SCOPE) {
+        // create a new scope for this comprehension
+        scope_t *s = scope_new_and_link(comp, kind, (mp_parse_node_t)pns, comp->scope_cur->emit_options);
+        // store the comprehension scope so the compiling function (this one) can use it at each pass
+        pns_comp_for->nodes[3] = (mp_parse_node_t)s;
+    }
+
+    // get the scope for this comprehension
+    scope_t *this_scope = (scope_t*)pns_comp_for->nodes[3];
+
+    // compile the comprehension
+    close_over_variables_etc(comp, this_scope, 0, 0);
+
+    compile_node(comp, pns_comp_for->nodes[1]); // source of the iterator
+    if (kind == SCOPE_GEN_EXPR) {
+        EMIT_ARG(get_iter, false);
+    }
+    EMIT_ARG(call_function, 1, 0, 0);
+}
+
+STATIC void compile_atom_paren(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
+        // an empty tuple
+        c_tuple(comp, MP_PARSE_NODE_NULL, NULL);
+    } else {
+        assert(MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp));
+        pns = (mp_parse_node_struct_t*)pns->nodes[0];
+        assert(!MP_PARSE_NODE_IS_NULL(pns->nodes[1]));
+        if (MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])) {
+            mp_parse_node_struct_t *pns2 = (mp_parse_node_struct_t*)pns->nodes[1];
+            if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_testlist_comp_3b) {
+                // tuple of one item, with trailing comma
+                assert(MP_PARSE_NODE_IS_NULL(pns2->nodes[0]));
+                c_tuple(comp, pns->nodes[0], NULL);
+            } else if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_testlist_comp_3c) {
+                // tuple of many items
+                c_tuple(comp, pns->nodes[0], pns2);
+            } else if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_comp_for) {
+                // generator expression
+                compile_comprehension(comp, pns, SCOPE_GEN_EXPR);
+            } else {
+                // tuple with 2 items
+                goto tuple_with_2_items;
+            }
+        } else {
+            // tuple with 2 items
+            tuple_with_2_items:
+            c_tuple(comp, MP_PARSE_NODE_NULL, pns);
+        }
+    }
+}
+
+STATIC void compile_atom_bracket(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
+        // empty list
+        EMIT_ARG(build_list, 0);
+    } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp)) {
+        mp_parse_node_struct_t *pns2 = (mp_parse_node_struct_t*)pns->nodes[0];
+        if (MP_PARSE_NODE_IS_STRUCT(pns2->nodes[1])) {
+            mp_parse_node_struct_t *pns3 = (mp_parse_node_struct_t*)pns2->nodes[1];
+            if (MP_PARSE_NODE_STRUCT_KIND(pns3) == PN_testlist_comp_3b) {
+                // list of one item, with trailing comma
+                assert(MP_PARSE_NODE_IS_NULL(pns3->nodes[0]));
+                compile_node(comp, pns2->nodes[0]);
+                EMIT_ARG(build_list, 1);
+            } else if (MP_PARSE_NODE_STRUCT_KIND(pns3) == PN_testlist_comp_3c) {
+                // list of many items
+                compile_node(comp, pns2->nodes[0]);
+                compile_generic_all_nodes(comp, pns3);
+                EMIT_ARG(build_list, 1 + MP_PARSE_NODE_STRUCT_NUM_NODES(pns3));
+            } else if (MP_PARSE_NODE_STRUCT_KIND(pns3) == PN_comp_for) {
+                // list comprehension
+                compile_comprehension(comp, pns2, SCOPE_LIST_COMP);
+            } else {
+                // list with 2 items
+                goto list_with_2_items;
+            }
+        } else {
+            // list with 2 items
+            list_with_2_items:
+            compile_node(comp, pns2->nodes[0]);
+            compile_node(comp, pns2->nodes[1]);
+            EMIT_ARG(build_list, 2);
+        }
+    } else {
+        // list with 1 item
+        compile_node(comp, pns->nodes[0]);
+        EMIT_ARG(build_list, 1);
+    }
+}
+
+STATIC void compile_atom_brace(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    mp_parse_node_t pn = pns->nodes[0];
+    if (MP_PARSE_NODE_IS_NULL(pn)) {
+        // empty dict
+        EMIT_ARG(build_map, 0);
+    } else if (MP_PARSE_NODE_IS_STRUCT(pn)) {
+        pns = (mp_parse_node_struct_t*)pn;
+        if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_dictorsetmaker_item) {
+            // dict with one element
+            EMIT_ARG(build_map, 1);
+            compile_node(comp, pn);
+            EMIT(store_map);
+        } else if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_dictorsetmaker) {
+            assert(MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])); // should succeed
+            mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t*)pns->nodes[1];
+            if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_dictorsetmaker_list) {
+                // dict/set with multiple elements
+
+                // get tail elements (2nd, 3rd, ...)
+                mp_parse_node_t *nodes;
+                int n = mp_parse_node_extract_list(&pns1->nodes[0], PN_dictorsetmaker_list2, &nodes);
+
+                // first element sets whether it's a dict or set
+                bool is_dict;
+                if (!MICROPY_PY_BUILTINS_SET || MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_dictorsetmaker_item)) {
+                    // a dictionary
+                    EMIT_ARG(build_map, 1 + n);
+                    compile_node(comp, pns->nodes[0]);
+                    EMIT(store_map);
+                    is_dict = true;
+                } else {
+                    // a set
+                    compile_node(comp, pns->nodes[0]); // 1st value of set
+                    is_dict = false;
+                }
+
+                // process rest of elements
+                for (int i = 0; i < n; i++) {
+                    mp_parse_node_t pn_i = nodes[i];
+                    bool is_key_value = MP_PARSE_NODE_IS_STRUCT_KIND(pn_i, PN_dictorsetmaker_item);
+                    compile_node(comp, pn_i);
+                    if (is_dict) {
+                        if (!is_key_value) {
+                            if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) {
+                                compile_syntax_error(comp, (mp_parse_node_t)pns, "invalid syntax");
+                            } else {
+                                compile_syntax_error(comp, (mp_parse_node_t)pns, "expecting key:value for dict");
+                            }
+                            return;
+                        }
+                        EMIT(store_map);
+                    } else {
+                        if (is_key_value) {
+                            if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) {
+                                compile_syntax_error(comp, (mp_parse_node_t)pns, "invalid syntax");
+                            } else {
+                                compile_syntax_error(comp, (mp_parse_node_t)pns, "expecting just a value for set");
+                            }
+                            return;
+                        }
+                    }
+                }
+
+                #if MICROPY_PY_BUILTINS_SET
+                // if it's a set, build it
+                if (!is_dict) {
+                    EMIT_ARG(build_set, 1 + n);
+                }
+                #endif
+            } else {
+                assert(MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_comp_for); // should be
+                // dict/set comprehension
+                if (!MICROPY_PY_BUILTINS_SET || MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_dictorsetmaker_item)) {
+                    // a dictionary comprehension
+                    compile_comprehension(comp, pns, SCOPE_DICT_COMP);
+                } else {
+                    // a set comprehension
+                    compile_comprehension(comp, pns, SCOPE_SET_COMP);
+                }
+            }
+        } else {
+            // set with one element
+            goto set_with_one_element;
+        }
+    } else {
+        // set with one element
+        set_with_one_element:
+        #if MICROPY_PY_BUILTINS_SET
+        compile_node(comp, pn);
+        EMIT_ARG(build_set, 1);
+        #else
+        assert(0);
+        #endif
+    }
+}
+
+STATIC void compile_trailer_paren(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    compile_trailer_paren_helper(comp, pns->nodes[0], false, 0);
+}
+
+STATIC void compile_trailer_bracket(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    // object who's index we want is on top of stack
+    compile_node(comp, pns->nodes[0]); // the index
+    EMIT(load_subscr);
+}
+
+STATIC void compile_trailer_period(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    // object who's attribute we want is on top of stack
+    EMIT_ARG(load_attr, MP_PARSE_NODE_LEAF_ARG(pns->nodes[0])); // attribute to get
+}
+
+#if MICROPY_PY_BUILTINS_SLICE
+STATIC void compile_subscript_3_helper(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    assert(MP_PARSE_NODE_STRUCT_KIND(pns) == PN_subscript_3); // should always be
+    mp_parse_node_t pn = pns->nodes[0];
+    if (MP_PARSE_NODE_IS_NULL(pn)) {
+        // [?:]
+        EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE);
+        EMIT_ARG(build_slice, 2);
+    } else if (MP_PARSE_NODE_IS_STRUCT(pn)) {
+        pns = (mp_parse_node_struct_t*)pn;
+        if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_subscript_3c) {
+            EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE);
+            pn = pns->nodes[0];
+            if (MP_PARSE_NODE_IS_NULL(pn)) {
+                // [?::]
+                EMIT_ARG(build_slice, 2);
+            } else {
+                // [?::x]
+                compile_node(comp, pn);
+                EMIT_ARG(build_slice, 3);
+            }
+        } else if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_subscript_3d) {
+            compile_node(comp, pns->nodes[0]);
+            assert(MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])); // should always be
+            pns = (mp_parse_node_struct_t*)pns->nodes[1];
+            assert(MP_PARSE_NODE_STRUCT_KIND(pns) == PN_sliceop); // should always be
+            if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
+                // [?:x:]
+                EMIT_ARG(build_slice, 2);
+            } else {
+                // [?:x:x]
+                compile_node(comp, pns->nodes[0]);
+                EMIT_ARG(build_slice, 3);
+            }
+        } else {
+            // [?:x]
+            compile_node(comp, pn);
+            EMIT_ARG(build_slice, 2);
+        }
+    } else {
+        // [?:x]
+        compile_node(comp, pn);
+        EMIT_ARG(build_slice, 2);
+    }
+}
+
+STATIC void compile_subscript_2(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    compile_node(comp, pns->nodes[0]); // start of slice
+    assert(MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])); // should always be
+    compile_subscript_3_helper(comp, (mp_parse_node_struct_t*)pns->nodes[1]);
+}
+
+STATIC void compile_subscript_3(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE);
+    compile_subscript_3_helper(comp, pns);
+}
+#endif // MICROPY_PY_BUILTINS_SLICE
+
+STATIC void compile_dictorsetmaker_item(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    // if this is called then we are compiling a dict key:value pair
+    compile_node(comp, pns->nodes[1]); // value
+    compile_node(comp, pns->nodes[0]); // key
+}
+
+STATIC void compile_classdef(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    qstr cname = compile_classdef_helper(comp, pns, comp->scope_cur->emit_options);
+    // store class object into class name
+    compile_store_id(comp, cname);
+}
+
+STATIC void compile_yield_expr(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    if (comp->scope_cur->kind != SCOPE_FUNCTION && comp->scope_cur->kind != SCOPE_LAMBDA) {
+        compile_syntax_error(comp, (mp_parse_node_t)pns, "'yield' outside function");
+        return;
+    }
+    if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
+        EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE);
+        EMIT(yield_value);
+    } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_yield_arg_from)) {
+        pns = (mp_parse_node_struct_t*)pns->nodes[0];
+        compile_node(comp, pns->nodes[0]);
+        compile_yield_from(comp);
+    } else {
+        compile_node(comp, pns->nodes[0]);
+        EMIT(yield_value);
+    }
+}
+
+#if MICROPY_PY_ASYNC_AWAIT
+STATIC void compile_atom_expr_await(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    if (comp->scope_cur->kind != SCOPE_FUNCTION && comp->scope_cur->kind != SCOPE_LAMBDA) {
+        compile_syntax_error(comp, (mp_parse_node_t)pns, "'await' outside function");
+        return;
+    }
+    compile_atom_expr_normal(comp, pns);
+    compile_yield_from(comp);
+}
+#endif
+
+STATIC mp_obj_t get_const_object(mp_parse_node_struct_t *pns) {
+    #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D
+    // nodes are 32-bit pointers, but need to extract 64-bit object
+    return (uint64_t)pns->nodes[0] | ((uint64_t)pns->nodes[1] << 32);
+    #else
+    return (mp_obj_t)pns->nodes[0];
+    #endif
+}
+
+STATIC void compile_const_object(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    EMIT_ARG(load_const_obj, get_const_object(pns));
+}
+
+typedef void (*compile_function_t)(compiler_t*, mp_parse_node_struct_t*);
+STATIC const compile_function_t compile_function[] = {
+// only define rules with a compile function
+#define c(f) compile_##f
+#define DEF_RULE(rule, comp, kind, ...) comp,
+#define DEF_RULE_NC(rule, kind, ...)
+#include "py/grammar.h"
+#undef c
+#undef DEF_RULE
+#undef DEF_RULE_NC
+    compile_const_object,
+};
+
+STATIC void compile_node(compiler_t *comp, mp_parse_node_t pn) {
+    if (MP_PARSE_NODE_IS_NULL(pn)) {
+        // pass
+    } else if (MP_PARSE_NODE_IS_SMALL_INT(pn)) {
+        mp_int_t arg = MP_PARSE_NODE_LEAF_SMALL_INT(pn);
+        #if MICROPY_DYNAMIC_COMPILER
+        mp_uint_t sign_mask = -(1 << (mp_dynamic_compiler.small_int_bits - 1));
+        if ((arg & sign_mask) == 0 || (arg & sign_mask) == sign_mask) {
+            // integer fits in target runtime's small-int
+            EMIT_ARG(load_const_small_int, arg);
+        } else {
+            // integer doesn't fit, so create a multi-precision int object
+            // (but only create the actual object on the last pass)
+            if (comp->pass != MP_PASS_EMIT) {
+                EMIT_ARG(load_const_obj, mp_const_none);
+            } else {
+                EMIT_ARG(load_const_obj, mp_obj_new_int_from_ll(arg));
+            }
+        }
+        #else
+        EMIT_ARG(load_const_small_int, arg);
+        #endif
+    } else if (MP_PARSE_NODE_IS_LEAF(pn)) {
+        uintptr_t arg = MP_PARSE_NODE_LEAF_ARG(pn);
+        switch (MP_PARSE_NODE_LEAF_KIND(pn)) {
+            case MP_PARSE_NODE_ID: compile_load_id(comp, arg); break;
+            case MP_PARSE_NODE_STRING: EMIT_ARG(load_const_str, arg); break;
+            case MP_PARSE_NODE_BYTES:
+                // only create and load the actual bytes object on the last pass
+                if (comp->pass != MP_PASS_EMIT) {
+                    EMIT_ARG(load_const_obj, mp_const_none);
+                } else {
+                    size_t len;
+                    const byte *data = qstr_data(arg, &len);
+                    EMIT_ARG(load_const_obj, mp_obj_new_bytes(data, len));
+                }
+                break;
+            case MP_PARSE_NODE_TOKEN: default:
+                if (arg == MP_TOKEN_NEWLINE) {
+                    // this can occur when file_input lets through a NEWLINE (eg if file starts with a newline)
+                    // or when single_input lets through a NEWLINE (user enters a blank line)
+                    // do nothing
+                } else {
+                  EMIT_ARG(load_const_tok, arg);
+                }
+                break;
+        }
+    } else {
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
+        EMIT_ARG(set_source_line, pns->source_line);
+        assert(MP_PARSE_NODE_STRUCT_KIND(pns) <= PN_const_object);
+        compile_function_t f = compile_function[MP_PARSE_NODE_STRUCT_KIND(pns)];
+        f(comp, pns);
+    }
+}
+
+STATIC void compile_scope_func_lambda_param(compiler_t *comp, mp_parse_node_t pn, pn_kind_t pn_name, pn_kind_t pn_star, pn_kind_t pn_dbl_star) {
+    // check that **kw is last
+    if ((comp->scope_cur->scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0) {
+        compile_syntax_error(comp, pn, "invalid syntax");
+        return;
+    }
+
+    qstr param_name = MP_QSTR_NULL;
+    uint param_flag = ID_FLAG_IS_PARAM;
+    if (MP_PARSE_NODE_IS_ID(pn)) {
+        param_name = MP_PARSE_NODE_LEAF_ARG(pn);
+        if (comp->have_star) {
+            // comes after a star, so counts as a keyword-only parameter
+            comp->scope_cur->num_kwonly_args += 1;
+        } else {
+            // comes before a star, so counts as a positional parameter
+            comp->scope_cur->num_pos_args += 1;
+        }
+    } else {
+        assert(MP_PARSE_NODE_IS_STRUCT(pn));
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
+        if (MP_PARSE_NODE_STRUCT_KIND(pns) == pn_name) {
+            param_name = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
+            if (comp->have_star) {
+                // comes after a star, so counts as a keyword-only parameter
+                comp->scope_cur->num_kwonly_args += 1;
+            } else {
+                // comes before a star, so counts as a positional parameter
+                comp->scope_cur->num_pos_args += 1;
+            }
+        } else if (MP_PARSE_NODE_STRUCT_KIND(pns) == pn_star) {
+            if (comp->have_star) {
+                // more than one star
+                compile_syntax_error(comp, pn, "invalid syntax");
+                return;
+            }
+            comp->have_star = true;
+            param_flag = ID_FLAG_IS_PARAM | ID_FLAG_IS_STAR_PARAM;
+            if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
+                // bare star
+                // TODO see http://www.python.org/dev/peps/pep-3102/
+                //assert(comp->scope_cur->num_dict_params == 0);
+            } else if (MP_PARSE_NODE_IS_ID(pns->nodes[0])) {
+                // named star
+                comp->scope_cur->scope_flags |= MP_SCOPE_FLAG_VARARGS;
+                param_name = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
+            } else {
+                assert(MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_tfpdef)); // should be
+                // named star with possible annotation
+                comp->scope_cur->scope_flags |= MP_SCOPE_FLAG_VARARGS;
+                pns = (mp_parse_node_struct_t*)pns->nodes[0];
+                param_name = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
+            }
+        } else {
+            assert(MP_PARSE_NODE_STRUCT_KIND(pns) == pn_dbl_star); // should be
+            param_name = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
+            param_flag = ID_FLAG_IS_PARAM | ID_FLAG_IS_DBL_STAR_PARAM;
+            comp->scope_cur->scope_flags |= MP_SCOPE_FLAG_VARKEYWORDS;
+        }
+    }
+
+    if (param_name != MP_QSTR_NULL) {
+        bool added;
+        id_info_t *id_info = scope_find_or_add_id(comp->scope_cur, param_name, &added);
+        if (!added) {
+            compile_syntax_error(comp, pn, "name reused for argument");
+            return;
+        }
+        id_info->kind = ID_INFO_KIND_LOCAL;
+        id_info->flags = param_flag;
+    }
+}
+
+STATIC void compile_scope_func_param(compiler_t *comp, mp_parse_node_t pn) {
+    compile_scope_func_lambda_param(comp, pn, PN_typedargslist_name, PN_typedargslist_star, PN_typedargslist_dbl_star);
+}
+
+STATIC void compile_scope_lambda_param(compiler_t *comp, mp_parse_node_t pn) {
+    compile_scope_func_lambda_param(comp, pn, PN_varargslist_name, PN_varargslist_star, PN_varargslist_dbl_star);
+}
+
+#if MICROPY_EMIT_NATIVE
+STATIC void compile_scope_func_annotations(compiler_t *comp, mp_parse_node_t pn) {
+    if (!MP_PARSE_NODE_IS_STRUCT(pn)) {
+        // no annotation
+        return;
+    }
+
+    mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
+    if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_typedargslist_name) {
+        // named parameter with possible annotation
+        // fallthrough
+    } else if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_typedargslist_star) {
+        if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_tfpdef)) {
+            // named star with possible annotation
+            pns = (mp_parse_node_struct_t*)pns->nodes[0];
+            // fallthrough
+        } else {
+            // no annotation
+            return;
+        }
+    } else {
+        assert(MP_PARSE_NODE_STRUCT_KIND(pns) == PN_typedargslist_dbl_star);
+        // double star with possible annotation
+        // fallthrough
+    }
+
+    mp_parse_node_t pn_annotation = pns->nodes[1];
+
+    if (!MP_PARSE_NODE_IS_NULL(pn_annotation)) {
+        qstr param_name = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
+        id_info_t *id_info = scope_find(comp->scope_cur, param_name);
+        assert(id_info != NULL);
+
+        if (MP_PARSE_NODE_IS_ID(pn_annotation)) {
+            qstr arg_type = MP_PARSE_NODE_LEAF_ARG(pn_annotation);
+            EMIT_ARG(set_native_type, MP_EMIT_NATIVE_TYPE_ARG, id_info->local_num, arg_type);
+        } else {
+            compile_syntax_error(comp, pn_annotation, "parameter annotation must be an identifier");
+        }
+    }
+}
+#endif // MICROPY_EMIT_NATIVE
+
+STATIC void compile_scope_comp_iter(compiler_t *comp, mp_parse_node_struct_t *pns_comp_for, mp_parse_node_t pn_inner_expr, int for_depth) {
+    uint l_top = comp_next_label(comp);
+    uint l_end = comp_next_label(comp);
+    EMIT_ARG(label_assign, l_top);
+    EMIT_ARG(for_iter, l_end);
+    c_assign(comp, pns_comp_for->nodes[0], ASSIGN_STORE);
+    mp_parse_node_t pn_iter = pns_comp_for->nodes[2];
+
+    tail_recursion:
+    if (MP_PARSE_NODE_IS_NULL(pn_iter)) {
+        // no more nested if/for; compile inner expression
+        compile_node(comp, pn_inner_expr);
+        if (comp->scope_cur->kind == SCOPE_GEN_EXPR) {
+            EMIT(yield_value);
+            EMIT(pop_top);
+        } else {
+            EMIT_ARG(store_comp, comp->scope_cur->kind, 4 * for_depth + 5);
+        }
+    } else if (MP_PARSE_NODE_STRUCT_KIND((mp_parse_node_struct_t*)pn_iter) == PN_comp_if) {
+        // if condition
+        mp_parse_node_struct_t *pns_comp_if = (mp_parse_node_struct_t*)pn_iter;
+        c_if_cond(comp, pns_comp_if->nodes[0], false, l_top);
+        pn_iter = pns_comp_if->nodes[1];
+        goto tail_recursion;
+    } else {
+        assert(MP_PARSE_NODE_STRUCT_KIND((mp_parse_node_struct_t*)pn_iter) == PN_comp_for); // should be
+        // for loop
+        mp_parse_node_struct_t *pns_comp_for2 = (mp_parse_node_struct_t*)pn_iter;
+        compile_node(comp, pns_comp_for2->nodes[1]);
+        EMIT_ARG(get_iter, true);
+        compile_scope_comp_iter(comp, pns_comp_for2, pn_inner_expr, for_depth + 1);
+    }
+
+    EMIT_ARG(jump, l_top);
+    EMIT_ARG(label_assign, l_end);
+    EMIT(for_iter_end);
+}
+
+STATIC void check_for_doc_string(compiler_t *comp, mp_parse_node_t pn) {
+#if MICROPY_ENABLE_DOC_STRING
+    // see http://www.python.org/dev/peps/pep-0257/
+
+    // look for the first statement
+    if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_expr_stmt)) {
+        // a statement; fall through
+    } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_file_input_2)) {
+        // file input; find the first non-newline node
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
+        int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
+        for (int i = 0; i < num_nodes; i++) {
+            pn = pns->nodes[i];
+            if (!(MP_PARSE_NODE_IS_LEAF(pn) && MP_PARSE_NODE_LEAF_KIND(pn) == MP_PARSE_NODE_TOKEN && MP_PARSE_NODE_LEAF_ARG(pn) == MP_TOKEN_NEWLINE)) {
+                // not a newline, so this is the first statement; finish search
+                break;
+            }
+        }
+        // if we didn't find a non-newline then it's okay to fall through; pn will be a newline and so doc-string test below will fail gracefully
+    } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_suite_block_stmts)) {
+        // a list of statements; get the first one
+        pn = ((mp_parse_node_struct_t*)pn)->nodes[0];
+    } else {
+        return;
+    }
+
+    // check the first statement for a doc string
+    if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_expr_stmt)) {
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
+        if ((MP_PARSE_NODE_IS_LEAF(pns->nodes[0])
+                && MP_PARSE_NODE_LEAF_KIND(pns->nodes[0]) == MP_PARSE_NODE_STRING)
+            || (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_const_object)
+                && MP_OBJ_IS_STR(get_const_object((mp_parse_node_struct_t*)pns->nodes[0])))) {
+                // compile the doc string
+                compile_node(comp, pns->nodes[0]);
+                // store the doc string
+                compile_store_id(comp, MP_QSTR___doc__);
+        }
+    }
+#else
+    (void)comp;
+    (void)pn;
+#endif
+}
+
+STATIC void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) {
+    comp->pass = pass;
+    comp->scope_cur = scope;
+    comp->next_label = 0;
+    EMIT_ARG(start_pass, pass, scope);
+
+    if (comp->pass == MP_PASS_SCOPE) {
+        // reset maximum stack sizes in scope
+        // they will be computed in this first pass
+        scope->stack_size = 0;
+        scope->exc_stack_size = 0;
+    }
+
+    // compile
+    if (MP_PARSE_NODE_IS_STRUCT_KIND(scope->pn, PN_eval_input)) {
+        assert(scope->kind == SCOPE_MODULE);
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)scope->pn;
+        compile_node(comp, pns->nodes[0]); // compile the expression
+        EMIT(return_value);
+    } else if (scope->kind == SCOPE_MODULE) {
+        if (!comp->is_repl) {
+            check_for_doc_string(comp, scope->pn);
+        }
+        compile_node(comp, scope->pn);
+        EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE);
+        EMIT(return_value);
+    } else if (scope->kind == SCOPE_FUNCTION) {
+        assert(MP_PARSE_NODE_IS_STRUCT(scope->pn));
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)scope->pn;
+        assert(MP_PARSE_NODE_STRUCT_KIND(pns) == PN_funcdef);
+
+        // work out number of parameters, keywords and default parameters, and add them to the id_info array
+        // must be done before compiling the body so that arguments are numbered first (for LOAD_FAST etc)
+        if (comp->pass == MP_PASS_SCOPE) {
+            comp->have_star = false;
+            apply_to_single_or_list(comp, pns->nodes[1], PN_typedargslist, compile_scope_func_param);
+        }
+        #if MICROPY_EMIT_NATIVE
+        else if (scope->emit_options == MP_EMIT_OPT_VIPER) {
+            // compile annotations; only needed on latter compiler passes
+            // only needed for viper emitter
+
+            // argument annotations
+            apply_to_single_or_list(comp, pns->nodes[1], PN_typedargslist, compile_scope_func_annotations);
+
+            // pns->nodes[2] is return/whole function annotation
+            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);
+                    EMIT_ARG(set_native_type, MP_EMIT_NATIVE_TYPE_RETURN, 0, ret_type);
+                } else {
+                    compile_syntax_error(comp, pn_annotation, "return annotation must be an identifier");
+                }
+            }
+        }
+        #endif // MICROPY_EMIT_NATIVE
+
+        compile_node(comp, pns->nodes[3]); // 3 is function body
+        // emit return if it wasn't the last opcode
+        if (!EMIT(last_emit_was_return_value)) {
+            EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE);
+            EMIT(return_value);
+        }
+    } else if (scope->kind == SCOPE_LAMBDA) {
+        assert(MP_PARSE_NODE_IS_STRUCT(scope->pn));
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)scope->pn;
+        assert(MP_PARSE_NODE_STRUCT_NUM_NODES(pns) == 3);
+
+        // work out number of parameters, keywords and default parameters, and add them to the id_info array
+        // must be done before compiling the body so that arguments are numbered first (for LOAD_FAST etc)
+        if (comp->pass == MP_PASS_SCOPE) {
+            comp->have_star = false;
+            apply_to_single_or_list(comp, pns->nodes[0], PN_varargslist, compile_scope_lambda_param);
+        }
+
+        compile_node(comp, pns->nodes[1]); // 1 is lambda body
+
+        // if the lambda is a generator, then we return None, not the result of the expression of the lambda
+        if (scope->scope_flags & MP_SCOPE_FLAG_GENERATOR) {
+            EMIT(pop_top);
+            EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE);
+        }
+        EMIT(return_value);
+    } else if (scope->kind == SCOPE_LIST_COMP || scope->kind == SCOPE_DICT_COMP || scope->kind == SCOPE_SET_COMP || scope->kind == SCOPE_GEN_EXPR) {
+        // a bit of a hack at the moment
+
+        assert(MP_PARSE_NODE_IS_STRUCT(scope->pn));
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)scope->pn;
+        assert(MP_PARSE_NODE_STRUCT_NUM_NODES(pns) == 2);
+        assert(MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_comp_for));
+        mp_parse_node_struct_t *pns_comp_for = (mp_parse_node_struct_t*)pns->nodes[1];
+
+        // We need a unique name for the comprehension argument (the iterator).
+        // CPython uses .0, but we should be able to use anything that won't
+        // clash with a user defined variable.  Best to use an existing qstr,
+        // so we use the blank qstr.
+        qstr qstr_arg = MP_QSTR_;
+        if (comp->pass == MP_PASS_SCOPE) {
+            bool added;
+            id_info_t *id_info = scope_find_or_add_id(comp->scope_cur, qstr_arg, &added);
+            assert(added);
+            id_info->kind = ID_INFO_KIND_LOCAL;
+            scope->num_pos_args = 1;
+        }
+
+        if (scope->kind == SCOPE_LIST_COMP) {
+            EMIT_ARG(build_list, 0);
+        } else if (scope->kind == SCOPE_DICT_COMP) {
+            EMIT_ARG(build_map, 0);
+        #if MICROPY_PY_BUILTINS_SET
+        } else if (scope->kind == SCOPE_SET_COMP) {
+            EMIT_ARG(build_set, 0);
+        #endif
+        }
+
+        // There are 4 slots on the stack for the iterator, and the first one is
+        // NULL to indicate that the second one points to the iterator object.
+        if (scope->kind == SCOPE_GEN_EXPR) {
+            // TODO static assert that MP_OBJ_ITER_BUF_NSLOTS == 4
+            EMIT(load_null);
+            compile_load_id(comp, qstr_arg);
+            EMIT(load_null);
+            EMIT(load_null);
+        } else {
+            compile_load_id(comp, qstr_arg);
+            EMIT_ARG(get_iter, true);
+        }
+
+        compile_scope_comp_iter(comp, pns_comp_for, pns->nodes[0], 0);
+
+        if (scope->kind == SCOPE_GEN_EXPR) {
+            EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE);
+        }
+        EMIT(return_value);
+    } else {
+        assert(scope->kind == SCOPE_CLASS);
+        assert(MP_PARSE_NODE_IS_STRUCT(scope->pn));
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)scope->pn;
+        assert(MP_PARSE_NODE_STRUCT_KIND(pns) == PN_classdef);
+
+        if (comp->pass == MP_PASS_SCOPE) {
+            bool added;
+            id_info_t *id_info = scope_find_or_add_id(scope, MP_QSTR___class__, &added);
+            assert(added);
+            id_info->kind = ID_INFO_KIND_LOCAL;
+        }
+
+        compile_load_id(comp, MP_QSTR___name__);
+        compile_store_id(comp, MP_QSTR___module__);
+        EMIT_ARG(load_const_str, MP_PARSE_NODE_LEAF_ARG(pns->nodes[0])); // 0 is class name
+        compile_store_id(comp, MP_QSTR___qualname__);
+
+        check_for_doc_string(comp, pns->nodes[2]);
+        compile_node(comp, pns->nodes[2]); // 2 is class body
+
+        id_info_t *id = scope_find(scope, MP_QSTR___class__);
+        assert(id != NULL);
+        if (id->kind == ID_INFO_KIND_LOCAL) {
+            EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE);
+        } else {
+            EMIT_LOAD_FAST(MP_QSTR___class__, id->local_num);
+        }
+        EMIT(return_value);
+    }
+
+    EMIT(end_pass);
+
+    // make sure we match all the exception levels
+    assert(comp->cur_except_level == 0);
+}
+
+#if MICROPY_EMIT_INLINE_ASM
+// requires 3 passes: SCOPE, CODE_SIZE, EMIT
+STATIC void compile_scope_inline_asm(compiler_t *comp, scope_t *scope, pass_kind_t pass) {
+    comp->pass = pass;
+    comp->scope_cur = scope;
+    comp->next_label = 0;
+
+    if (scope->kind != SCOPE_FUNCTION) {
+        compile_syntax_error(comp, MP_PARSE_NODE_NULL, "inline assembler must be a function");
+        return;
+    }
+
+    if (comp->pass > MP_PASS_SCOPE) {
+        EMIT_INLINE_ASM_ARG(start_pass, comp->pass, &comp->compile_error);
+    }
+
+    // get the function definition parse node
+    assert(MP_PARSE_NODE_IS_STRUCT(scope->pn));
+    mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)scope->pn;
+    assert(MP_PARSE_NODE_STRUCT_KIND(pns) == PN_funcdef);
+
+    //qstr f_id = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]); // function name
+
+    // parameters are in pns->nodes[1]
+    if (comp->pass == MP_PASS_CODE_SIZE) {
+        mp_parse_node_t *pn_params;
+        int n_params = mp_parse_node_extract_list(&pns->nodes[1], PN_typedargslist, &pn_params);
+        scope->num_pos_args = EMIT_INLINE_ASM_ARG(count_params, n_params, pn_params);
+        if (comp->compile_error != MP_OBJ_NULL) {
+            goto inline_asm_error;
+        }
+    }
+
+    // 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;
+    int num = mp_parse_node_extract_list(&pn_body, PN_suite_block_stmts, &nodes);
+
+    for (int i = 0; i < num; i++) {
+        assert(MP_PARSE_NODE_IS_STRUCT(nodes[i]));
+        mp_parse_node_struct_t *pns2 = (mp_parse_node_struct_t*)nodes[i];
+        if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_pass_stmt) {
+            // no instructions
+            continue;
+        } else if (MP_PARSE_NODE_STRUCT_KIND(pns2) != PN_expr_stmt) {
+            // not an instruction; error
+        not_an_instruction:
+            compile_syntax_error(comp, nodes[i], "expecting an assembler instruction");
+            return;
+        }
+
+        // check structure of parse node
+        assert(MP_PARSE_NODE_IS_STRUCT(pns2->nodes[0]));
+        if (!MP_PARSE_NODE_IS_NULL(pns2->nodes[1])) {
+            goto not_an_instruction;
+        }
+        pns2 = (mp_parse_node_struct_t*)pns2->nodes[0];
+        if (MP_PARSE_NODE_STRUCT_KIND(pns2) != PN_atom_expr_normal) {
+            goto not_an_instruction;
+        }
+        if (!MP_PARSE_NODE_IS_ID(pns2->nodes[0])) {
+            goto not_an_instruction;
+        }
+        if (!MP_PARSE_NODE_IS_STRUCT_KIND(pns2->nodes[1], PN_trailer_paren)) {
+            goto not_an_instruction;
+        }
+
+        // parse node looks like an instruction
+        // get instruction name and args
+        qstr op = MP_PARSE_NODE_LEAF_ARG(pns2->nodes[0]);
+        pns2 = (mp_parse_node_struct_t*)pns2->nodes[1]; // PN_trailer_paren
+        mp_parse_node_t *pn_arg;
+        int n_args = mp_parse_node_extract_list(&pns2->nodes[0], PN_arglist, &pn_arg);
+
+        // emit instructions
+        if (op == MP_QSTR_label) {
+            if (!(n_args == 1 && MP_PARSE_NODE_IS_ID(pn_arg[0]))) {
+                compile_syntax_error(comp, nodes[i], "'label' requires 1 argument");
+                return;
+            }
+            uint lab = comp_next_label(comp);
+            if (pass > MP_PASS_SCOPE) {
+                if (!EMIT_INLINE_ASM_ARG(label, lab, MP_PARSE_NODE_LEAF_ARG(pn_arg[0]))) {
+                    compile_syntax_error(comp, nodes[i], "label redefined");
+                    return;
+                }
+            }
+        } else if (op == MP_QSTR_align) {
+            if (!(n_args == 1 && MP_PARSE_NODE_IS_SMALL_INT(pn_arg[0]))) {
+                compile_syntax_error(comp, nodes[i], "'align' requires 1 argument");
+                return;
+            }
+            if (pass > MP_PASS_SCOPE) {
+                mp_asm_base_align((mp_asm_base_t*)comp->emit_inline_asm,
+                    MP_PARSE_NODE_LEAF_SMALL_INT(pn_arg[0]));
+            }
+        } else if (op == MP_QSTR_data) {
+            if (!(n_args >= 2 && MP_PARSE_NODE_IS_SMALL_INT(pn_arg[0]))) {
+                compile_syntax_error(comp, nodes[i], "'data' requires at least 2 arguments");
+                return;
+            }
+            if (pass > MP_PASS_SCOPE) {
+                mp_int_t bytesize = MP_PARSE_NODE_LEAF_SMALL_INT(pn_arg[0]);
+                for (uint j = 1; j < n_args; j++) {
+                    if (!MP_PARSE_NODE_IS_SMALL_INT(pn_arg[j])) {
+                        compile_syntax_error(comp, nodes[i], "'data' requires integer arguments");
+                        return;
+                    }
+                    mp_asm_base_data((mp_asm_base_t*)comp->emit_inline_asm,
+                        bytesize, MP_PARSE_NODE_LEAF_SMALL_INT(pn_arg[j]));
+                }
+            }
+        } else {
+            if (pass > MP_PASS_SCOPE) {
+                EMIT_INLINE_ASM_ARG(op, op, n_args, pn_arg);
+            }
+        }
+
+        if (comp->compile_error != MP_OBJ_NULL) {
+            pns = pns2; // this is the parse node that had the error
+            goto inline_asm_error;
+        }
+    }
+
+    if (comp->pass > MP_PASS_SCOPE) {
+        EMIT_INLINE_ASM_ARG(end_pass, type_sig);
+
+        if (comp->pass == MP_PASS_EMIT) {
+            void *f = mp_asm_base_get_code((mp_asm_base_t*)comp->emit_inline_asm);
+            mp_emit_glue_assign_native(comp->scope_cur->raw_code, MP_CODE_NATIVE_ASM,
+                f, mp_asm_base_get_code_size((mp_asm_base_t*)comp->emit_inline_asm),
+                NULL, comp->scope_cur->num_pos_args, 0, type_sig);
+        }
+    }
+
+    if (comp->compile_error != MP_OBJ_NULL) {
+        // inline assembler had an error; set line for its exception
+    inline_asm_error:
+        comp->compile_error_line = pns->source_line;
+    }
+}
+#endif
+
+STATIC void scope_compute_things(scope_t *scope) {
+    // in MicroPython we put the *x parameter after all other parameters (except **y)
+    if (scope->scope_flags & MP_SCOPE_FLAG_VARARGS) {
+        id_info_t *id_param = NULL;
+        for (int i = scope->id_info_len - 1; i >= 0; i--) {
+            id_info_t *id = &scope->id_info[i];
+            if (id->flags & ID_FLAG_IS_STAR_PARAM) {
+                if (id_param != NULL) {
+                    // swap star param with last param
+                    id_info_t temp = *id_param; *id_param = *id; *id = temp;
+                }
+                break;
+            } else if (id_param == NULL && id->flags == ID_FLAG_IS_PARAM) {
+                id_param = id;
+            }
+        }
+    }
+
+    // in functions, turn implicit globals into explicit globals
+    // compute the index of each local
+    scope->num_locals = 0;
+    for (int i = 0; i < scope->id_info_len; i++) {
+        id_info_t *id = &scope->id_info[i];
+        if (scope->kind == SCOPE_CLASS && id->qst == MP_QSTR___class__) {
+            // __class__ is not counted as a local; if it's used then it becomes a ID_INFO_KIND_CELL
+            continue;
+        }
+        if (SCOPE_IS_FUNC_LIKE(scope->kind) && id->kind == ID_INFO_KIND_GLOBAL_IMPLICIT) {
+            id->kind = ID_INFO_KIND_GLOBAL_EXPLICIT;
+        }
+        // params always count for 1 local, even if they are a cell
+        if (id->kind == ID_INFO_KIND_LOCAL || (id->flags & ID_FLAG_IS_PARAM)) {
+            id->local_num = scope->num_locals++;
+        }
+    }
+
+    // compute the index of cell vars
+    for (int i = 0; i < scope->id_info_len; i++) {
+        id_info_t *id = &scope->id_info[i];
+        // in MicroPython the cells come right after the fast locals
+        // parameters are not counted here, since they remain at the start
+        // of the locals, even if they are cell vars
+        if (id->kind == ID_INFO_KIND_CELL && !(id->flags & ID_FLAG_IS_PARAM)) {
+            id->local_num = scope->num_locals;
+            scope->num_locals += 1;
+        }
+    }
+
+    // compute the index of free vars
+    // make sure they are in the order of the parent scope
+    if (scope->parent != NULL) {
+        int num_free = 0;
+        for (int i = 0; i < scope->parent->id_info_len; i++) {
+            id_info_t *id = &scope->parent->id_info[i];
+            if (id->kind == ID_INFO_KIND_CELL || id->kind == ID_INFO_KIND_FREE) {
+                for (int j = 0; j < scope->id_info_len; j++) {
+                    id_info_t *id2 = &scope->id_info[j];
+                    if (id2->kind == ID_INFO_KIND_FREE && id->qst == id2->qst) {
+                        assert(!(id2->flags & ID_FLAG_IS_PARAM)); // free vars should not be params
+                        // in MicroPython the frees come first, before the params
+                        id2->local_num = num_free;
+                        num_free += 1;
+                    }
+                }
+            }
+        }
+        // in MicroPython shift all other locals after the free locals
+        if (num_free > 0) {
+            for (int i = 0; i < scope->id_info_len; i++) {
+                id_info_t *id = &scope->id_info[i];
+                if (id->kind != ID_INFO_KIND_FREE || (id->flags & ID_FLAG_IS_PARAM)) {
+                    id->local_num += num_free;
+                }
+            }
+            scope->num_pos_args += num_free; // free vars are counted as params for passing them into the function
+            scope->num_locals += num_free;
+        }
+    }
+}
+
+#if !MICROPY_PERSISTENT_CODE_SAVE
+STATIC
+#endif
+mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, uint emit_opt, bool is_repl) {
+    // put compiler state on the stack, it's relatively small
+    compiler_t comp_state = {0};
+    compiler_t *comp = &comp_state;
+
+    comp->source_file = source_file;
+    comp->is_repl = is_repl;
+    comp->break_label = INVALID_LABEL;
+    comp->continue_label = INVALID_LABEL;
+
+    // create the module scope
+    scope_t *module_scope = scope_new_and_link(comp, SCOPE_MODULE, parse_tree->root, emit_opt);
+
+    // create standard emitter; it's used at least for MP_PASS_SCOPE
+    emit_t *emit_bc = emit_bc_new();
+
+    // compile pass 1
+    comp->emit = emit_bc;
+    #if MICROPY_EMIT_NATIVE
+    comp->emit_method_table = &emit_bc_method_table;
+    #endif
+    uint max_num_labels = 0;
+    for (scope_t *s = comp->scope_head; s != NULL && comp->compile_error == MP_OBJ_NULL; s = s->next) {
+        if (false) {
+        #if MICROPY_EMIT_INLINE_ASM
+        } else if (s->emit_options == MP_EMIT_OPT_ASM) {
+            compile_scope_inline_asm(comp, s, MP_PASS_SCOPE);
+        #endif
+        } else {
+            compile_scope(comp, s, MP_PASS_SCOPE);
+        }
+
+        // update maximim number of labels needed
+        if (comp->next_label > max_num_labels) {
+            max_num_labels = comp->next_label;
+        }
+    }
+
+    // compute some things related to scope and identifiers
+    for (scope_t *s = comp->scope_head; s != NULL && comp->compile_error == MP_OBJ_NULL; s = s->next) {
+        scope_compute_things(s);
+    }
+
+    // set max number of labels now that it's calculated
+    emit_bc_set_max_num_labels(emit_bc, max_num_labels);
+
+    // compile pass 2 and 3
+#if MICROPY_EMIT_NATIVE
+    emit_t *emit_native = NULL;
+#endif
+    for (scope_t *s = comp->scope_head; s != NULL && comp->compile_error == MP_OBJ_NULL; s = s->next) {
+        if (false) {
+            // dummy
+
+        #if MICROPY_EMIT_INLINE_ASM
+        } else if (s->emit_options == MP_EMIT_OPT_ASM) {
+            // inline assembly
+            if (comp->emit_inline_asm == NULL) {
+                comp->emit_inline_asm = ASM_EMITTER(new)(max_num_labels);
+            }
+            comp->emit = NULL;
+            comp->emit_inline_asm_method_table = &ASM_EMITTER(method_table);
+            compile_scope_inline_asm(comp, s, MP_PASS_CODE_SIZE);
+            #if MICROPY_EMIT_INLINE_XTENSA
+            // Xtensa requires an extra pass to compute size of l32r const table
+            // TODO this can be improved by calculating it during SCOPE pass
+            // but that requires some other structural changes to the asm emitters
+            compile_scope_inline_asm(comp, s, MP_PASS_CODE_SIZE);
+            #endif
+            if (comp->compile_error == MP_OBJ_NULL) {
+                compile_scope_inline_asm(comp, s, MP_PASS_EMIT);
+            }
+        #endif
+
+        } else {
+
+            // choose the emit type
+
+            switch (s->emit_options) {
+
+#if MICROPY_EMIT_NATIVE
+                case MP_EMIT_OPT_NATIVE_PYTHON:
+                case MP_EMIT_OPT_VIPER:
+                    if (emit_native == NULL) {
+                        emit_native = NATIVE_EMITTER(new)(&comp->compile_error, max_num_labels);
+                    }
+                    comp->emit_method_table = &NATIVE_EMITTER(method_table);
+                    comp->emit = emit_native;
+                    EMIT_ARG(set_native_type, MP_EMIT_NATIVE_TYPE_ENABLE, s->emit_options == MP_EMIT_OPT_VIPER, 0);
+                    break;
+#endif // MICROPY_EMIT_NATIVE
+
+                default:
+                    comp->emit = emit_bc;
+                    #if MICROPY_EMIT_NATIVE
+                    comp->emit_method_table = &emit_bc_method_table;
+                    #endif
+                    break;
+            }
+
+            // need a pass to compute stack size
+            compile_scope(comp, s, MP_PASS_STACK_SIZE);
+
+            // second last pass: compute code size
+            if (comp->compile_error == MP_OBJ_NULL) {
+                compile_scope(comp, s, MP_PASS_CODE_SIZE);
+            }
+
+            // final pass: emit code
+            if (comp->compile_error == MP_OBJ_NULL) {
+                compile_scope(comp, s, MP_PASS_EMIT);
+            }
+        }
+    }
+
+    if (comp->compile_error != MP_OBJ_NULL) {
+        // if there is no line number for the error then use the line
+        // number for the start of this scope
+        compile_error_set_line(comp, comp->scope_cur->pn);
+        // add a traceback to the exception using relevant source info
+        mp_obj_exception_add_traceback(comp->compile_error, comp->source_file,
+            comp->compile_error_line, comp->scope_cur->simple_name);
+    }
+
+    // free the emitters
+
+    emit_bc_free(emit_bc);
+#if MICROPY_EMIT_NATIVE
+    if (emit_native != NULL) {
+        NATIVE_EMITTER(free)(emit_native);
+    }
+#endif
+    #if MICROPY_EMIT_INLINE_ASM
+    if (comp->emit_inline_asm != NULL) {
+        ASM_EMITTER(free)(comp->emit_inline_asm);
+    }
+    #endif
+
+    // free the parse tree
+    mp_parse_tree_clear(parse_tree);
+
+    // free the scopes
+    mp_raw_code_t *outer_raw_code = module_scope->raw_code;
+    for (scope_t *s = module_scope; s;) {
+        scope_t *next = s->next;
+        scope_free(s);
+        s = next;
+    }
+
+    if (comp->compile_error != MP_OBJ_NULL) {
+        nlr_raise(comp->compile_error);
+    } else {
+        return outer_raw_code;
+    }
+}
+
+mp_obj_t mp_compile(mp_parse_tree_t *parse_tree, qstr source_file, uint emit_opt, bool is_repl) {
+    mp_raw_code_t *rc = mp_compile_to_raw_code(parse_tree, source_file, emit_opt, is_repl);
+    // return function that executes the outer module
+    return mp_make_function_from_raw_code(rc, MP_OBJ_NULL, MP_OBJ_NULL);
+}
+
+#endif // MICROPY_ENABLE_COMPILER

+ 54 - 0
py/compile.h

@@ -0,0 +1,54 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_PY_COMPILE_H
+#define MICROPY_INCLUDED_PY_COMPILE_H
+
+#include "py/lexer.h"
+#include "py/parse.h"
+#include "py/emitglue.h"
+
+// These must fit in 8 bits; see scope.h
+enum {
+    MP_EMIT_OPT_NONE,
+    MP_EMIT_OPT_BYTECODE,
+    MP_EMIT_OPT_NATIVE_PYTHON,
+    MP_EMIT_OPT_VIPER,
+    MP_EMIT_OPT_ASM,
+};
+
+// the compiler will raise an exception if an error occurred
+// the compiler will clear the parse tree before it returns
+mp_obj_t mp_compile(mp_parse_tree_t *parse_tree, qstr source_file, uint emit_opt, bool is_repl);
+
+#if MICROPY_PERSISTENT_CODE_SAVE
+// this has the same semantics as mp_compile
+mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, uint emit_opt, bool is_repl);
+#endif
+
+// this is implemented in runtime.c
+mp_obj_t mp_parse_compile_execute(mp_lexer_t *lex, mp_parse_input_kind_t parse_input_kind, mp_obj_dict_t *globals, mp_obj_dict_t *locals);
+
+#endif // MICROPY_INCLUDED_PY_COMPILE_H

+ 285 - 0
py/emit.h

@@ -0,0 +1,285 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_PY_EMIT_H
+#define MICROPY_INCLUDED_PY_EMIT_H
+
+#include "py/lexer.h"
+#include "py/scope.h"
+
+/* Notes on passes:
+ * We don't know exactly the opcodes in pass 1 because they depend on the
+ * closing over of variables (LOAD_CLOSURE, BUILD_TUPLE, MAKE_CLOSURE), which
+ * depends on determining the scope of variables in each function, and this
+ * is not known until the end of pass 1.
+ * As a consequence, we don't know the maximum stack size until the end of pass 2.
+ * This is problematic for some emitters (x64) since they need to know the maximum
+ * stack size to compile the entry to the function, and this affects code size.
+ */
+
+typedef enum {
+    MP_PASS_SCOPE = 1,      // work out id's and their kind, and number of labels
+    MP_PASS_STACK_SIZE = 2, // work out maximum stack size
+    MP_PASS_CODE_SIZE = 3,  // work out code size and label offsets
+    MP_PASS_EMIT = 4,       // emit code
+} pass_kind_t;
+
+#define MP_EMIT_STAR_FLAG_SINGLE (0x01)
+#define MP_EMIT_STAR_FLAG_DOUBLE (0x02)
+
+#define MP_EMIT_BREAK_FROM_FOR (0x8000)
+
+#define MP_EMIT_NATIVE_TYPE_ENABLE (0)
+#define MP_EMIT_NATIVE_TYPE_RETURN (1)
+#define MP_EMIT_NATIVE_TYPE_ARG    (2)
+
+typedef struct _emit_t emit_t;
+
+typedef struct _mp_emit_method_table_id_ops_t {
+    void (*fast)(emit_t *emit, qstr qst, mp_uint_t local_num);
+    void (*deref)(emit_t *emit, qstr qst, mp_uint_t local_num);
+    void (*name)(emit_t *emit, qstr qst);
+    void (*global)(emit_t *emit, qstr qst);
+} mp_emit_method_table_id_ops_t;
+
+typedef struct _emit_method_table_t {
+    void (*set_native_type)(emit_t *emit, mp_uint_t op, mp_uint_t arg1, qstr arg2);
+    void (*start_pass)(emit_t *emit, pass_kind_t pass, scope_t *scope);
+    void (*end_pass)(emit_t *emit);
+    bool (*last_emit_was_return_value)(emit_t *emit);
+    void (*adjust_stack_size)(emit_t *emit, mp_int_t delta);
+    void (*set_source_line)(emit_t *emit, mp_uint_t line);
+
+    mp_emit_method_table_id_ops_t load_id;
+    mp_emit_method_table_id_ops_t store_id;
+    mp_emit_method_table_id_ops_t delete_id;
+
+    void (*label_assign)(emit_t *emit, mp_uint_t l);
+    void (*import_name)(emit_t *emit, qstr qst);
+    void (*import_from)(emit_t *emit, qstr qst);
+    void (*import_star)(emit_t *emit);
+    void (*load_const_tok)(emit_t *emit, mp_token_kind_t tok);
+    void (*load_const_small_int)(emit_t *emit, mp_int_t arg);
+    void (*load_const_str)(emit_t *emit, qstr qst);
+    void (*load_const_obj)(emit_t *emit, mp_obj_t obj);
+    void (*load_null)(emit_t *emit);
+    void (*load_attr)(emit_t *emit, qstr qst);
+    void (*load_method)(emit_t *emit, qstr qst, bool is_super);
+    void (*load_build_class)(emit_t *emit);
+    void (*load_subscr)(emit_t *emit);
+    void (*store_attr)(emit_t *emit, qstr qst);
+    void (*store_subscr)(emit_t *emit);
+    void (*delete_attr)(emit_t *emit, qstr qst);
+    void (*delete_subscr)(emit_t *emit);
+    void (*dup_top)(emit_t *emit);
+    void (*dup_top_two)(emit_t *emit);
+    void (*pop_top)(emit_t *emit);
+    void (*rot_two)(emit_t *emit);
+    void (*rot_three)(emit_t *emit);
+    void (*jump)(emit_t *emit, mp_uint_t label);
+    void (*pop_jump_if)(emit_t *emit, bool cond, mp_uint_t label);
+    void (*jump_if_or_pop)(emit_t *emit, bool cond, mp_uint_t label);
+    void (*break_loop)(emit_t *emit, mp_uint_t label, mp_uint_t except_depth);
+    void (*continue_loop)(emit_t *emit, mp_uint_t label, mp_uint_t except_depth);
+    void (*setup_with)(emit_t *emit, mp_uint_t label);
+    void (*with_cleanup)(emit_t *emit, mp_uint_t label);
+    void (*setup_except)(emit_t *emit, mp_uint_t label);
+    void (*setup_finally)(emit_t *emit, mp_uint_t label);
+    void (*end_finally)(emit_t *emit);
+    void (*get_iter)(emit_t *emit, bool use_stack);
+    void (*for_iter)(emit_t *emit, mp_uint_t label);
+    void (*for_iter_end)(emit_t *emit);
+    void (*pop_block)(emit_t *emit);
+    void (*pop_except)(emit_t *emit);
+    void (*unary_op)(emit_t *emit, mp_unary_op_t op);
+    void (*binary_op)(emit_t *emit, mp_binary_op_t op);
+    void (*build_tuple)(emit_t *emit, mp_uint_t n_args);
+    void (*build_list)(emit_t *emit, mp_uint_t n_args);
+    void (*build_map)(emit_t *emit, mp_uint_t n_args);
+    void (*store_map)(emit_t *emit);
+    #if MICROPY_PY_BUILTINS_SET
+    void (*build_set)(emit_t *emit, mp_uint_t n_args);
+    #endif
+    #if MICROPY_PY_BUILTINS_SLICE
+    void (*build_slice)(emit_t *emit, mp_uint_t n_args);
+    #endif
+    void (*store_comp)(emit_t *emit, scope_kind_t kind, mp_uint_t set_stack_index);
+    void (*unpack_sequence)(emit_t *emit, mp_uint_t n_args);
+    void (*unpack_ex)(emit_t *emit, mp_uint_t n_left, mp_uint_t n_right);
+    void (*make_function)(emit_t *emit, scope_t *scope, mp_uint_t n_pos_defaults, mp_uint_t n_kw_defaults);
+    void (*make_closure)(emit_t *emit, scope_t *scope, mp_uint_t n_closed_over, mp_uint_t n_pos_defaults, mp_uint_t n_kw_defaults);
+    void (*call_function)(emit_t *emit, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags);
+    void (*call_method)(emit_t *emit, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags);
+    void (*return_value)(emit_t *emit);
+    void (*raise_varargs)(emit_t *emit, mp_uint_t n_args);
+    void (*yield_value)(emit_t *emit);
+    void (*yield_from)(emit_t *emit);
+
+    // these methods are used to control entry to/exit from an exception handler
+    // they may or may not emit code
+    void (*start_except_handler)(emit_t *emit);
+    void (*end_except_handler)(emit_t *emit);
+} emit_method_table_t;
+
+void mp_emit_common_get_id_for_load(scope_t *scope, qstr qst);
+void mp_emit_common_get_id_for_modification(scope_t *scope, qstr qst);
+void mp_emit_common_id_op(emit_t *emit, const mp_emit_method_table_id_ops_t *emit_method_table, scope_t *scope, qstr qst);
+
+extern const emit_method_table_t emit_bc_method_table;
+extern const emit_method_table_t emit_native_x64_method_table;
+extern const emit_method_table_t emit_native_x86_method_table;
+extern const emit_method_table_t emit_native_thumb_method_table;
+extern const emit_method_table_t emit_native_arm_method_table;
+extern const emit_method_table_t emit_native_xtensa_method_table;
+
+extern const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_load_id_ops;
+extern const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_store_id_ops;
+extern const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_delete_id_ops;
+
+emit_t *emit_bc_new(void);
+emit_t *emit_native_x64_new(mp_obj_t *error_slot, mp_uint_t max_num_labels);
+emit_t *emit_native_x86_new(mp_obj_t *error_slot, mp_uint_t max_num_labels);
+emit_t *emit_native_thumb_new(mp_obj_t *error_slot, mp_uint_t max_num_labels);
+emit_t *emit_native_arm_new(mp_obj_t *error_slot, mp_uint_t max_num_labels);
+emit_t *emit_native_xtensa_new(mp_obj_t *error_slot, mp_uint_t max_num_labels);
+
+void emit_bc_set_max_num_labels(emit_t* emit, mp_uint_t max_num_labels);
+
+void emit_bc_free(emit_t *emit);
+void emit_native_x64_free(emit_t *emit);
+void emit_native_x86_free(emit_t *emit);
+void emit_native_thumb_free(emit_t *emit);
+void emit_native_arm_free(emit_t *emit);
+void emit_native_xtensa_free(emit_t *emit);
+
+void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope);
+void mp_emit_bc_end_pass(emit_t *emit);
+bool mp_emit_bc_last_emit_was_return_value(emit_t *emit);
+void mp_emit_bc_adjust_stack_size(emit_t *emit, mp_int_t delta);
+void mp_emit_bc_set_source_line(emit_t *emit, mp_uint_t line);
+
+void mp_emit_bc_load_fast(emit_t *emit, qstr qst, mp_uint_t local_num);
+void mp_emit_bc_load_deref(emit_t *emit, qstr qst, mp_uint_t local_num);
+void mp_emit_bc_load_name(emit_t *emit, qstr qst);
+void mp_emit_bc_load_global(emit_t *emit, qstr qst);
+void mp_emit_bc_store_fast(emit_t *emit, qstr qst, mp_uint_t local_num);
+void mp_emit_bc_store_deref(emit_t *emit, qstr qst, mp_uint_t local_num);
+void mp_emit_bc_store_name(emit_t *emit, qstr qst);
+void mp_emit_bc_store_global(emit_t *emit, qstr qst);
+void mp_emit_bc_delete_fast(emit_t *emit, qstr qst, mp_uint_t local_num);
+void mp_emit_bc_delete_deref(emit_t *emit, qstr qst, mp_uint_t local_num);
+void mp_emit_bc_delete_name(emit_t *emit, qstr qst);
+void mp_emit_bc_delete_global(emit_t *emit, qstr qst);
+
+void mp_emit_bc_label_assign(emit_t *emit, mp_uint_t l);
+void mp_emit_bc_import_name(emit_t *emit, qstr qst);
+void mp_emit_bc_import_from(emit_t *emit, qstr qst);
+void mp_emit_bc_import_star(emit_t *emit);
+void mp_emit_bc_load_const_tok(emit_t *emit, mp_token_kind_t tok);
+void mp_emit_bc_load_const_small_int(emit_t *emit, mp_int_t arg);
+void mp_emit_bc_load_const_str(emit_t *emit, qstr qst);
+void mp_emit_bc_load_const_obj(emit_t *emit, mp_obj_t obj);
+void mp_emit_bc_load_null(emit_t *emit);
+void mp_emit_bc_load_attr(emit_t *emit, qstr qst);
+void mp_emit_bc_load_method(emit_t *emit, qstr qst, bool is_super);
+void mp_emit_bc_load_build_class(emit_t *emit);
+void mp_emit_bc_load_subscr(emit_t *emit);
+void mp_emit_bc_store_attr(emit_t *emit, qstr qst);
+void mp_emit_bc_store_subscr(emit_t *emit);
+void mp_emit_bc_delete_attr(emit_t *emit, qstr qst);
+void mp_emit_bc_delete_subscr(emit_t *emit);
+void mp_emit_bc_dup_top(emit_t *emit);
+void mp_emit_bc_dup_top_two(emit_t *emit);
+void mp_emit_bc_pop_top(emit_t *emit);
+void mp_emit_bc_rot_two(emit_t *emit);
+void mp_emit_bc_rot_three(emit_t *emit);
+void mp_emit_bc_jump(emit_t *emit, mp_uint_t label);
+void mp_emit_bc_pop_jump_if(emit_t *emit, bool cond, mp_uint_t label);
+void mp_emit_bc_jump_if_or_pop(emit_t *emit, bool cond, mp_uint_t label);
+void mp_emit_bc_unwind_jump(emit_t *emit, mp_uint_t label, mp_uint_t except_depth);
+#define mp_emit_bc_break_loop mp_emit_bc_unwind_jump
+#define mp_emit_bc_continue_loop mp_emit_bc_unwind_jump
+void mp_emit_bc_setup_with(emit_t *emit, mp_uint_t label);
+void mp_emit_bc_with_cleanup(emit_t *emit, mp_uint_t label);
+void mp_emit_bc_setup_except(emit_t *emit, mp_uint_t label);
+void mp_emit_bc_setup_finally(emit_t *emit, mp_uint_t label);
+void mp_emit_bc_end_finally(emit_t *emit);
+void mp_emit_bc_get_iter(emit_t *emit, bool use_stack);
+void mp_emit_bc_for_iter(emit_t *emit, mp_uint_t label);
+void mp_emit_bc_for_iter_end(emit_t *emit);
+void mp_emit_bc_pop_block(emit_t *emit);
+void mp_emit_bc_pop_except(emit_t *emit);
+void mp_emit_bc_unary_op(emit_t *emit, mp_unary_op_t op);
+void mp_emit_bc_binary_op(emit_t *emit, mp_binary_op_t op);
+void mp_emit_bc_build_tuple(emit_t *emit, mp_uint_t n_args);
+void mp_emit_bc_build_list(emit_t *emit, mp_uint_t n_args);
+void mp_emit_bc_build_map(emit_t *emit, mp_uint_t n_args);
+void mp_emit_bc_store_map(emit_t *emit);
+#if MICROPY_PY_BUILTINS_SET
+void mp_emit_bc_build_set(emit_t *emit, mp_uint_t n_args);
+#endif
+#if MICROPY_PY_BUILTINS_SLICE
+void mp_emit_bc_build_slice(emit_t *emit, mp_uint_t n_args);
+#endif
+void mp_emit_bc_store_comp(emit_t *emit, scope_kind_t kind, mp_uint_t list_stack_index);
+void mp_emit_bc_unpack_sequence(emit_t *emit, mp_uint_t n_args);
+void mp_emit_bc_unpack_ex(emit_t *emit, mp_uint_t n_left, mp_uint_t n_right);
+void mp_emit_bc_make_function(emit_t *emit, scope_t *scope, mp_uint_t n_pos_defaults, mp_uint_t n_kw_defaults);
+void mp_emit_bc_make_closure(emit_t *emit, scope_t *scope, mp_uint_t n_closed_over, mp_uint_t n_pos_defaults, mp_uint_t n_kw_defaults);
+void mp_emit_bc_call_function(emit_t *emit, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags);
+void mp_emit_bc_call_method(emit_t *emit, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags);
+void mp_emit_bc_return_value(emit_t *emit);
+void mp_emit_bc_raise_varargs(emit_t *emit, mp_uint_t n_args);
+void mp_emit_bc_yield_value(emit_t *emit);
+void mp_emit_bc_yield_from(emit_t *emit);
+void mp_emit_bc_start_except_handler(emit_t *emit);
+void mp_emit_bc_end_except_handler(emit_t *emit);
+
+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, mp_obj_t *error_slot);
+    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 (*op)(emit_inline_asm_t *emit, qstr op, mp_uint_t n_args, mp_parse_node_t *pn_args);
+} emit_inline_asm_method_table_t;
+
+extern const emit_inline_asm_method_table_t emit_inline_thumb_method_table;
+extern const emit_inline_asm_method_table_t emit_inline_xtensa_method_table;
+
+emit_inline_asm_t *emit_inline_thumb_new(mp_uint_t max_num_labels);
+emit_inline_asm_t *emit_inline_xtensa_new(mp_uint_t max_num_labels);
+
+void emit_inline_thumb_free(emit_inline_asm_t *emit);
+void emit_inline_xtensa_free(emit_inline_asm_t *emit);
+
+#if MICROPY_WARNINGS
+void mp_emitter_warning(pass_kind_t pass, const char *msg);
+#else
+#define mp_emitter_warning(pass, msg)
+#endif
+
+#endif // MICROPY_INCLUDED_PY_EMIT_H

+ 1076 - 0
py/emitbc.c

@@ -0,0 +1,1076 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "py/mpstate.h"
+#include "py/emit.h"
+#include "py/bc0.h"
+
+#if MICROPY_ENABLE_COMPILER
+
+#define BYTES_FOR_INT ((BYTES_PER_WORD * 8 + 6) / 7)
+#define DUMMY_DATA_SIZE (BYTES_FOR_INT)
+
+struct _emit_t {
+    // Accessed as mp_obj_t, so must be aligned as such, and we rely on the
+    // memory allocator returning a suitably aligned pointer.
+    // Should work for cases when mp_obj_t is 64-bit on a 32-bit machine.
+    byte dummy_data[DUMMY_DATA_SIZE];
+
+    pass_kind_t pass : 8;
+    mp_uint_t last_emit_was_return_value : 8;
+
+    int stack_size;
+
+    scope_t *scope;
+
+    mp_uint_t last_source_line_offset;
+    mp_uint_t last_source_line;
+
+    mp_uint_t max_num_labels;
+    mp_uint_t *label_offsets;
+
+    size_t code_info_offset;
+    size_t code_info_size;
+    size_t bytecode_offset;
+    size_t bytecode_size;
+    byte *code_base; // stores both byte code and code info
+
+    #if MICROPY_PERSISTENT_CODE
+    uint16_t ct_cur_obj;
+    uint16_t ct_num_obj;
+    uint16_t ct_cur_raw_code;
+    #endif
+    mp_uint_t *const_table;
+};
+
+emit_t *emit_bc_new(void) {
+    emit_t *emit = m_new0(emit_t, 1);
+    return emit;
+}
+
+void emit_bc_set_max_num_labels(emit_t *emit, mp_uint_t max_num_labels) {
+    emit->max_num_labels = max_num_labels;
+    emit->label_offsets = m_new(mp_uint_t, emit->max_num_labels);
+}
+
+void emit_bc_free(emit_t *emit) {
+    m_del(mp_uint_t, emit->label_offsets, emit->max_num_labels);
+    m_del_obj(emit_t, emit);
+}
+
+typedef byte *(*emit_allocator_t)(emit_t *emit, int nbytes);
+
+STATIC void emit_write_uint(emit_t *emit, emit_allocator_t allocator, mp_uint_t val) {
+    // We store each 7 bits in a separate byte, and that's how many bytes needed
+    byte buf[BYTES_FOR_INT];
+    byte *p = buf + sizeof(buf);
+    // We encode in little-ending order, but store in big-endian, to help decoding
+    do {
+        *--p = val & 0x7f;
+        val >>= 7;
+    } while (val != 0);
+    byte *c = allocator(emit, buf + sizeof(buf) - p);
+    while (p != buf + sizeof(buf) - 1) {
+        *c++ = *p++ | 0x80;
+    }
+    *c = *p;
+}
+
+// all functions must go through this one to emit code info
+STATIC byte *emit_get_cur_to_write_code_info(emit_t *emit, int num_bytes_to_write) {
+    //printf("emit %d\n", num_bytes_to_write);
+    if (emit->pass < MP_PASS_EMIT) {
+        emit->code_info_offset += num_bytes_to_write;
+        return emit->dummy_data;
+    } else {
+        assert(emit->code_info_offset + num_bytes_to_write <= emit->code_info_size);
+        byte *c = emit->code_base + emit->code_info_offset;
+        emit->code_info_offset += num_bytes_to_write;
+        return c;
+    }
+}
+
+STATIC void emit_write_code_info_byte(emit_t* emit, byte val) {
+    *emit_get_cur_to_write_code_info(emit, 1) = val;
+}
+
+STATIC void emit_write_code_info_uint(emit_t* emit, mp_uint_t val) {
+    emit_write_uint(emit, emit_get_cur_to_write_code_info, val);
+}
+
+STATIC void emit_write_code_info_qstr(emit_t *emit, qstr qst) {
+    #if MICROPY_PERSISTENT_CODE
+    assert((qst >> 16) == 0);
+    byte *c = emit_get_cur_to_write_code_info(emit, 2);
+    c[0] = qst;
+    c[1] = qst >> 8;
+    #else
+    emit_write_uint(emit, emit_get_cur_to_write_code_info, qst);
+    #endif
+}
+
+#if MICROPY_ENABLE_SOURCE_LINE
+STATIC void emit_write_code_info_bytes_lines(emit_t *emit, mp_uint_t bytes_to_skip, mp_uint_t lines_to_skip) {
+    assert(bytes_to_skip > 0 || lines_to_skip > 0);
+    //printf("  %d %d\n", bytes_to_skip, lines_to_skip);
+    while (bytes_to_skip > 0 || lines_to_skip > 0) {
+        mp_uint_t b, l;
+        if (lines_to_skip <= 6 || bytes_to_skip > 0xf) {
+            // use 0b0LLBBBBB encoding
+            b = MIN(bytes_to_skip, 0x1f);
+            if (b < bytes_to_skip) {
+                // we can't skip any lines until we skip all the bytes
+                l = 0;
+            } else {
+                l = MIN(lines_to_skip, 0x3);
+            }
+            *emit_get_cur_to_write_code_info(emit, 1) = b | (l << 5);
+        } else {
+            // use 0b1LLLBBBB 0bLLLLLLLL encoding (l's LSB in second byte)
+            b = MIN(bytes_to_skip, 0xf);
+            l = MIN(lines_to_skip, 0x7ff);
+            byte *ci = emit_get_cur_to_write_code_info(emit, 2);
+            ci[0] = 0x80 | b | ((l >> 4) & 0x70);
+            ci[1] = l;
+        }
+        bytes_to_skip -= b;
+        lines_to_skip -= l;
+    }
+}
+#endif
+
+// all functions must go through this one to emit byte code
+STATIC byte *emit_get_cur_to_write_bytecode(emit_t *emit, int num_bytes_to_write) {
+    //printf("emit %d\n", num_bytes_to_write);
+    if (emit->pass < MP_PASS_EMIT) {
+        emit->bytecode_offset += num_bytes_to_write;
+        return emit->dummy_data;
+    } else {
+        assert(emit->bytecode_offset + num_bytes_to_write <= emit->bytecode_size);
+        byte *c = emit->code_base + emit->code_info_size + emit->bytecode_offset;
+        emit->bytecode_offset += num_bytes_to_write;
+        return c;
+    }
+}
+
+STATIC void emit_write_bytecode_byte(emit_t *emit, byte b1) {
+    byte *c = emit_get_cur_to_write_bytecode(emit, 1);
+    c[0] = b1;
+}
+
+STATIC void emit_write_bytecode_byte_byte(emit_t* emit, byte b1, byte b2) {
+    byte *c = emit_get_cur_to_write_bytecode(emit, 2);
+    c[0] = b1;
+    c[1] = b2;
+}
+
+// Similar to emit_write_bytecode_uint(), just some extra handling to encode sign
+STATIC void emit_write_bytecode_byte_int(emit_t *emit, byte b1, mp_int_t num) {
+    emit_write_bytecode_byte(emit, b1);
+
+    // We store each 7 bits in a separate byte, and that's how many bytes needed
+    byte buf[BYTES_FOR_INT];
+    byte *p = buf + sizeof(buf);
+    // We encode in little-ending order, but store in big-endian, to help decoding
+    do {
+        *--p = num & 0x7f;
+        num >>= 7;
+    } while (num != 0 && num != -1);
+    // Make sure that highest bit we stored (mask 0x40) matches sign
+    // of the number. If not, store extra byte just to encode sign
+    if (num == -1 && (*p & 0x40) == 0) {
+        *--p = 0x7f;
+    } else if (num == 0 && (*p & 0x40) != 0) {
+        *--p = 0;
+    }
+
+    byte *c = emit_get_cur_to_write_bytecode(emit, buf + sizeof(buf) - p);
+    while (p != buf + sizeof(buf) - 1) {
+        *c++ = *p++ | 0x80;
+    }
+    *c = *p;
+}
+
+STATIC void emit_write_bytecode_byte_uint(emit_t *emit, byte b, mp_uint_t val) {
+    emit_write_bytecode_byte(emit, b);
+    emit_write_uint(emit, emit_get_cur_to_write_bytecode, val);
+}
+
+#if MICROPY_PERSISTENT_CODE
+STATIC void emit_write_bytecode_byte_const(emit_t *emit, byte b, mp_uint_t n, mp_uint_t c) {
+    if (emit->pass == MP_PASS_EMIT) {
+        emit->const_table[n] = c;
+    }
+    emit_write_bytecode_byte_uint(emit, b, n);
+}
+#endif
+
+STATIC void emit_write_bytecode_byte_qstr(emit_t* emit, byte b, qstr qst) {
+    #if MICROPY_PERSISTENT_CODE
+    assert((qst >> 16) == 0);
+    byte *c = emit_get_cur_to_write_bytecode(emit, 3);
+    c[0] = b;
+    c[1] = qst;
+    c[2] = qst >> 8;
+    #else
+    emit_write_bytecode_byte_uint(emit, b, qst);
+    #endif
+}
+
+STATIC void emit_write_bytecode_byte_obj(emit_t *emit, byte b, mp_obj_t obj) {
+    #if MICROPY_PERSISTENT_CODE
+    emit_write_bytecode_byte_const(emit, b,
+        emit->scope->num_pos_args + emit->scope->num_kwonly_args
+        + emit->ct_cur_obj++, (mp_uint_t)obj);
+    #else
+    // aligns the pointer so it is friendly to GC
+    emit_write_bytecode_byte(emit, b);
+    emit->bytecode_offset = (size_t)MP_ALIGN(emit->bytecode_offset, sizeof(mp_obj_t));
+    mp_obj_t *c = (mp_obj_t*)emit_get_cur_to_write_bytecode(emit, sizeof(mp_obj_t));
+    // Verify thar c is already uint-aligned
+    assert(c == MP_ALIGN(c, sizeof(mp_obj_t)));
+    *c = obj;
+    #endif
+}
+
+STATIC void emit_write_bytecode_byte_raw_code(emit_t *emit, byte b, mp_raw_code_t *rc) {
+    #if MICROPY_PERSISTENT_CODE
+    emit_write_bytecode_byte_const(emit, b,
+        emit->scope->num_pos_args + emit->scope->num_kwonly_args
+        + emit->ct_num_obj + emit->ct_cur_raw_code++, (mp_uint_t)(uintptr_t)rc);
+    #else
+    // aligns the pointer so it is friendly to GC
+    emit_write_bytecode_byte(emit, b);
+    emit->bytecode_offset = (size_t)MP_ALIGN(emit->bytecode_offset, sizeof(void*));
+    void **c = (void**)emit_get_cur_to_write_bytecode(emit, sizeof(void*));
+    // Verify thar c is already uint-aligned
+    assert(c == MP_ALIGN(c, sizeof(void*)));
+    *c = rc;
+    #endif
+}
+
+// unsigned labels are relative to ip following this instruction, stored as 16 bits
+STATIC void emit_write_bytecode_byte_unsigned_label(emit_t *emit, byte b1, mp_uint_t label) {
+    mp_uint_t bytecode_offset;
+    if (emit->pass < MP_PASS_EMIT) {
+        bytecode_offset = 0;
+    } else {
+        bytecode_offset = emit->label_offsets[label] - emit->bytecode_offset - 3;
+    }
+    byte *c = emit_get_cur_to_write_bytecode(emit, 3);
+    c[0] = b1;
+    c[1] = bytecode_offset;
+    c[2] = bytecode_offset >> 8;
+}
+
+// signed labels are relative to ip following this instruction, stored as 16 bits, in excess
+STATIC void emit_write_bytecode_byte_signed_label(emit_t *emit, byte b1, mp_uint_t label) {
+    int bytecode_offset;
+    if (emit->pass < MP_PASS_EMIT) {
+        bytecode_offset = 0;
+    } else {
+        bytecode_offset = emit->label_offsets[label] - emit->bytecode_offset - 3 + 0x8000;
+    }
+    byte *c = emit_get_cur_to_write_bytecode(emit, 3);
+    c[0] = b1;
+    c[1] = bytecode_offset;
+    c[2] = bytecode_offset >> 8;
+}
+
+void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) {
+    emit->pass = pass;
+    emit->stack_size = 0;
+    emit->last_emit_was_return_value = false;
+    emit->scope = scope;
+    emit->last_source_line_offset = 0;
+    emit->last_source_line = 1;
+    if (pass < MP_PASS_EMIT) {
+        memset(emit->label_offsets, -1, emit->max_num_labels * sizeof(mp_uint_t));
+    }
+    emit->bytecode_offset = 0;
+    emit->code_info_offset = 0;
+
+    // Write local state size and exception stack size.
+    {
+        mp_uint_t n_state = scope->num_locals + scope->stack_size;
+        if (n_state == 0) {
+            // Need at least 1 entry in the state, in the case an exception is
+            // propagated through this function, the exception is returned in
+            // the highest slot in the state (fastn[0], see vm.c).
+            n_state = 1;
+        }
+        emit_write_code_info_uint(emit, n_state);
+        emit_write_code_info_uint(emit, scope->exc_stack_size);
+    }
+
+    // Write scope flags and number of arguments.
+    // TODO check that num args all fit in a byte
+    emit_write_code_info_byte(emit, emit->scope->scope_flags);
+    emit_write_code_info_byte(emit, emit->scope->num_pos_args);
+    emit_write_code_info_byte(emit, emit->scope->num_kwonly_args);
+    emit_write_code_info_byte(emit, emit->scope->num_def_pos_args);
+
+    // Write size of the rest of the code info.  We don't know how big this
+    // variable uint will be on the MP_PASS_CODE_SIZE pass so we reserve 2 bytes
+    // for it and hope that is enough!  TODO assert this or something.
+    if (pass == MP_PASS_EMIT) {
+        emit_write_code_info_uint(emit, emit->code_info_size - emit->code_info_offset);
+    } else  {
+        emit_get_cur_to_write_code_info(emit, 2);
+    }
+
+    // Write the name and source file of this function.
+    emit_write_code_info_qstr(emit, scope->simple_name);
+    emit_write_code_info_qstr(emit, scope->source_file);
+
+    // bytecode prelude: initialise closed over variables
+    for (int i = 0; i < scope->id_info_len; i++) {
+        id_info_t *id = &scope->id_info[i];
+        if (id->kind == ID_INFO_KIND_CELL) {
+            assert(id->local_num < 255);
+            emit_write_bytecode_byte(emit, id->local_num); // write the local which should be converted to a cell
+        }
+    }
+    emit_write_bytecode_byte(emit, 255); // end of list sentinel
+
+    #if MICROPY_PERSISTENT_CODE
+    emit->ct_cur_obj = 0;
+    emit->ct_cur_raw_code = 0;
+    #endif
+
+    if (pass == MP_PASS_EMIT) {
+        // Write argument names (needed to resolve positional args passed as
+        // keywords).  We store them as full word-sized objects for efficient access
+        // in mp_setup_code_state this is the start of the prelude and is guaranteed
+        // to be aligned on a word boundary.
+
+        // For a given argument position (indexed by i) we need to find the
+        // corresponding id_info which is a parameter, as it has the correct
+        // qstr name to use as the argument name.  Note that it's not a simple
+        // 1-1 mapping (ie i!=j in general) because of possible closed-over
+        // variables.  In the case that the argument i has no corresponding
+        // parameter we use "*" as its name (since no argument can ever be named
+        // "*").  We could use a blank qstr but "*" is better for debugging.
+        // Note: there is some wasted RAM here for the case of storing a qstr
+        // for each closed-over variable, and maybe there is a better way to do
+        // it, but that would require changes to mp_setup_code_state.
+        for (int i = 0; i < scope->num_pos_args + scope->num_kwonly_args; i++) {
+            qstr qst = MP_QSTR__star_;
+            for (int j = 0; j < scope->id_info_len; ++j) {
+                id_info_t *id = &scope->id_info[j];
+                if ((id->flags & ID_FLAG_IS_PARAM) && id->local_num == i) {
+                    qst = id->qst;
+                    break;
+                }
+            }
+            emit->const_table[i] = (mp_uint_t)MP_OBJ_NEW_QSTR(qst);
+        }
+    }
+}
+
+void mp_emit_bc_end_pass(emit_t *emit) {
+    if (emit->pass == MP_PASS_SCOPE) {
+        return;
+    }
+
+    // check stack is back to zero size
+    assert(emit->stack_size == 0);
+
+    emit_write_code_info_byte(emit, 0); // end of line number info
+
+    #if MICROPY_PERSISTENT_CODE
+    assert(emit->pass <= MP_PASS_STACK_SIZE || (emit->ct_num_obj == emit->ct_cur_obj));
+    emit->ct_num_obj = emit->ct_cur_obj;
+    #endif
+
+    if (emit->pass == MP_PASS_CODE_SIZE) {
+        #if !MICROPY_PERSISTENT_CODE
+        // so bytecode is aligned
+        emit->code_info_offset = (size_t)MP_ALIGN(emit->code_info_offset, sizeof(mp_uint_t));
+        #endif
+
+        // calculate size of total code-info + bytecode, in bytes
+        emit->code_info_size = emit->code_info_offset;
+        emit->bytecode_size = emit->bytecode_offset;
+        emit->code_base = m_new0(byte, emit->code_info_size + emit->bytecode_size);
+
+        #if MICROPY_PERSISTENT_CODE
+        emit->const_table = m_new0(mp_uint_t,
+            emit->scope->num_pos_args + emit->scope->num_kwonly_args
+            + emit->ct_cur_obj + emit->ct_cur_raw_code);
+        #else
+        emit->const_table = m_new0(mp_uint_t,
+            emit->scope->num_pos_args + emit->scope->num_kwonly_args);
+        #endif
+
+    } else if (emit->pass == MP_PASS_EMIT) {
+        mp_emit_glue_assign_bytecode(emit->scope->raw_code, emit->code_base,
+            emit->code_info_size + emit->bytecode_size,
+            emit->const_table,
+            #if MICROPY_PERSISTENT_CODE_SAVE
+            emit->ct_cur_obj, emit->ct_cur_raw_code,
+            #endif
+            emit->scope->scope_flags);
+    }
+}
+
+bool mp_emit_bc_last_emit_was_return_value(emit_t *emit) {
+    return emit->last_emit_was_return_value;
+}
+
+void mp_emit_bc_adjust_stack_size(emit_t *emit, mp_int_t delta) {
+    if (emit->pass == MP_PASS_SCOPE) {
+        return;
+    }
+    assert((mp_int_t)emit->stack_size + delta >= 0);
+    emit->stack_size += delta;
+    if (emit->stack_size > emit->scope->stack_size) {
+        emit->scope->stack_size = emit->stack_size;
+    }
+    emit->last_emit_was_return_value = false;
+}
+
+static inline void emit_bc_pre(emit_t *emit, mp_int_t stack_size_delta) {
+    mp_emit_bc_adjust_stack_size(emit, stack_size_delta);
+}
+
+void mp_emit_bc_set_source_line(emit_t *emit, mp_uint_t source_line) {
+    //printf("source: line %d -> %d  offset %d -> %d\n", emit->last_source_line, source_line, emit->last_source_line_offset, emit->bytecode_offset);
+#if MICROPY_ENABLE_SOURCE_LINE
+    if (MP_STATE_VM(mp_optimise_value) >= 3) {
+        // If we compile with -O3, don't store line numbers.
+        return;
+    }
+    if (source_line > emit->last_source_line) {
+        mp_uint_t bytes_to_skip = emit->bytecode_offset - emit->last_source_line_offset;
+        mp_uint_t lines_to_skip = source_line - emit->last_source_line;
+        emit_write_code_info_bytes_lines(emit, bytes_to_skip, lines_to_skip);
+        emit->last_source_line_offset = emit->bytecode_offset;
+        emit->last_source_line = source_line;
+    }
+#else
+    (void)emit;
+    (void)source_line;
+#endif
+}
+
+void mp_emit_bc_label_assign(emit_t *emit, mp_uint_t l) {
+    emit_bc_pre(emit, 0);
+    if (emit->pass == MP_PASS_SCOPE) {
+        return;
+    }
+    assert(l < emit->max_num_labels);
+    if (emit->pass < MP_PASS_EMIT) {
+        // assign label offset
+        assert(emit->label_offsets[l] == (mp_uint_t)-1);
+        emit->label_offsets[l] = emit->bytecode_offset;
+    } else {
+        // ensure label offset has not changed from MP_PASS_CODE_SIZE to MP_PASS_EMIT
+        //printf("l%d: (at %d vs %d)\n", l, emit->bytecode_offset, emit->label_offsets[l]);
+        assert(emit->label_offsets[l] == emit->bytecode_offset);
+    }
+}
+
+void mp_emit_bc_import_name(emit_t *emit, qstr qst) {
+    emit_bc_pre(emit, -1);
+    emit_write_bytecode_byte_qstr(emit, MP_BC_IMPORT_NAME, qst);
+}
+
+void mp_emit_bc_import_from(emit_t *emit, qstr qst) {
+    emit_bc_pre(emit, 1);
+    emit_write_bytecode_byte_qstr(emit, MP_BC_IMPORT_FROM, qst);
+}
+
+void mp_emit_bc_import_star(emit_t *emit) {
+    emit_bc_pre(emit, -1);
+    emit_write_bytecode_byte(emit, MP_BC_IMPORT_STAR);
+}
+
+void mp_emit_bc_load_const_tok(emit_t *emit, mp_token_kind_t tok) {
+    emit_bc_pre(emit, 1);
+    switch (tok) {
+        case MP_TOKEN_KW_FALSE: emit_write_bytecode_byte(emit, MP_BC_LOAD_CONST_FALSE); break;
+        case MP_TOKEN_KW_NONE: emit_write_bytecode_byte(emit, MP_BC_LOAD_CONST_NONE); break;
+        case MP_TOKEN_KW_TRUE: emit_write_bytecode_byte(emit, MP_BC_LOAD_CONST_TRUE); break;
+        default:
+            assert(tok == MP_TOKEN_ELLIPSIS);
+            emit_write_bytecode_byte_obj(emit, MP_BC_LOAD_CONST_OBJ, MP_OBJ_FROM_PTR(&mp_const_ellipsis_obj));
+            break;
+    }
+}
+
+void mp_emit_bc_load_const_small_int(emit_t *emit, mp_int_t arg) {
+    emit_bc_pre(emit, 1);
+    if (-16 <= arg && arg <= 47) {
+        emit_write_bytecode_byte(emit, MP_BC_LOAD_CONST_SMALL_INT_MULTI + 16 + arg);
+    } else {
+        emit_write_bytecode_byte_int(emit, MP_BC_LOAD_CONST_SMALL_INT, arg);
+    }
+}
+
+void mp_emit_bc_load_const_str(emit_t *emit, qstr qst) {
+    emit_bc_pre(emit, 1);
+    emit_write_bytecode_byte_qstr(emit, MP_BC_LOAD_CONST_STRING, qst);
+}
+
+void mp_emit_bc_load_const_obj(emit_t *emit, mp_obj_t obj) {
+    emit_bc_pre(emit, 1);
+    emit_write_bytecode_byte_obj(emit, MP_BC_LOAD_CONST_OBJ, obj);
+}
+
+void mp_emit_bc_load_null(emit_t *emit) {
+    emit_bc_pre(emit, 1);
+    emit_write_bytecode_byte(emit, MP_BC_LOAD_NULL);
+}
+
+void mp_emit_bc_load_fast(emit_t *emit, qstr qst, mp_uint_t local_num) {
+    (void)qst;
+    emit_bc_pre(emit, 1);
+    if (local_num <= 15) {
+        emit_write_bytecode_byte(emit, MP_BC_LOAD_FAST_MULTI + local_num);
+    } else {
+        emit_write_bytecode_byte_uint(emit, MP_BC_LOAD_FAST_N, local_num);
+    }
+}
+
+void mp_emit_bc_load_deref(emit_t *emit, qstr qst, mp_uint_t local_num) {
+    (void)qst;
+    emit_bc_pre(emit, 1);
+    emit_write_bytecode_byte_uint(emit, MP_BC_LOAD_DEREF, local_num);
+}
+
+void mp_emit_bc_load_name(emit_t *emit, qstr qst) {
+    (void)qst;
+    emit_bc_pre(emit, 1);
+    emit_write_bytecode_byte_qstr(emit, MP_BC_LOAD_NAME, qst);
+    if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC) {
+        emit_write_bytecode_byte(emit, 0);
+    }
+}
+
+void mp_emit_bc_load_global(emit_t *emit, qstr qst) {
+    (void)qst;
+    emit_bc_pre(emit, 1);
+    emit_write_bytecode_byte_qstr(emit, MP_BC_LOAD_GLOBAL, qst);
+    if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC) {
+        emit_write_bytecode_byte(emit, 0);
+    }
+}
+
+void mp_emit_bc_load_attr(emit_t *emit, qstr qst) {
+    emit_bc_pre(emit, 0);
+    emit_write_bytecode_byte_qstr(emit, MP_BC_LOAD_ATTR, qst);
+    if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC) {
+        emit_write_bytecode_byte(emit, 0);
+    }
+}
+
+void mp_emit_bc_load_method(emit_t *emit, qstr qst, bool is_super) {
+    emit_bc_pre(emit, 1 - 2 * is_super);
+    emit_write_bytecode_byte_qstr(emit, is_super ? MP_BC_LOAD_SUPER_METHOD : MP_BC_LOAD_METHOD, qst);
+}
+
+void mp_emit_bc_load_build_class(emit_t *emit) {
+    emit_bc_pre(emit, 1);
+    emit_write_bytecode_byte(emit, MP_BC_LOAD_BUILD_CLASS);
+}
+
+void mp_emit_bc_load_subscr(emit_t *emit) {
+    emit_bc_pre(emit, -1);
+    emit_write_bytecode_byte(emit, MP_BC_LOAD_SUBSCR);
+}
+
+void mp_emit_bc_store_fast(emit_t *emit, qstr qst, mp_uint_t local_num) {
+    (void)qst;
+    emit_bc_pre(emit, -1);
+    if (local_num <= 15) {
+        emit_write_bytecode_byte(emit, MP_BC_STORE_FAST_MULTI + local_num);
+    } else {
+        emit_write_bytecode_byte_uint(emit, MP_BC_STORE_FAST_N, local_num);
+    }
+}
+
+void mp_emit_bc_store_deref(emit_t *emit, qstr qst, mp_uint_t local_num) {
+    (void)qst;
+    emit_bc_pre(emit, -1);
+    emit_write_bytecode_byte_uint(emit, MP_BC_STORE_DEREF, local_num);
+}
+
+void mp_emit_bc_store_name(emit_t *emit, qstr qst) {
+    emit_bc_pre(emit, -1);
+    emit_write_bytecode_byte_qstr(emit, MP_BC_STORE_NAME, qst);
+}
+
+void mp_emit_bc_store_global(emit_t *emit, qstr qst) {
+    emit_bc_pre(emit, -1);
+    emit_write_bytecode_byte_qstr(emit, MP_BC_STORE_GLOBAL, qst);
+}
+
+void mp_emit_bc_store_attr(emit_t *emit, qstr qst) {
+    emit_bc_pre(emit, -2);
+    emit_write_bytecode_byte_qstr(emit, MP_BC_STORE_ATTR, qst);
+    if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC) {
+        emit_write_bytecode_byte(emit, 0);
+    }
+}
+
+void mp_emit_bc_store_subscr(emit_t *emit) {
+    emit_bc_pre(emit, -3);
+    emit_write_bytecode_byte(emit, MP_BC_STORE_SUBSCR);
+}
+
+void mp_emit_bc_delete_fast(emit_t *emit, qstr qst, mp_uint_t local_num) {
+    (void)qst;
+    emit_write_bytecode_byte_uint(emit, MP_BC_DELETE_FAST, local_num);
+}
+
+void mp_emit_bc_delete_deref(emit_t *emit, qstr qst, mp_uint_t local_num) {
+    (void)qst;
+    emit_write_bytecode_byte_uint(emit, MP_BC_DELETE_DEREF, local_num);
+}
+
+void mp_emit_bc_delete_name(emit_t *emit, qstr qst) {
+    emit_bc_pre(emit, 0);
+    emit_write_bytecode_byte_qstr(emit, MP_BC_DELETE_NAME, qst);
+}
+
+void mp_emit_bc_delete_global(emit_t *emit, qstr qst) {
+    emit_bc_pre(emit, 0);
+    emit_write_bytecode_byte_qstr(emit, MP_BC_DELETE_GLOBAL, qst);
+}
+
+void mp_emit_bc_delete_attr(emit_t *emit, qstr qst) {
+    mp_emit_bc_load_null(emit);
+    mp_emit_bc_rot_two(emit);
+    mp_emit_bc_store_attr(emit, qst);
+}
+
+void mp_emit_bc_delete_subscr(emit_t *emit) {
+    mp_emit_bc_load_null(emit);
+    mp_emit_bc_rot_three(emit);
+    mp_emit_bc_store_subscr(emit);
+}
+
+void mp_emit_bc_dup_top(emit_t *emit) {
+    emit_bc_pre(emit, 1);
+    emit_write_bytecode_byte(emit, MP_BC_DUP_TOP);
+}
+
+void mp_emit_bc_dup_top_two(emit_t *emit) {
+    emit_bc_pre(emit, 2);
+    emit_write_bytecode_byte(emit, MP_BC_DUP_TOP_TWO);
+}
+
+void mp_emit_bc_pop_top(emit_t *emit) {
+    emit_bc_pre(emit, -1);
+    emit_write_bytecode_byte(emit, MP_BC_POP_TOP);
+}
+
+void mp_emit_bc_rot_two(emit_t *emit) {
+    emit_bc_pre(emit, 0);
+    emit_write_bytecode_byte(emit, MP_BC_ROT_TWO);
+}
+
+void mp_emit_bc_rot_three(emit_t *emit) {
+    emit_bc_pre(emit, 0);
+    emit_write_bytecode_byte(emit, MP_BC_ROT_THREE);
+}
+
+void mp_emit_bc_jump(emit_t *emit, mp_uint_t label) {
+    emit_bc_pre(emit, 0);
+    emit_write_bytecode_byte_signed_label(emit, MP_BC_JUMP, label);
+}
+
+void mp_emit_bc_pop_jump_if(emit_t *emit, bool cond, mp_uint_t label) {
+    emit_bc_pre(emit, -1);
+    if (cond) {
+        emit_write_bytecode_byte_signed_label(emit, MP_BC_POP_JUMP_IF_TRUE, label);
+    } else {
+        emit_write_bytecode_byte_signed_label(emit, MP_BC_POP_JUMP_IF_FALSE, label);
+    }
+}
+
+void mp_emit_bc_jump_if_or_pop(emit_t *emit, bool cond, mp_uint_t label) {
+    emit_bc_pre(emit, -1);
+    if (cond) {
+        emit_write_bytecode_byte_signed_label(emit, MP_BC_JUMP_IF_TRUE_OR_POP, label);
+    } else {
+        emit_write_bytecode_byte_signed_label(emit, MP_BC_JUMP_IF_FALSE_OR_POP, label);
+    }
+}
+
+void mp_emit_bc_unwind_jump(emit_t *emit, mp_uint_t label, mp_uint_t except_depth) {
+    if (except_depth == 0) {
+        emit_bc_pre(emit, 0);
+        if (label & MP_EMIT_BREAK_FROM_FOR) {
+            // need to pop the iterator if we are breaking out of a for loop
+            emit_write_bytecode_byte(emit, MP_BC_POP_TOP);
+            // also pop the iter_buf
+            for (size_t i = 0; i < MP_OBJ_ITER_BUF_NSLOTS - 1; ++i) {
+                emit_write_bytecode_byte(emit, MP_BC_POP_TOP);
+            }
+        }
+        emit_write_bytecode_byte_signed_label(emit, MP_BC_JUMP, label & ~MP_EMIT_BREAK_FROM_FOR);
+    } else {
+        emit_write_bytecode_byte_signed_label(emit, MP_BC_UNWIND_JUMP, label & ~MP_EMIT_BREAK_FROM_FOR);
+        emit_write_bytecode_byte(emit, ((label & MP_EMIT_BREAK_FROM_FOR) ? 0x80 : 0) | except_depth);
+    }
+}
+
+void mp_emit_bc_setup_with(emit_t *emit, mp_uint_t label) {
+    // The SETUP_WITH opcode pops ctx_mgr from the top of the stack
+    // and then pushes 3 entries: __exit__, ctx_mgr, as_value.
+    emit_bc_pre(emit, 2);
+    emit_write_bytecode_byte_unsigned_label(emit, MP_BC_SETUP_WITH, label);
+}
+
+void mp_emit_bc_with_cleanup(emit_t *emit, mp_uint_t label) {
+    mp_emit_bc_pop_block(emit);
+    mp_emit_bc_load_const_tok(emit, MP_TOKEN_KW_NONE);
+    mp_emit_bc_label_assign(emit, label);
+    emit_bc_pre(emit, 2); // ensure we have enough stack space to call the __exit__ method
+    emit_write_bytecode_byte(emit, MP_BC_WITH_CLEANUP);
+    emit_bc_pre(emit, -4); // cancel the 2 above, plus the 2 from mp_emit_bc_setup_with
+}
+
+void mp_emit_bc_setup_except(emit_t *emit, mp_uint_t label) {
+    emit_bc_pre(emit, 0);
+    emit_write_bytecode_byte_unsigned_label(emit, MP_BC_SETUP_EXCEPT, label);
+}
+
+void mp_emit_bc_setup_finally(emit_t *emit, mp_uint_t label) {
+    emit_bc_pre(emit, 0);
+    emit_write_bytecode_byte_unsigned_label(emit, MP_BC_SETUP_FINALLY, label);
+}
+
+void mp_emit_bc_end_finally(emit_t *emit) {
+    emit_bc_pre(emit, -1);
+    emit_write_bytecode_byte(emit, MP_BC_END_FINALLY);
+}
+
+void mp_emit_bc_get_iter(emit_t *emit, bool use_stack) {
+    emit_bc_pre(emit, use_stack ? MP_OBJ_ITER_BUF_NSLOTS - 1 : 0);
+    emit_write_bytecode_byte(emit, use_stack ? MP_BC_GET_ITER_STACK : MP_BC_GET_ITER);
+}
+
+void mp_emit_bc_for_iter(emit_t *emit, mp_uint_t label) {
+    emit_bc_pre(emit, 1);
+    emit_write_bytecode_byte_unsigned_label(emit, MP_BC_FOR_ITER, label);
+}
+
+void mp_emit_bc_for_iter_end(emit_t *emit) {
+    emit_bc_pre(emit, -MP_OBJ_ITER_BUF_NSLOTS);
+}
+
+void mp_emit_bc_pop_block(emit_t *emit) {
+    emit_bc_pre(emit, 0);
+    emit_write_bytecode_byte(emit, MP_BC_POP_BLOCK);
+}
+
+void mp_emit_bc_pop_except(emit_t *emit) {
+    emit_bc_pre(emit, 0);
+    emit_write_bytecode_byte(emit, MP_BC_POP_EXCEPT);
+}
+
+void mp_emit_bc_unary_op(emit_t *emit, mp_unary_op_t op) {
+    emit_bc_pre(emit, 0);
+    emit_write_bytecode_byte(emit, MP_BC_UNARY_OP_MULTI + op);
+}
+
+void mp_emit_bc_binary_op(emit_t *emit, mp_binary_op_t op) {
+    bool invert = false;
+    if (op == MP_BINARY_OP_NOT_IN) {
+        invert = true;
+        op = MP_BINARY_OP_IN;
+    } else if (op == MP_BINARY_OP_IS_NOT) {
+        invert = true;
+        op = MP_BINARY_OP_IS;
+    }
+    emit_bc_pre(emit, -1);
+    emit_write_bytecode_byte(emit, MP_BC_BINARY_OP_MULTI + op);
+    if (invert) {
+        emit_bc_pre(emit, 0);
+        emit_write_bytecode_byte(emit, MP_BC_UNARY_OP_MULTI + MP_UNARY_OP_NOT);
+    }
+}
+
+void mp_emit_bc_build_tuple(emit_t *emit, mp_uint_t n_args) {
+    emit_bc_pre(emit, 1 - n_args);
+    emit_write_bytecode_byte_uint(emit, MP_BC_BUILD_TUPLE, n_args);
+}
+
+void mp_emit_bc_build_list(emit_t *emit, mp_uint_t n_args) {
+    emit_bc_pre(emit, 1 - n_args);
+    emit_write_bytecode_byte_uint(emit, MP_BC_BUILD_LIST, n_args);
+}
+
+void mp_emit_bc_build_map(emit_t *emit, mp_uint_t n_args) {
+    emit_bc_pre(emit, 1);
+    emit_write_bytecode_byte_uint(emit, MP_BC_BUILD_MAP, n_args);
+}
+
+void mp_emit_bc_store_map(emit_t *emit) {
+    emit_bc_pre(emit, -2);
+    emit_write_bytecode_byte(emit, MP_BC_STORE_MAP);
+}
+
+#if MICROPY_PY_BUILTINS_SET
+void mp_emit_bc_build_set(emit_t *emit, mp_uint_t n_args) {
+    emit_bc_pre(emit, 1 - n_args);
+    emit_write_bytecode_byte_uint(emit, MP_BC_BUILD_SET, n_args);
+}
+#endif
+
+#if MICROPY_PY_BUILTINS_SLICE
+void mp_emit_bc_build_slice(emit_t *emit, mp_uint_t n_args) {
+    emit_bc_pre(emit, 1 - n_args);
+    emit_write_bytecode_byte_uint(emit, MP_BC_BUILD_SLICE, n_args);
+}
+#endif
+
+void mp_emit_bc_store_comp(emit_t *emit, scope_kind_t kind, mp_uint_t collection_stack_index) {
+    int t;
+    int n;
+    if (kind == SCOPE_LIST_COMP) {
+        n = 0;
+        t = 0;
+    } else if (!MICROPY_PY_BUILTINS_SET || kind == SCOPE_DICT_COMP) {
+        n = 1;
+        t = 1;
+    } else if (MICROPY_PY_BUILTINS_SET) {
+        n = 0;
+        t = 2;
+    }
+    emit_bc_pre(emit, -1 - n);
+    // the lower 2 bits of the opcode argument indicate the collection type
+    emit_write_bytecode_byte_uint(emit, MP_BC_STORE_COMP, ((collection_stack_index + n) << 2) | t);
+}
+
+void mp_emit_bc_unpack_sequence(emit_t *emit, mp_uint_t n_args) {
+    emit_bc_pre(emit, -1 + n_args);
+    emit_write_bytecode_byte_uint(emit, MP_BC_UNPACK_SEQUENCE, n_args);
+}
+
+void mp_emit_bc_unpack_ex(emit_t *emit, mp_uint_t n_left, mp_uint_t n_right) {
+    emit_bc_pre(emit, -1 + n_left + n_right + 1);
+    emit_write_bytecode_byte_uint(emit, MP_BC_UNPACK_EX, n_left | (n_right << 8));
+}
+
+void mp_emit_bc_make_function(emit_t *emit, scope_t *scope, mp_uint_t n_pos_defaults, mp_uint_t n_kw_defaults) {
+    if (n_pos_defaults == 0 && n_kw_defaults == 0) {
+        emit_bc_pre(emit, 1);
+        emit_write_bytecode_byte_raw_code(emit, MP_BC_MAKE_FUNCTION, scope->raw_code);
+    } else {
+        emit_bc_pre(emit, -1);
+        emit_write_bytecode_byte_raw_code(emit, MP_BC_MAKE_FUNCTION_DEFARGS, scope->raw_code);
+    }
+}
+
+void mp_emit_bc_make_closure(emit_t *emit, scope_t *scope, mp_uint_t n_closed_over, mp_uint_t n_pos_defaults, mp_uint_t n_kw_defaults) {
+    if (n_pos_defaults == 0 && n_kw_defaults == 0) {
+        emit_bc_pre(emit, -n_closed_over + 1);
+        emit_write_bytecode_byte_raw_code(emit, MP_BC_MAKE_CLOSURE, scope->raw_code);
+        emit_write_bytecode_byte(emit, n_closed_over);
+    } else {
+        assert(n_closed_over <= 255);
+        emit_bc_pre(emit, -2 - (mp_int_t)n_closed_over + 1);
+        emit_write_bytecode_byte_raw_code(emit, MP_BC_MAKE_CLOSURE_DEFARGS, scope->raw_code);
+        emit_write_bytecode_byte(emit, n_closed_over);
+    }
+}
+
+STATIC void emit_bc_call_function_method_helper(emit_t *emit, mp_int_t stack_adj, mp_uint_t bytecode_base, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags) {
+    if (star_flags) {
+        emit_bc_pre(emit, stack_adj - (mp_int_t)n_positional - 2 * (mp_int_t)n_keyword - 2);
+        emit_write_bytecode_byte_uint(emit, bytecode_base + 1, (n_keyword << 8) | n_positional); // TODO make it 2 separate uints?
+    } else {
+        emit_bc_pre(emit, stack_adj - (mp_int_t)n_positional - 2 * (mp_int_t)n_keyword);
+        emit_write_bytecode_byte_uint(emit, bytecode_base, (n_keyword << 8) | n_positional); // TODO make it 2 separate uints?
+    }
+}
+
+void mp_emit_bc_call_function(emit_t *emit, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags) {
+    emit_bc_call_function_method_helper(emit, 0, MP_BC_CALL_FUNCTION, n_positional, n_keyword, star_flags);
+}
+
+void mp_emit_bc_call_method(emit_t *emit, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags) {
+    emit_bc_call_function_method_helper(emit, -1, MP_BC_CALL_METHOD, n_positional, n_keyword, star_flags);
+}
+
+void mp_emit_bc_return_value(emit_t *emit) {
+    emit_bc_pre(emit, -1);
+    emit->last_emit_was_return_value = true;
+    emit_write_bytecode_byte(emit, MP_BC_RETURN_VALUE);
+}
+
+void mp_emit_bc_raise_varargs(emit_t *emit, mp_uint_t n_args) {
+    assert(n_args <= 2);
+    emit_bc_pre(emit, -n_args);
+    emit_write_bytecode_byte_byte(emit, MP_BC_RAISE_VARARGS, n_args);
+}
+
+void mp_emit_bc_yield_value(emit_t *emit) {
+    emit_bc_pre(emit, 0);
+    emit->scope->scope_flags |= MP_SCOPE_FLAG_GENERATOR;
+    emit_write_bytecode_byte(emit, MP_BC_YIELD_VALUE);
+}
+
+void mp_emit_bc_yield_from(emit_t *emit) {
+    emit_bc_pre(emit, -1);
+    emit->scope->scope_flags |= MP_SCOPE_FLAG_GENERATOR;
+    emit_write_bytecode_byte(emit, MP_BC_YIELD_FROM);
+}
+
+void mp_emit_bc_start_except_handler(emit_t *emit) {
+    mp_emit_bc_adjust_stack_size(emit, 4); // stack adjust for the exception instance, +3 for possible UNWIND_JUMP state
+}
+
+void mp_emit_bc_end_except_handler(emit_t *emit) {
+    mp_emit_bc_adjust_stack_size(emit, -3); // stack adjust
+}
+
+#if MICROPY_EMIT_NATIVE
+const emit_method_table_t emit_bc_method_table = {
+    NULL, // set_native_type is never called when emitting bytecode
+    mp_emit_bc_start_pass,
+    mp_emit_bc_end_pass,
+    mp_emit_bc_last_emit_was_return_value,
+    mp_emit_bc_adjust_stack_size,
+    mp_emit_bc_set_source_line,
+
+    {
+        mp_emit_bc_load_fast,
+        mp_emit_bc_load_deref,
+        mp_emit_bc_load_name,
+        mp_emit_bc_load_global,
+    },
+    {
+        mp_emit_bc_store_fast,
+        mp_emit_bc_store_deref,
+        mp_emit_bc_store_name,
+        mp_emit_bc_store_global,
+    },
+    {
+        mp_emit_bc_delete_fast,
+        mp_emit_bc_delete_deref,
+        mp_emit_bc_delete_name,
+        mp_emit_bc_delete_global,
+    },
+
+    mp_emit_bc_label_assign,
+    mp_emit_bc_import_name,
+    mp_emit_bc_import_from,
+    mp_emit_bc_import_star,
+    mp_emit_bc_load_const_tok,
+    mp_emit_bc_load_const_small_int,
+    mp_emit_bc_load_const_str,
+    mp_emit_bc_load_const_obj,
+    mp_emit_bc_load_null,
+    mp_emit_bc_load_attr,
+    mp_emit_bc_load_method,
+    mp_emit_bc_load_build_class,
+    mp_emit_bc_load_subscr,
+    mp_emit_bc_store_attr,
+    mp_emit_bc_store_subscr,
+    mp_emit_bc_delete_attr,
+    mp_emit_bc_delete_subscr,
+    mp_emit_bc_dup_top,
+    mp_emit_bc_dup_top_two,
+    mp_emit_bc_pop_top,
+    mp_emit_bc_rot_two,
+    mp_emit_bc_rot_three,
+    mp_emit_bc_jump,
+    mp_emit_bc_pop_jump_if,
+    mp_emit_bc_jump_if_or_pop,
+    mp_emit_bc_unwind_jump,
+    mp_emit_bc_unwind_jump,
+    mp_emit_bc_setup_with,
+    mp_emit_bc_with_cleanup,
+    mp_emit_bc_setup_except,
+    mp_emit_bc_setup_finally,
+    mp_emit_bc_end_finally,
+    mp_emit_bc_get_iter,
+    mp_emit_bc_for_iter,
+    mp_emit_bc_for_iter_end,
+    mp_emit_bc_pop_block,
+    mp_emit_bc_pop_except,
+    mp_emit_bc_unary_op,
+    mp_emit_bc_binary_op,
+    mp_emit_bc_build_tuple,
+    mp_emit_bc_build_list,
+    mp_emit_bc_build_map,
+    mp_emit_bc_store_map,
+    #if MICROPY_PY_BUILTINS_SET
+    mp_emit_bc_build_set,
+    #endif
+    #if MICROPY_PY_BUILTINS_SLICE
+    mp_emit_bc_build_slice,
+    #endif
+    mp_emit_bc_store_comp,
+    mp_emit_bc_unpack_sequence,
+    mp_emit_bc_unpack_ex,
+    mp_emit_bc_make_function,
+    mp_emit_bc_make_closure,
+    mp_emit_bc_call_function,
+    mp_emit_bc_call_method,
+    mp_emit_bc_return_value,
+    mp_emit_bc_raise_varargs,
+    mp_emit_bc_yield_value,
+    mp_emit_bc_yield_from,
+
+    mp_emit_bc_start_except_handler,
+    mp_emit_bc_end_except_handler,
+};
+#else
+const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_load_id_ops = {
+    mp_emit_bc_load_fast,
+    mp_emit_bc_load_deref,
+    mp_emit_bc_load_name,
+    mp_emit_bc_load_global,
+};
+
+const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_store_id_ops = {
+    mp_emit_bc_store_fast,
+    mp_emit_bc_store_deref,
+    mp_emit_bc_store_name,
+    mp_emit_bc_store_global,
+};
+
+const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_delete_id_ops = {
+    mp_emit_bc_delete_fast,
+    mp_emit_bc_delete_deref,
+    mp_emit_bc_delete_name,
+    mp_emit_bc_delete_global,
+};
+#endif
+
+#endif //MICROPY_ENABLE_COMPILER

+ 77 - 0
py/emitcommon.c

@@ -0,0 +1,77 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <assert.h>
+
+#include "py/emit.h"
+
+#if MICROPY_ENABLE_COMPILER
+
+void mp_emit_common_get_id_for_load(scope_t *scope, qstr qst) {
+    // name adding/lookup
+    bool added;
+    id_info_t *id = scope_find_or_add_id(scope, qst, &added);
+    if (added) {
+        scope_find_local_and_close_over(scope, id, qst);
+    }
+}
+
+void mp_emit_common_get_id_for_modification(scope_t *scope, qstr qst) {
+    // name adding/lookup
+    bool added;
+    id_info_t *id = scope_find_or_add_id(scope, qst, &added);
+    if (added) {
+        if (SCOPE_IS_FUNC_LIKE(scope->kind)) {
+            id->kind = ID_INFO_KIND_LOCAL;
+        } else {
+            id->kind = ID_INFO_KIND_GLOBAL_IMPLICIT;
+        }
+    } else if (SCOPE_IS_FUNC_LIKE(scope->kind) && id->kind == ID_INFO_KIND_GLOBAL_IMPLICIT) {
+        // rebind as a local variable
+        id->kind = ID_INFO_KIND_LOCAL;
+    }
+}
+
+void mp_emit_common_id_op(emit_t *emit, const mp_emit_method_table_id_ops_t *emit_method_table, scope_t *scope, qstr qst) {
+    // assumes pass is greater than 1, ie that all identifiers are defined in the scope
+
+    id_info_t *id = scope_find(scope, qst);
+    assert(id != NULL);
+
+    // call the emit backend with the correct code
+    if (id->kind == ID_INFO_KIND_GLOBAL_IMPLICIT) {
+        emit_method_table->name(emit, qst);
+    } else if (id->kind == ID_INFO_KIND_GLOBAL_EXPLICIT) {
+        emit_method_table->global(emit, qst);
+    } else if (id->kind == ID_INFO_KIND_LOCAL) {
+        emit_method_table->fast(emit, qst, id->local_num);
+    } else {
+        assert(id->kind == ID_INFO_KIND_CELL || id->kind == ID_INFO_KIND_FREE);
+        emit_method_table->deref(emit, qst, id->local_num);
+    }
+}
+
+#endif // MICROPY_ENABLE_COMPILER

+ 170 - 0
py/emitglue.c

@@ -0,0 +1,170 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+// This code glues the code emitters to the runtime.
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "py/emitglue.h"
+#include "py/runtime0.h"
+#include "py/bc.h"
+
+#if MICROPY_DEBUG_VERBOSE // 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
+
+#if MICROPY_DEBUG_PRINTERS
+mp_uint_t mp_verbose_flag = 0;
+#endif
+
+mp_raw_code_t *mp_emit_glue_new_raw_code(void) {
+    mp_raw_code_t *rc = m_new0(mp_raw_code_t, 1);
+    rc->kind = MP_CODE_RESERVED;
+    return rc;
+}
+
+void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code, mp_uint_t len,
+    const mp_uint_t *const_table,
+    #if MICROPY_PERSISTENT_CODE_SAVE
+    uint16_t n_obj, uint16_t n_raw_code,
+    #endif
+    mp_uint_t scope_flags) {
+
+    rc->kind = MP_CODE_BYTECODE;
+    rc->scope_flags = scope_flags;
+    rc->data.u_byte.bytecode = code;
+    rc->data.u_byte.const_table = const_table;
+    #if MICROPY_PERSISTENT_CODE_SAVE
+    rc->data.u_byte.bc_len = len;
+    rc->data.u_byte.n_obj = n_obj;
+    rc->data.u_byte.n_raw_code = n_raw_code;
+    #endif
+
+#ifdef DEBUG_PRINT
+    DEBUG_printf("assign byte code: code=%p len=" UINT_FMT " flags=%x\n", code, len, (uint)scope_flags);
+#endif
+#if MICROPY_DEBUG_PRINTERS
+    if (mp_verbose_flag >= 2) {
+        mp_bytecode_print(rc, code, len, const_table);
+    }
+#endif
+}
+
+#if MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_ASM
+void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void *fun_data, mp_uint_t fun_len, const mp_uint_t *const_table, mp_uint_t n_pos_args, mp_uint_t scope_flags, mp_uint_t type_sig) {
+    assert(kind == MP_CODE_NATIVE_PY || kind == MP_CODE_NATIVE_VIPER || kind == MP_CODE_NATIVE_ASM);
+    rc->kind = kind;
+    rc->scope_flags = scope_flags;
+    rc->n_pos_args = n_pos_args;
+    rc->data.u_native.fun_data = fun_data;
+    rc->data.u_native.const_table = const_table;
+    rc->data.u_native.type_sig = type_sig;
+
+#ifdef DEBUG_PRINT
+    DEBUG_printf("assign native: kind=%d fun=%p len=" UINT_FMT " n_pos_args=" UINT_FMT " flags=%x\n", kind, fun_data, fun_len, n_pos_args, (uint)scope_flags);
+    for (mp_uint_t i = 0; i < fun_len; i++) {
+        if (i > 0 && i % 16 == 0) {
+            DEBUG_printf("\n");
+        }
+        DEBUG_printf(" %02x", ((byte*)fun_data)[i]);
+    }
+    DEBUG_printf("\n");
+
+#ifdef WRITE_CODE
+    FILE *fp_write_code = fopen("out-code", "wb");
+    fwrite(fun_data, fun_len, 1, fp_write_code);
+    fclose(fp_write_code);
+#endif
+#else
+    (void)fun_len;
+#endif
+}
+#endif
+
+mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, mp_obj_t def_args, mp_obj_t def_kw_args) {
+    DEBUG_OP_printf("make_function_from_raw_code %p\n", rc);
+    assert(rc != NULL);
+
+    // def_args must be MP_OBJ_NULL or a tuple
+    assert(def_args == MP_OBJ_NULL || MP_OBJ_IS_TYPE(def_args, &mp_type_tuple));
+
+    // def_kw_args must be MP_OBJ_NULL or a dict
+    assert(def_kw_args == MP_OBJ_NULL || MP_OBJ_IS_TYPE(def_kw_args, &mp_type_dict));
+
+    // make the function, depending on the raw code kind
+    mp_obj_t fun;
+    switch (rc->kind) {
+        #if MICROPY_EMIT_NATIVE
+        case MP_CODE_NATIVE_PY:
+            fun = mp_obj_new_fun_native(def_args, def_kw_args, rc->data.u_native.fun_data, rc->data.u_native.const_table);
+            break;
+        case MP_CODE_NATIVE_VIPER:
+            fun = mp_obj_new_fun_viper(rc->n_pos_args, rc->data.u_native.fun_data, rc->data.u_native.type_sig);
+            break;
+        #endif
+        #if MICROPY_EMIT_INLINE_ASM
+        case MP_CODE_NATIVE_ASM:
+            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:
+            // rc->kind should always be set and BYTECODE is the only remaining case
+            assert(rc->kind == MP_CODE_BYTECODE);
+            fun = mp_obj_new_fun_bc(def_args, def_kw_args, rc->data.u_byte.bytecode, rc->data.u_byte.const_table);
+            break;
+    }
+
+    // check for generator functions and if so wrap in generator object
+    if ((rc->scope_flags & MP_SCOPE_FLAG_GENERATOR) != 0) {
+        fun = mp_obj_new_gen_wrap(fun);
+    }
+
+    return fun;
+}
+
+mp_obj_t mp_make_closure_from_raw_code(const mp_raw_code_t *rc, mp_uint_t n_closed_over, const mp_obj_t *args) {
+    DEBUG_OP_printf("make_closure_from_raw_code %p " UINT_FMT " %p\n", rc, n_closed_over, args);
+    // make function object
+    mp_obj_t ffun;
+    if (n_closed_over & 0x100) {
+        // default positional and keyword args given
+        ffun = mp_make_function_from_raw_code(rc, args[0], args[1]);
+    } else {
+        // default positional and keyword args not given
+        ffun = mp_make_function_from_raw_code(rc, MP_OBJ_NULL, MP_OBJ_NULL);
+    }
+    // wrap function in closure object
+    return mp_obj_new_closure(ffun, n_closed_over & 0xff, args + ((n_closed_over >> 7) & 2));
+}

+ 77 - 0
py/emitglue.h

@@ -0,0 +1,77 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_PY_EMITGLUE_H
+#define MICROPY_INCLUDED_PY_EMITGLUE_H
+
+#include "py/obj.h"
+
+// These variables and functions glue the code emitters to the runtime.
+
+typedef enum {
+    MP_CODE_UNUSED,
+    MP_CODE_RESERVED,
+    MP_CODE_BYTECODE,
+    MP_CODE_NATIVE_PY,
+    MP_CODE_NATIVE_VIPER,
+    MP_CODE_NATIVE_ASM,
+} mp_raw_code_kind_t;
+
+typedef struct _mp_raw_code_t {
+    mp_raw_code_kind_t kind : 3;
+    mp_uint_t scope_flags : 7;
+    mp_uint_t n_pos_args : 11;
+    union {
+        struct {
+            const byte *bytecode;
+            const mp_uint_t *const_table;
+            #if MICROPY_PERSISTENT_CODE_SAVE
+            mp_uint_t bc_len;
+            uint16_t n_obj;
+            uint16_t n_raw_code;
+            #endif
+        } u_byte;
+        struct {
+            void *fun_data;
+            const mp_uint_t *const_table;
+            mp_uint_t type_sig; // for viper, compressed as 2-bit types; ret is MSB, then arg0, arg1, etc
+        } u_native;
+    } data;
+} mp_raw_code_t;
+
+mp_raw_code_t *mp_emit_glue_new_raw_code(void);
+
+void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code, mp_uint_t len,
+    const mp_uint_t *const_table,
+    #if MICROPY_PERSISTENT_CODE_SAVE
+    uint16_t n_obj, uint16_t n_raw_code,
+    #endif
+    mp_uint_t scope_flags);
+void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void *fun_data, mp_uint_t fun_len, const mp_uint_t *const_table, mp_uint_t n_pos_args, mp_uint_t scope_flags, mp_uint_t type_sig);
+
+mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, mp_obj_t def_args, mp_obj_t def_kw_args);
+mp_obj_t mp_make_closure_from_raw_code(const mp_raw_code_t *rc, mp_uint_t n_closed_over, const mp_obj_t *args);
+
+#endif // MICROPY_INCLUDED_PY_EMITGLUE_H

Некоторые файлы не были показаны из-за большого количества измененных файлов