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

【更新】MicroPython 源码至 1.9.4(gafecc12) 最新版本。

Signed-off-by: armink <armink.ztl@gmail.com>
armink 7 лет назад
Родитель
Сommit
15dfa7c635
100 измененных файлов с 4581 добавлено и 1726 удалено
  1. 3 3
      extmod/machine_i2c.c
  2. 1 1
      extmod/modbtree.c
  3. 162 0
      extmod/modonewire.c
  4. 292 0
      extmod/moducryptolib.c
  5. 71 8
      extmod/moductypes.c
  6. 218 80
      extmod/moduhashlib.c
  7. 1 3
      extmod/modujson.c
  8. 203 0
      extmod/modure.c
  9. 23 23
      extmod/moduselect.c
  10. 17 18
      extmod/modussl_axtls.c
  11. 23 28
      extmod/modussl_mbedtls.c
  12. 2 1
      extmod/moduzlib.c
  13. 20 13
      extmod/modwebrepl.c
  14. 8 10
      extmod/modwebsocket.c
  15. 19 28
      extmod/uos_dupterm.c
  16. 4 2
      extmod/uzlib/tinflate.c
  17. 39 10
      extmod/vfs.c
  18. 5 0
      extmod/vfs.h
  19. 8 1
      extmod/vfs_fat.c
  20. 2 1
      extmod/vfs_fat.h
  21. 66 122
      extmod/vfs_fat_diskio.c
  22. 19 27
      extmod/vfs_fat_file.c
  23. 371 0
      extmod/vfs_posix.c
  24. 38 0
      extmod/vfs_posix.h
  25. 261 0
      extmod/vfs_posix_file.c
  26. 0 2
      lib/oofatfs/ff.h
  27. 124 0
      lib/utils/mpirq.c
  28. 82 0
      lib/utils/mpirq.h
  29. 12 16
      lib/utils/printf.c
  30. 10 8
      lib/utils/pyexec.c
  31. 1 0
      lib/utils/pyexec.h
  32. 26 0
      lib/utils/stdout_helpers.c
  33. 163 0
      lib/utils/sys_stdio_mphal.c
  34. 3 9
      port/genhdr/mpversion.h
  35. 12 0
      port/genhdr/qstrdefs.generated.h
  36. 1 0
      port/mpconfigport.h
  37. 0 23
      port/mphalport.c
  38. 8 0
      port/mpy_main.c
  39. 7 2
      py/argcheck.c
  40. 23 12
      py/asmarm.c
  41. 14 5
      py/asmarm.h
  42. 48 37
      py/asmthumb.c
  43. 37 7
      py/asmthumb.h
  44. 55 55
      py/asmx64.c
  45. 25 8
      py/asmx64.h
  46. 68 54
      py/asmx86.c
  47. 25 8
      py/asmx86.h
  48. 65 16
      py/asmxtensa.c
  49. 20 12
      py/asmxtensa.h
  50. 9 7
      py/bc.c
  51. 1 0
      py/builtin.h
  52. 0 4
      py/builtinevex.c
  53. 4 4
      py/builtinhelp.c
  54. 1 1
      py/builtinimport.c
  55. 242 230
      py/compile.c
  56. 0 9
      py/compile.h
  57. 70 76
      py/emit.h
  58. 111 192
      py/emitbc.c
  59. 6 22
      py/emitcommon.c
  60. 12 8
      py/emitglue.c
  61. 9 0
      py/emitglue.h
  62. 1 1
      py/emitinlinethumb.c
  63. 1 1
      py/emitinlinextensa.c
  64. 20 0
      py/emitnarm.c
  65. 609 337
      py/emitnative.c
  66. 20 0
      py/emitnthumb.c
  67. 20 0
      py/emitnx64.c
  68. 76 0
      py/emitnx86.c
  69. 20 0
      py/emitnxtensa.c
  70. 17 4
      py/gc.c
  71. 8 1
      py/gc.h
  72. 11 11
      py/grammar.h
  73. 2 0
      py/lexer.c
  74. 3 0
      py/makeqstrdata.py
  75. 4 20
      py/makeversionhdr.py
  76. 8 0
      py/malloc.c
  77. 4 4
      py/map.c
  78. 3 0
      py/misc.h
  79. 7 6
      py/mkrules.mk
  80. 51 11
      py/modbuiltins.c
  81. 69 0
      py/modio.c
  82. 69 2
      py/modmath.c
  83. 4 0
      py/modmicropython.c
  84. 5 4
      py/modsys.c
  85. 136 12
      py/mpconfig.h
  86. 1 1
      py/mperrno.h
  87. 24 15
      py/mpstate.h
  88. 1 1
      py/mpz.c
  89. 59 3
      py/nativeglue.c
  90. 2 1
      py/nlrthumb.c
  91. 21 5
      py/nlrx86.c
  92. 9 13
      py/obj.c
  93. 31 24
      py/obj.h
  94. 20 15
      py/objarray.c
  95. 10 0
      py/objarray.h
  96. 3 4
      py/objboundmeth.c
  97. 2 2
      py/objcomplex.c
  98. 2 1
      py/objdeque.c
  99. 5 7
      py/objdict.c
  100. 53 14
      py/objexcept.c

+ 3 - 3
extmod/machine_i2c.c

@@ -54,7 +54,7 @@ STATIC int mp_hal_i2c_scl_release(machine_i2c_obj_t *self) {
     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);
+        mp_hal_delay_us_fast(1);
     }
     if (count == 0) {
         return -MP_ETIMEDOUT;
@@ -307,11 +307,11 @@ STATIC mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, s
     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;
+    return MP_OBJ_FROM_PTR(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);
+    machine_i2c_obj_init_helper(MP_OBJ_TO_PTR(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);

+ 1 - 1
extmod/modbtree.c

@@ -352,7 +352,7 @@ STATIC mp_obj_t mod_btree_open(size_t n_args, const mp_obj_t *pos_args, mp_map_t
     openinfo.psize = args.pagesize.u_int;
     openinfo.minkeypage = args.minkeypage.u_int;
 
-    DB *db = __bt_open(pos_args[0], &btree_stream_fvtable, &openinfo, /*dflags*/0);
+    DB *db = __bt_open(MP_OBJ_TO_PTR(pos_args[0]), &btree_stream_fvtable, &openinfo, /*dflags*/0);
     if (db == NULL) {
         mp_raise_OSError(errno);
     }

+ 162 - 0
extmod/modonewire.c

@@ -0,0 +1,162 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015-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 <stdint.h>
+
+#include "py/obj.h"
+#include "py/mphal.h"
+
+/******************************************************************************/
+// Low-level 1-Wire routines
+
+#define TIMING_RESET1 (480)
+#define TIMING_RESET2 (70)
+#define TIMING_RESET3 (410)
+#define TIMING_READ1 (5)
+#define TIMING_READ2 (5)
+#define TIMING_READ3 (40)
+#define TIMING_WRITE1 (10)
+#define TIMING_WRITE2 (50)
+#define TIMING_WRITE3 (10)
+
+STATIC int onewire_bus_reset(mp_hal_pin_obj_t pin) {
+    mp_hal_pin_write(pin, 0);
+    mp_hal_delay_us(TIMING_RESET1);
+    uint32_t i = mp_hal_quiet_timing_enter();
+    mp_hal_pin_write(pin, 1);
+    mp_hal_delay_us_fast(TIMING_RESET2);
+    int status = !mp_hal_pin_read(pin);
+    mp_hal_quiet_timing_exit(i);
+    mp_hal_delay_us(TIMING_RESET3);
+    return status;
+}
+
+STATIC int onewire_bus_readbit(mp_hal_pin_obj_t pin) {
+    mp_hal_pin_write(pin, 1);
+    uint32_t i = mp_hal_quiet_timing_enter();
+    mp_hal_pin_write(pin, 0);
+    mp_hal_delay_us_fast(TIMING_READ1);
+    mp_hal_pin_write(pin, 1);
+    mp_hal_delay_us_fast(TIMING_READ2);
+    int value = mp_hal_pin_read(pin);
+    mp_hal_quiet_timing_exit(i);
+    mp_hal_delay_us_fast(TIMING_READ3);
+    return value;
+}
+
+STATIC void onewire_bus_writebit(mp_hal_pin_obj_t pin, int value) {
+    uint32_t i = mp_hal_quiet_timing_enter();
+    mp_hal_pin_write(pin, 0);
+    mp_hal_delay_us_fast(TIMING_WRITE1);
+    if (value) {
+        mp_hal_pin_write(pin, 1);
+    }
+    mp_hal_delay_us_fast(TIMING_WRITE2);
+    mp_hal_pin_write(pin, 1);
+    mp_hal_delay_us_fast(TIMING_WRITE3);
+    mp_hal_quiet_timing_exit(i);
+}
+
+/******************************************************************************/
+// MicroPython bindings
+
+STATIC mp_obj_t onewire_reset(mp_obj_t pin_in) {
+    return mp_obj_new_bool(onewire_bus_reset(mp_hal_get_pin_obj(pin_in)));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(onewire_reset_obj, onewire_reset);
+
+STATIC mp_obj_t onewire_readbit(mp_obj_t pin_in) {
+    return MP_OBJ_NEW_SMALL_INT(onewire_bus_readbit(mp_hal_get_pin_obj(pin_in)));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(onewire_readbit_obj, onewire_readbit);
+
+STATIC mp_obj_t onewire_readbyte(mp_obj_t pin_in) {
+    mp_hal_pin_obj_t pin = mp_hal_get_pin_obj(pin_in);
+    uint8_t value = 0;
+    for (int i = 0; i < 8; ++i) {
+        value |= onewire_bus_readbit(pin) << i;
+    }
+    return MP_OBJ_NEW_SMALL_INT(value);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(onewire_readbyte_obj, onewire_readbyte);
+
+STATIC mp_obj_t onewire_writebit(mp_obj_t pin_in, mp_obj_t value_in) {
+    onewire_bus_writebit(mp_hal_get_pin_obj(pin_in), mp_obj_get_int(value_in));
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(onewire_writebit_obj, onewire_writebit);
+
+STATIC mp_obj_t onewire_writebyte(mp_obj_t pin_in, mp_obj_t value_in) {
+    mp_hal_pin_obj_t pin = mp_hal_get_pin_obj(pin_in);
+    int value = mp_obj_get_int(value_in);
+    for (int i = 0; i < 8; ++i) {
+        onewire_bus_writebit(pin, value & 1);
+        value >>= 1;
+    }
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(onewire_writebyte_obj, onewire_writebyte);
+
+STATIC mp_obj_t onewire_crc8(mp_obj_t data) {
+    mp_buffer_info_t bufinfo;
+    mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ);
+    uint8_t crc = 0;
+    for (size_t i = 0; i < bufinfo.len; ++i) {
+        uint8_t byte = ((uint8_t*)bufinfo.buf)[i];
+        for (int b = 0; b < 8; ++b) {
+            uint8_t fb_bit = (crc ^ byte) & 0x01;
+            if (fb_bit == 0x01) {
+                crc = crc ^ 0x18;
+            }
+            crc = (crc >> 1) & 0x7f;
+            if (fb_bit == 0x01) {
+                crc = crc | 0x80;
+            }
+            byte = byte >> 1;
+        }
+    }
+    return MP_OBJ_NEW_SMALL_INT(crc);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(onewire_crc8_obj, onewire_crc8);
+
+STATIC const mp_rom_map_elem_t onewire_module_globals_table[] = {
+    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_onewire) },
+
+    { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&onewire_reset_obj) },
+    { MP_ROM_QSTR(MP_QSTR_readbit), MP_ROM_PTR(&onewire_readbit_obj) },
+    { MP_ROM_QSTR(MP_QSTR_readbyte), MP_ROM_PTR(&onewire_readbyte_obj) },
+    { MP_ROM_QSTR(MP_QSTR_writebit), MP_ROM_PTR(&onewire_writebit_obj) },
+    { MP_ROM_QSTR(MP_QSTR_writebyte), MP_ROM_PTR(&onewire_writebyte_obj) },
+    { MP_ROM_QSTR(MP_QSTR_crc8), MP_ROM_PTR(&onewire_crc8_obj) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(onewire_module_globals, onewire_module_globals_table);
+
+const mp_obj_module_t mp_module_onewire = {
+    .base = { &mp_type_module },
+    .globals = (mp_obj_dict_t*)&onewire_module_globals,
+};

+ 292 - 0
extmod/moducryptolib.c

@@ -0,0 +1,292 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017-2018 Paul Sokolovsky
+ * Copyright (c) 2018 Yonatan Goldschmidt
+ *
+ * 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_UCRYPTOLIB
+
+#include <assert.h>
+#include <string.h>
+
+#include "py/runtime.h"
+
+// This module implements crypto ciphers API, roughly following
+// https://www.python.org/dev/peps/pep-0272/ . Exact implementation
+// of PEP 272 can be made with a simple wrapper which adds all the
+// needed boilerplate.
+
+// values follow PEP 272
+enum {
+    UCRYPTOLIB_MODE_MIN = 0,
+    UCRYPTOLIB_MODE_ECB,
+    UCRYPTOLIB_MODE_CBC,
+    UCRYPTOLIB_MODE_MAX,
+};
+
+#if MICROPY_SSL_AXTLS
+#include "lib/axtls/crypto/crypto.h"
+
+#define AES_CTX_IMPL AES_CTX
+#endif
+
+#if MICROPY_SSL_MBEDTLS
+#include <mbedtls/aes.h>
+
+// we can't run mbedtls AES key schedule until we know whether we're used for encrypt or decrypt.
+// therefore, we store the key & keysize and on the first call to encrypt/decrypt we override them
+// with the mbedtls_aes_context, as they are not longer required. (this is done to save space)
+struct mbedtls_aes_ctx_with_key {
+    union {
+        mbedtls_aes_context mbedtls_ctx;
+        struct {
+            uint8_t key[32];
+            uint8_t keysize;
+        } init_data;
+    } u;
+    unsigned char iv[16];
+};
+#define AES_CTX_IMPL struct mbedtls_aes_ctx_with_key
+#endif
+
+typedef struct _mp_obj_aes_t {
+    mp_obj_base_t base;
+    AES_CTX_IMPL ctx;
+    uint8_t block_mode: 6;
+#define AES_KEYTYPE_NONE 0
+#define AES_KEYTYPE_ENC  1
+#define AES_KEYTYPE_DEC  2
+    uint8_t key_type: 2;
+} mp_obj_aes_t;
+
+#if MICROPY_SSL_AXTLS
+STATIC void aes_initial_set_key_impl(AES_CTX_IMPL *ctx, const uint8_t *key, size_t keysize, const uint8_t iv[16]) {
+    assert(16 == keysize || 32 == keysize);
+    AES_set_key(ctx, key, iv, (16 == keysize) ? AES_MODE_128 : AES_MODE_256);
+}
+
+STATIC void aes_final_set_key_impl(AES_CTX_IMPL *ctx, bool encrypt) {
+    if (!encrypt) {
+        AES_convert_key(ctx);
+    }
+}
+
+STATIC void aes_process_ecb_impl(AES_CTX_IMPL *ctx, const uint8_t in[16], uint8_t out[16], bool encrypt) {
+    memcpy(out, in, 16);
+    // We assume that out (vstr.buf or given output buffer) is uint32_t aligned
+    uint32_t *p = (uint32_t*)out;
+    // axTLS likes it weird and complicated with byteswaps
+    for (int i = 0; i < 4; i++) {
+        p[i] = MP_HTOBE32(p[i]);
+    }
+    if (encrypt) {
+        AES_encrypt(ctx, p);
+    } else {
+        AES_decrypt(ctx, p);
+    }
+    for (int i = 0; i < 4; i++) {
+        p[i] = MP_BE32TOH(p[i]);
+    }
+}
+
+STATIC void aes_process_cbc_impl(AES_CTX_IMPL *ctx, const uint8_t *in, uint8_t *out, size_t in_len, bool encrypt) {
+    if (encrypt) {
+        AES_cbc_encrypt(ctx, in, out, in_len);
+    } else {
+        AES_cbc_decrypt(ctx, in, out, in_len);
+    }
+}
+#endif
+
+#if MICROPY_SSL_MBEDTLS
+STATIC void aes_initial_set_key_impl(AES_CTX_IMPL *ctx, const uint8_t *key, size_t keysize, const uint8_t iv[16]) {
+    ctx->u.init_data.keysize = keysize;
+    memcpy(ctx->u.init_data.key, key, keysize);
+
+    if (NULL != iv) {
+        memcpy(ctx->iv, iv, sizeof(ctx->iv));
+    }
+}
+
+STATIC void aes_final_set_key_impl(AES_CTX_IMPL *ctx, bool encrypt) {
+    // first, copy key aside
+    uint8_t key[32];
+    uint8_t keysize = ctx->u.init_data.keysize;
+    memcpy(key, ctx->u.init_data.key, keysize);
+    // now, override key with the mbedtls context object
+    mbedtls_aes_init(&ctx->u.mbedtls_ctx);
+
+    // setkey call will succeed, we've already checked the keysize earlier.
+    assert(16 == keysize || 32 == keysize);
+    if (encrypt) {
+        mbedtls_aes_setkey_enc(&ctx->u.mbedtls_ctx, key, keysize * 8);
+    } else {
+        mbedtls_aes_setkey_dec(&ctx->u.mbedtls_ctx, key, keysize * 8);
+    }
+}
+
+STATIC void aes_process_ecb_impl(AES_CTX_IMPL *ctx, const uint8_t in[16], uint8_t out[16], bool encrypt) {
+    mbedtls_aes_crypt_ecb(&ctx->u.mbedtls_ctx, encrypt ? MBEDTLS_AES_ENCRYPT : MBEDTLS_AES_DECRYPT, in, out);
+}
+
+STATIC void aes_process_cbc_impl(AES_CTX_IMPL *ctx, const uint8_t *in, uint8_t *out, size_t in_len, bool encrypt) {
+    mbedtls_aes_crypt_cbc(&ctx->u.mbedtls_ctx, encrypt ? MBEDTLS_AES_ENCRYPT : MBEDTLS_AES_DECRYPT, in_len, ctx->iv, in, out);
+}
+#endif
+
+STATIC mp_obj_t ucryptolib_aes_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_aes_t *o = m_new_obj(mp_obj_aes_t);
+    o->base.type = type;
+
+    o->block_mode = mp_obj_get_int(args[1]);
+    o->key_type = AES_KEYTYPE_NONE;
+
+    if (o->block_mode <= UCRYPTOLIB_MODE_MIN || o->block_mode >= UCRYPTOLIB_MODE_MAX) {
+        mp_raise_ValueError("mode");
+    }
+
+    mp_buffer_info_t keyinfo;
+    mp_get_buffer_raise(args[0], &keyinfo, MP_BUFFER_READ);
+    if (32 != keyinfo.len && 16 != keyinfo.len) {
+        mp_raise_ValueError("key");
+    }
+
+    mp_buffer_info_t ivinfo;
+    ivinfo.buf = NULL;
+    if (n_args > 2 && args[2] != mp_const_none) {
+        mp_get_buffer_raise(args[2], &ivinfo, MP_BUFFER_READ);
+
+        if (16 != ivinfo.len) {
+            mp_raise_ValueError("IV");
+        }
+    } else if (o->block_mode == UCRYPTOLIB_MODE_CBC) {
+        mp_raise_ValueError("IV");
+    }
+
+    aes_initial_set_key_impl(&o->ctx, keyinfo.buf, keyinfo.len, ivinfo.buf);
+
+    return MP_OBJ_FROM_PTR(o);
+}
+
+STATIC mp_obj_t aes_process(size_t n_args, const mp_obj_t *args, bool encrypt) {
+    mp_obj_aes_t *self = MP_OBJ_TO_PTR(args[0]);
+
+    mp_obj_t in_buf = args[1];
+    mp_obj_t out_buf = MP_OBJ_NULL;
+    if (n_args > 2) {
+        out_buf = args[2];
+    }
+
+    mp_buffer_info_t in_bufinfo;
+    mp_get_buffer_raise(in_buf, &in_bufinfo, MP_BUFFER_READ);
+
+    if (in_bufinfo.len % 16 != 0) {
+        mp_raise_ValueError("blksize % 16");
+    }
+
+    vstr_t vstr;
+    mp_buffer_info_t out_bufinfo;
+    uint8_t *out_buf_ptr;
+
+    if (out_buf != MP_OBJ_NULL) {
+        mp_get_buffer_raise(out_buf, &out_bufinfo, MP_BUFFER_WRITE);
+        if (out_bufinfo.len < in_bufinfo.len) {
+            mp_raise_ValueError("output too small");
+        }
+        out_buf_ptr = out_bufinfo.buf;
+    } else {
+        vstr_init_len(&vstr, in_bufinfo.len);
+        out_buf_ptr = (uint8_t*)vstr.buf;
+    }
+
+    if (AES_KEYTYPE_NONE == self->key_type) {
+        aes_final_set_key_impl(&self->ctx, encrypt);
+        self->key_type = encrypt ? AES_KEYTYPE_ENC : AES_KEYTYPE_DEC;
+    } else {
+        if ((encrypt && self->key_type == AES_KEYTYPE_DEC) ||
+            (!encrypt && self->key_type == AES_KEYTYPE_ENC)) {
+
+            mp_raise_ValueError("can't encrypt & decrypt");
+        }
+    }
+
+    if (self->block_mode == UCRYPTOLIB_MODE_ECB) {
+        uint8_t *in = in_bufinfo.buf, *out = out_buf_ptr;
+        uint8_t *top = in + in_bufinfo.len;
+        for (; in < top; in += 16, out += 16) {
+            aes_process_ecb_impl(&self->ctx, in, out, encrypt);
+        }
+    } else {
+        aes_process_cbc_impl(&self->ctx, in_bufinfo.buf, out_buf_ptr, in_bufinfo.len, encrypt);
+    }
+
+    if (out_buf != MP_OBJ_NULL) {
+        return out_buf;
+    }
+    return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
+}
+
+STATIC mp_obj_t ucryptolib_aes_encrypt(size_t n_args, const mp_obj_t *args) {
+    return aes_process(n_args, args, true);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ucryptolib_aes_encrypt_obj, 2, 3, ucryptolib_aes_encrypt);
+
+STATIC mp_obj_t ucryptolib_aes_decrypt(size_t n_args, const mp_obj_t *args) {
+    return aes_process(n_args, args, false);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ucryptolib_aes_decrypt_obj, 2, 3, ucryptolib_aes_decrypt);
+
+STATIC const mp_rom_map_elem_t ucryptolib_aes_locals_dict_table[] = {
+    { MP_ROM_QSTR(MP_QSTR_encrypt), MP_ROM_PTR(&ucryptolib_aes_encrypt_obj) },
+    { MP_ROM_QSTR(MP_QSTR_decrypt), MP_ROM_PTR(&ucryptolib_aes_decrypt_obj) },
+};
+STATIC MP_DEFINE_CONST_DICT(ucryptolib_aes_locals_dict, ucryptolib_aes_locals_dict_table);
+
+STATIC const mp_obj_type_t ucryptolib_aes_type = {
+    { &mp_type_type },
+    .name = MP_QSTR_aes,
+    .make_new = ucryptolib_aes_make_new,
+    .locals_dict = (void*)&ucryptolib_aes_locals_dict,
+};
+
+STATIC const mp_rom_map_elem_t mp_module_ucryptolib_globals_table[] = {
+    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ucryptolib) },
+    { MP_ROM_QSTR(MP_QSTR_aes), MP_ROM_PTR(&ucryptolib_aes_type) },
+#if MICROPY_PY_UCRYPTOLIB_CONSTS
+    { MP_ROM_QSTR(MP_QSTR_MODE_ECB), MP_ROM_INT(UCRYPTOLIB_MODE_ECB) },
+    { MP_ROM_QSTR(MP_QSTR_MODE_CBC), MP_ROM_INT(UCRYPTOLIB_MODE_CBC) },
+#endif
+};
+
+STATIC MP_DEFINE_CONST_DICT(mp_module_ucryptolib_globals, mp_module_ucryptolib_globals_table);
+
+const mp_obj_module_t mp_module_ucryptolib = {
+    .base = { &mp_type_module },
+    .globals = (mp_obj_dict_t*)&mp_module_ucryptolib_globals,
+};
+
+#endif //MICROPY_PY_UCRYPTOLIB

+ 71 - 8
extmod/moductypes.c

@@ -3,7 +3,7 @@
  *
  * The MIT License (MIT)
  *
- * Copyright (c) 2014 Paul Sokolovsky
+ * Copyright (c) 2014-2018 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
@@ -92,7 +92,7 @@ enum {
 #define AGG_TYPE_BITS 2
 
 enum {
-    STRUCT, PTR, ARRAY, BITFIELD,
+    STRUCT, PTR, ARRAY,
 };
 
 // Here we need to set sign bit right
@@ -137,7 +137,11 @@ STATIC void uctypes_struct_print(const mp_print_t *print, mp_obj_t self_in, mp_p
     (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)) {
+    if (MP_OBJ_IS_TYPE(self->desc, &mp_type_dict)
+      #if MICROPY_PY_COLLECTIONS_ORDEREDDICT
+        || MP_OBJ_IS_TYPE(self->desc, &mp_type_ordereddict)
+      #endif
+      ) {
         typen = "STRUCT";
     } else if (MP_OBJ_IS_TYPE(self->desc, &mp_type_tuple)) {
         mp_obj_tuple_t *t = MP_OBJ_TO_PTR(self->desc);
@@ -206,7 +210,11 @@ STATIC mp_uint_t uctypes_struct_agg_size(mp_obj_tuple_t *t, int layout_type, mp_
 }
 
 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_dict)
+      #if MICROPY_PY_COLLECTIONS_ORDEREDDICT
+        && !MP_OBJ_IS_TYPE(desc_in, &mp_type_ordereddict)
+      #endif
+      ) {
         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)) {
@@ -261,7 +269,8 @@ STATIC mp_uint_t uctypes_struct_size(mp_obj_t desc_in, int layout_type, mp_uint_
     return total_size;
 }
 
-STATIC mp_obj_t uctypes_struct_sizeof(mp_obj_t obj_in) {
+STATIC mp_obj_t uctypes_struct_sizeof(size_t n_args, const mp_obj_t *args) {
+    mp_obj_t obj_in = args[0];
     mp_uint_t max_field_size = 0;
     if (MP_OBJ_IS_TYPE(obj_in, &mp_type_bytearray)) {
         return mp_obj_len(obj_in);
@@ -270,15 +279,22 @@ STATIC mp_obj_t uctypes_struct_sizeof(mp_obj_t obj_in) {
     // We can apply sizeof either to structure definition (a dict)
     // or to instantiated structure
     if (MP_OBJ_IS_TYPE(obj_in, &uctypes_struct_type)) {
+        if (n_args != 1) {
+            mp_raise_TypeError(NULL);
+        }
         // Extract structure definition
         mp_obj_uctypes_struct_t *obj = MP_OBJ_TO_PTR(obj_in);
         obj_in = obj->desc;
         layout_type = obj->flags;
+    } else {
+        if (n_args == 2) {
+            layout_type = mp_obj_get_int(args[1]);
+        }
     }
     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 MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(uctypes_struct_sizeof_obj, 1, 2, uctypes_struct_sizeof);
 
 static inline mp_obj_t get_unaligned(uint val_type, byte *p, int big_endian) {
     char struct_type = big_endian ? '>' : '<';
@@ -390,8 +406,11 @@ STATIC void set_aligned(uint val_type, void *p, mp_int_t index, mp_obj_t val) {
 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)) {
+    if (!MP_OBJ_IS_TYPE(self->desc, &mp_type_dict)
+      #if MICROPY_PY_COLLECTIONS_ORDEREDDICT
+        && !MP_OBJ_IS_TYPE(self->desc, &mp_type_ordereddict)
+      #endif
+      ) {
             mp_raise_TypeError("struct: no fields");
     }
 
@@ -595,6 +614,25 @@ STATIC mp_obj_t uctypes_struct_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_ob
     }
 }
 
+STATIC mp_obj_t uctypes_struct_unary_op(mp_unary_op_t op, mp_obj_t self_in) {
+    mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in);
+    switch (op) {
+        case MP_UNARY_OP_INT:
+            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);
+                if (agg_type == PTR) {
+                    byte *p = *(void**)self->addr;
+                    return mp_obj_new_int((mp_int_t)(uintptr_t)p);
+                }
+            }
+            /* fallthru */
+
+        default: return MP_OBJ_NULL; // op not supported
+    }
+}
+
 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);
@@ -643,6 +681,7 @@ STATIC const mp_obj_type_t uctypes_struct_type = {
     .make_new = uctypes_struct_make_new,
     .attr = uctypes_struct_attr,
     .subscr = uctypes_struct_subscr,
+    .unary_op = uctypes_struct_unary_op,
     .buffer_p = { .get_buffer = uctypes_get_buffer },
 };
 
@@ -701,6 +740,30 @@ STATIC const mp_rom_map_elem_t mp_module_uctypes_globals_table[] = {
     { MP_ROM_QSTR(MP_QSTR_FLOAT64), MP_ROM_INT(TYPE2SMALLINT(FLOAT64, 4)) },
     #endif
 
+    #if MICROPY_PY_UCTYPES_NATIVE_C_TYPES
+    // C native type aliases. These depend on GCC-compatible predefined
+    // preprocessor macros.
+    #if __SIZEOF_SHORT__ == 2
+    { MP_ROM_QSTR(MP_QSTR_SHORT), MP_ROM_INT(TYPE2SMALLINT(INT16, 4)) },
+    { MP_ROM_QSTR(MP_QSTR_USHORT), MP_ROM_INT(TYPE2SMALLINT(UINT16, 4)) },
+    #endif
+    #if __SIZEOF_INT__ == 4
+    { MP_ROM_QSTR(MP_QSTR_INT), MP_ROM_INT(TYPE2SMALLINT(INT32, 4)) },
+    { MP_ROM_QSTR(MP_QSTR_UINT), MP_ROM_INT(TYPE2SMALLINT(UINT32, 4)) },
+    #endif
+    #if __SIZEOF_LONG__ == 4
+    { MP_ROM_QSTR(MP_QSTR_LONG), MP_ROM_INT(TYPE2SMALLINT(INT32, 4)) },
+    { MP_ROM_QSTR(MP_QSTR_ULONG), MP_ROM_INT(TYPE2SMALLINT(UINT32, 4)) },
+    #elif __SIZEOF_LONG__ == 8
+    { MP_ROM_QSTR(MP_QSTR_LONG), MP_ROM_INT(TYPE2SMALLINT(INT64, 4)) },
+    { MP_ROM_QSTR(MP_QSTR_ULONG), MP_ROM_INT(TYPE2SMALLINT(UINT64, 4)) },
+    #endif
+    #if __SIZEOF_LONG_LONG__ == 8
+    { MP_ROM_QSTR(MP_QSTR_LONGLONG), MP_ROM_INT(TYPE2SMALLINT(INT64, 4)) },
+    { MP_ROM_QSTR(MP_QSTR_ULONGLONG), MP_ROM_INT(TYPE2SMALLINT(UINT64, 4)) },
+    #endif
+    #endif // MICROPY_PY_UCTYPES_NATIVE_C_TYPES
+
     { 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)) },
 };

+ 218 - 80
extmod/moduhashlib.c

@@ -31,15 +31,28 @@
 
 #if MICROPY_PY_UHASHLIB
 
+#if MICROPY_SSL_MBEDTLS
+#include "mbedtls/version.h"
+#endif
+
+#if MICROPY_PY_UHASHLIB_SHA256
+
+#if MICROPY_SSL_MBEDTLS
+#include "mbedtls/sha256.h"
+#else
 #include "crypto-algorithms/sha256.h"
+#endif
 
-#if MICROPY_PY_UHASHLIB_SHA1
+#endif
+
+#if MICROPY_PY_UHASHLIB_SHA1 || MICROPY_PY_UHASHLIB_MD5
 
 #if MICROPY_SSL_AXTLS
 #include "lib/axtls/crypto/crypto.h"
 #endif
 
 #if MICROPY_SSL_MBEDTLS
+#include "mbedtls/md5.h"
 #include "mbedtls/sha1.h"
 #endif
 
@@ -50,164 +63,289 @@ typedef struct _mp_obj_hash_t {
     char state[0];
 } mp_obj_hash_t;
 
-STATIC mp_obj_t hash_update(mp_obj_t self_in, mp_obj_t arg);
+#if MICROPY_PY_UHASHLIB_SHA256
+STATIC mp_obj_t uhashlib_sha256_update(mp_obj_t self_in, mp_obj_t arg);
+
+#if MICROPY_SSL_MBEDTLS
+
+#if MBEDTLS_VERSION_NUMBER < 0x02070000
+#define mbedtls_sha256_starts_ret mbedtls_sha256_starts
+#define mbedtls_sha256_update_ret mbedtls_sha256_update
+#define mbedtls_sha256_finish_ret mbedtls_sha256_finish
+#endif
+
+STATIC mp_obj_t uhashlib_sha256_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(mbedtls_sha256_context));
+    o->base.type = type;
+    mbedtls_sha256_init((mbedtls_sha256_context*)&o->state);
+    mbedtls_sha256_starts_ret((mbedtls_sha256_context*)&o->state, 0);
+    if (n_args == 1) {
+        uhashlib_sha256_update(MP_OBJ_FROM_PTR(o), args[0]);
+    }
+    return MP_OBJ_FROM_PTR(o);
+}
+
+STATIC mp_obj_t uhashlib_sha256_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);
+    mbedtls_sha256_update_ret((mbedtls_sha256_context*)&self->state, bufinfo.buf, bufinfo.len);
+    return mp_const_none;
+}
 
-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) {
+STATIC mp_obj_t uhashlib_sha256_digest(mp_obj_t self_in) {
+    mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
+    vstr_t vstr;
+    vstr_init_len(&vstr, 32);
+    mbedtls_sha256_finish_ret((mbedtls_sha256_context*)&self->state, (unsigned char *)vstr.buf);
+    return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
+}
+
+#else
+
+STATIC mp_obj_t uhashlib_sha256_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]);
+        uhashlib_sha256_update(MP_OBJ_FROM_PTR(o), args[0]);
     }
     return MP_OBJ_FROM_PTR(o);
 }
 
+STATIC mp_obj_t uhashlib_sha256_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;
+}
+
+STATIC mp_obj_t uhashlib_sha256_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);
+}
+#endif
+
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(uhashlib_sha256_update_obj, uhashlib_sha256_update);
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(uhashlib_sha256_digest_obj, uhashlib_sha256_digest);
+
+STATIC const mp_rom_map_elem_t uhashlib_sha256_locals_dict_table[] = {
+    { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&uhashlib_sha256_update_obj) },
+    { MP_ROM_QSTR(MP_QSTR_digest), MP_ROM_PTR(&uhashlib_sha256_digest_obj) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(uhashlib_sha256_locals_dict, uhashlib_sha256_locals_dict_table);
+
+STATIC const mp_obj_type_t uhashlib_sha256_type = {
+    { &mp_type_type },
+    .name = MP_QSTR_sha256,
+    .make_new = uhashlib_sha256_make_new,
+    .locals_dict = (void*)&uhashlib_sha256_locals_dict,
+};
+#endif
+
 #if MICROPY_PY_UHASHLIB_SHA1
-STATIC mp_obj_t sha1_update(mp_obj_t self_in, mp_obj_t arg);
+STATIC mp_obj_t uhashlib_sha1_update(mp_obj_t self_in, mp_obj_t arg);
 
 #if MICROPY_SSL_AXTLS
-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) {
+STATIC mp_obj_t uhashlib_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]);
+        uhashlib_sha1_update(MP_OBJ_FROM_PTR(o), args[0]);
     }
     return MP_OBJ_FROM_PTR(o);
 }
+
+STATIC mp_obj_t uhashlib_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;
+}
+
+STATIC mp_obj_t uhashlib_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);
+}
 #endif
 
 #if MICROPY_SSL_MBEDTLS
-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) {
+
+#if MBEDTLS_VERSION_NUMBER < 0x02070000
+#define mbedtls_sha1_starts_ret mbedtls_sha1_starts
+#define mbedtls_sha1_update_ret mbedtls_sha1_update
+#define mbedtls_sha1_finish_ret mbedtls_sha1_finish
+#endif
+
+STATIC mp_obj_t uhashlib_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(mbedtls_sha1_context));
     o->base.type = type;
     mbedtls_sha1_init((mbedtls_sha1_context*)o->state);
-    mbedtls_sha1_starts((mbedtls_sha1_context*)o->state);
+    mbedtls_sha1_starts_ret((mbedtls_sha1_context*)o->state);
     if (n_args == 1) {
-        sha1_update(MP_OBJ_FROM_PTR(o), args[0]);
+        uhashlib_sha1_update(MP_OBJ_FROM_PTR(o), args[0]);
     }
     return MP_OBJ_FROM_PTR(o);
 }
-#endif
 
-#endif
-
-STATIC mp_obj_t hash_update(mp_obj_t self_in, mp_obj_t arg) {
+STATIC mp_obj_t uhashlib_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);
-    sha256_update((CRYAL_SHA256_CTX*)self->state, bufinfo.buf, bufinfo.len);
+    mbedtls_sha1_update_ret((mbedtls_sha1_context*)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
-
-#if MICROPY_SSL_AXTLS
-STATIC mp_obj_t sha1_update(mp_obj_t self_in, mp_obj_t arg) {
+STATIC mp_obj_t uhashlib_sha1_digest(mp_obj_t self_in) {
     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;
+    vstr_t vstr;
+    vstr_init_len(&vstr, 20);
+    mbedtls_sha1_finish_ret((mbedtls_sha1_context*)self->state, (byte*)vstr.buf);
+    mbedtls_sha1_free((mbedtls_sha1_context*)self->state);
+    return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
 }
 #endif
 
-#if MICROPY_SSL_MBEDTLS
-STATIC mp_obj_t sha1_update(mp_obj_t self_in, mp_obj_t arg) {
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(uhashlib_sha1_update_obj, uhashlib_sha1_update);
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(uhashlib_sha1_digest_obj, uhashlib_sha1_digest);
+
+STATIC const mp_rom_map_elem_t uhashlib_sha1_locals_dict_table[] = {
+    { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&uhashlib_sha1_update_obj) },
+    { MP_ROM_QSTR(MP_QSTR_digest), MP_ROM_PTR(&uhashlib_sha1_digest_obj) },
+};
+STATIC MP_DEFINE_CONST_DICT(uhashlib_sha1_locals_dict, uhashlib_sha1_locals_dict_table);
+
+STATIC const mp_obj_type_t uhashlib_sha1_type = {
+    { &mp_type_type },
+    .name = MP_QSTR_sha1,
+    .make_new = uhashlib_sha1_make_new,
+    .locals_dict = (void*)&uhashlib_sha1_locals_dict,
+};
+#endif
+
+#if MICROPY_PY_UHASHLIB_MD5
+STATIC mp_obj_t uhashlib_md5_update(mp_obj_t self_in, mp_obj_t arg);
+
+#if MICROPY_SSL_AXTLS
+STATIC mp_obj_t uhashlib_md5_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(MD5_CTX));
+    o->base.type = type;
+    MD5_Init((MD5_CTX*)o->state);
+    if (n_args == 1) {
+        uhashlib_md5_update(MP_OBJ_FROM_PTR(o), args[0]);
+    }
+    return MP_OBJ_FROM_PTR(o);
+}
+
+STATIC mp_obj_t uhashlib_md5_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);
-    mbedtls_sha1_update((mbedtls_sha1_context*)self->state, bufinfo.buf, bufinfo.len);
+    MD5_Update((MD5_CTX*)self->state, bufinfo.buf, bufinfo.len);
     return mp_const_none;
 }
-#endif
 
-MP_DEFINE_CONST_FUN_OBJ_2(sha1_update_obj, sha1_update);
-#endif
-
-STATIC mp_obj_t hash_digest(mp_obj_t self_in) {
+STATIC mp_obj_t uhashlib_md5_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);
+    vstr_init_len(&vstr, MD5_SIZE);
+    MD5_Final((byte*)vstr.buf, (MD5_CTX*)self->state);
     return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
 }
-MP_DEFINE_CONST_FUN_OBJ_1(hash_digest_obj, hash_digest);
+#endif // MICROPY_SSL_AXTLS
 
-#if MICROPY_PY_UHASHLIB_SHA1
+#if MICROPY_SSL_MBEDTLS
 
-#if MICROPY_SSL_AXTLS
-STATIC mp_obj_t sha1_digest(mp_obj_t self_in) {
+#if MBEDTLS_VERSION_NUMBER < 0x02070000
+#define mbedtls_md5_starts_ret mbedtls_md5_starts
+#define mbedtls_md5_update_ret mbedtls_md5_update
+#define mbedtls_md5_finish_ret mbedtls_md5_finish
+#endif
+
+STATIC mp_obj_t uhashlib_md5_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(mbedtls_md5_context));
+    o->base.type = type;
+    mbedtls_md5_init((mbedtls_md5_context*)o->state);
+    mbedtls_md5_starts_ret((mbedtls_md5_context*)o->state);
+    if (n_args == 1) {
+        uhashlib_md5_update(MP_OBJ_FROM_PTR(o), args[0]);
+    }
+    return MP_OBJ_FROM_PTR(o);
+}
+
+STATIC mp_obj_t uhashlib_md5_update(mp_obj_t self_in, mp_obj_t arg) {
     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_buffer_info_t bufinfo;
+    mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ);
+    mbedtls_md5_update_ret((mbedtls_md5_context*)self->state, bufinfo.buf, bufinfo.len);
+    return mp_const_none;
 }
-#endif
 
-#if MICROPY_SSL_MBEDTLS
-STATIC mp_obj_t sha1_digest(mp_obj_t self_in) {
+STATIC mp_obj_t uhashlib_md5_digest(mp_obj_t self_in) {
     mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
     vstr_t vstr;
-    vstr_init_len(&vstr, 20);
-    mbedtls_sha1_finish((mbedtls_sha1_context*)self->state, (byte*)vstr.buf);
-    mbedtls_sha1_free((mbedtls_sha1_context*)self->state);
+    vstr_init_len(&vstr, 16);
+    mbedtls_md5_finish_ret((mbedtls_md5_context*)self->state, (byte*)vstr.buf);
+    mbedtls_md5_free((mbedtls_md5_context*)self->state);
     return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
 }
-#endif
-
-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);
+#endif // MICROPY_SSL_MBEDTLS
 
-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,
-};
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(uhashlib_md5_update_obj, uhashlib_md5_update);
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(uhashlib_md5_digest_obj, uhashlib_md5_digest);
 
-#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 const mp_rom_map_elem_t uhashlib_md5_locals_dict_table[] = {
+    { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&uhashlib_md5_update_obj) },
+    { MP_ROM_QSTR(MP_QSTR_digest), MP_ROM_PTR(&uhashlib_md5_digest_obj) },
 };
-STATIC MP_DEFINE_CONST_DICT(sha1_locals_dict, sha1_locals_dict_table);
+STATIC MP_DEFINE_CONST_DICT(uhashlib_md5_locals_dict, uhashlib_md5_locals_dict_table);
 
-STATIC const mp_obj_type_t sha1_type = {
+STATIC const mp_obj_type_t uhashlib_md5_type = {
     { &mp_type_type },
-    .name = MP_QSTR_sha1,
-    .make_new = sha1_make_new,
-    .locals_dict = (void*)&sha1_locals_dict,
+    .name = MP_QSTR_md5,
+    .make_new = uhashlib_md5_make_new,
+    .locals_dict = (void*)&uhashlib_md5_locals_dict,
 };
-#endif
+#endif // MICROPY_PY_UHASHLIB_MD5
 
-STATIC const mp_rom_map_elem_t mp_module_hashlib_globals_table[] = {
+STATIC const mp_rom_map_elem_t mp_module_uhashlib_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_SHA256
+    { MP_ROM_QSTR(MP_QSTR_sha256), MP_ROM_PTR(&uhashlib_sha256_type) },
+    #endif
     #if MICROPY_PY_UHASHLIB_SHA1
-    { MP_ROM_QSTR(MP_QSTR_sha1), MP_ROM_PTR(&sha1_type) },
+    { MP_ROM_QSTR(MP_QSTR_sha1), MP_ROM_PTR(&uhashlib_sha1_type) },
+    #endif
+    #if MICROPY_PY_UHASHLIB_MD5
+    { MP_ROM_QSTR(MP_QSTR_md5), MP_ROM_PTR(&uhashlib_md5_type) },
     #endif
 };
 
-STATIC MP_DEFINE_CONST_DICT(mp_module_hashlib_globals, mp_module_hashlib_globals_table);
+STATIC MP_DEFINE_CONST_DICT(mp_module_uhashlib_globals, mp_module_uhashlib_globals_table);
 
 const mp_obj_module_t mp_module_uhashlib = {
     .base = { &mp_type_module },
-    .globals = (mp_obj_dict_t*)&mp_module_hashlib_globals,
+    .globals = (mp_obj_dict_t*)&mp_module_uhashlib_globals,
 };
 
+#if MICROPY_PY_UHASHLIB_SHA256
 #include "crypto-algorithms/sha256.c"
+#endif
 
 #endif //MICROPY_PY_UHASHLIB

+ 1 - 3
extmod/modujson.c

@@ -35,9 +35,7 @@
 #if MICROPY_PY_UJSON
 
 STATIC mp_obj_t mod_ujson_dump(mp_obj_t obj, mp_obj_t stream) {
-    if (!MP_OBJ_IS_OBJ(stream)) {
-        mp_raise_TypeError(NULL);
-    }
+    mp_get_stream_raise(stream, MP_STREAM_OP_WRITE);
     mp_print_t print = {MP_OBJ_TO_PTR(stream), mp_stream_write_adaptor};
     mp_obj_print_helper(&print, obj, PRINT_JSON);
     return mp_const_none;

+ 203 - 0
extmod/modure.c

@@ -77,8 +77,83 @@ STATIC mp_obj_t match_group(mp_obj_t self_in, mp_obj_t no_in) {
 }
 MP_DEFINE_CONST_FUN_OBJ_2(match_group_obj, match_group);
 
+#if MICROPY_PY_URE_MATCH_GROUPS
+
+STATIC mp_obj_t match_groups(mp_obj_t self_in) {
+    mp_obj_match_t *self = MP_OBJ_TO_PTR(self_in);
+    if (self->num_matches <= 1) {
+        return mp_const_empty_tuple;
+    }
+    mp_obj_tuple_t *groups = MP_OBJ_TO_PTR(mp_obj_new_tuple(self->num_matches - 1, NULL));
+    for (int i = 1; i < self->num_matches; ++i) {
+        groups->items[i - 1] = match_group(self_in, MP_OBJ_NEW_SMALL_INT(i));
+    }
+    return MP_OBJ_FROM_PTR(groups);
+}
+MP_DEFINE_CONST_FUN_OBJ_1(match_groups_obj, match_groups);
+
+#endif
+
+#if MICROPY_PY_URE_MATCH_SPAN_START_END
+
+STATIC void match_span_helper(size_t n_args, const mp_obj_t *args, mp_obj_t span[2]) {
+    mp_obj_match_t *self = MP_OBJ_TO_PTR(args[0]);
+
+    mp_int_t no = 0;
+    if (n_args == 2) {
+        no = mp_obj_get_int(args[1]);
+        if (no < 0 || no >= self->num_matches) {
+            nlr_raise(mp_obj_new_exception_arg1(&mp_type_IndexError, args[1]));
+        }
+    }
+
+    mp_int_t s = -1;
+    mp_int_t e = -1;
+    const char *start = self->caps[no * 2];
+    if (start != NULL) {
+        // have a match for this group
+        const char *begin = mp_obj_str_get_str(self->str);
+        s = start - begin;
+        e = self->caps[no * 2 + 1] - begin;
+    }
+
+    span[0] = mp_obj_new_int(s);
+    span[1] = mp_obj_new_int(e);
+}
+
+STATIC mp_obj_t match_span(size_t n_args, const mp_obj_t *args) {
+    mp_obj_t span[2];
+    match_span_helper(n_args, args, span);
+    return mp_obj_new_tuple(2, span);
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(match_span_obj, 1, 2, match_span);
+
+STATIC mp_obj_t match_start(size_t n_args, const mp_obj_t *args) {
+    mp_obj_t span[2];
+    match_span_helper(n_args, args, span);
+    return span[0];
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(match_start_obj, 1, 2, match_start);
+
+STATIC mp_obj_t match_end(size_t n_args, const mp_obj_t *args) {
+    mp_obj_t span[2];
+    match_span_helper(n_args, args, span);
+    return span[1];
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(match_end_obj, 1, 2, match_end);
+
+#endif
+
 STATIC const mp_rom_map_elem_t match_locals_dict_table[] = {
     { MP_ROM_QSTR(MP_QSTR_group), MP_ROM_PTR(&match_group_obj) },
+    #if MICROPY_PY_URE_MATCH_GROUPS
+    { MP_ROM_QSTR(MP_QSTR_groups), MP_ROM_PTR(&match_groups_obj) },
+    #endif
+    #if MICROPY_PY_URE_MATCH_SPAN_START_END
+    { MP_ROM_QSTR(MP_QSTR_span), MP_ROM_PTR(&match_span_obj) },
+    { MP_ROM_QSTR(MP_QSTR_start), MP_ROM_PTR(&match_start_obj) },
+    { MP_ROM_QSTR(MP_QSTR_end), MP_ROM_PTR(&match_end_obj) },
+    #endif
 };
 
 STATIC MP_DEFINE_CONST_DICT(match_locals_dict, match_locals_dict_table);
@@ -174,10 +249,127 @@ STATIC mp_obj_t re_split(size_t n_args, const mp_obj_t *args) {
 }
 MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(re_split_obj, 2, 3, re_split);
 
+#if MICROPY_PY_URE_SUB
+
+STATIC mp_obj_t re_sub_helper(mp_obj_t self_in, size_t n_args, const mp_obj_t *args) {
+    mp_obj_re_t *self = MP_OBJ_TO_PTR(self_in);
+    mp_obj_t replace = args[1];
+    mp_obj_t where = args[2];
+    mp_int_t count = 0;
+    if (n_args > 3) {
+        count = mp_obj_get_int(args[3]);
+        // Note: flags are currently ignored
+    }
+
+    size_t where_len;
+    const char *where_str = mp_obj_str_get_data(where, &where_len);
+    Subject subj;
+    subj.begin = where_str;
+    subj.end = subj.begin + where_len;
+    int caps_num = (self->re.sub + 1) * 2;
+
+    vstr_t vstr_return;
+    vstr_return.buf = NULL; // We'll init the vstr after the first match
+    mp_obj_match_t *match = mp_local_alloc(sizeof(mp_obj_match_t) + caps_num * sizeof(char*));
+    match->base.type = &match_type;
+    match->num_matches = caps_num / 2; // caps_num counts start and end pointers
+    match->str = where;
+
+    for (;;) {
+        // 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, false);
+
+        // If we didn't have a match, or had an empty match, it's time to stop
+        if (!res || match->caps[0] == match->caps[1]) {
+            break;
+        }
+
+        // Initialise the vstr if it's not already
+        if (vstr_return.buf == NULL) {
+            vstr_init(&vstr_return, match->caps[0] - subj.begin);
+        }
+
+        // Add pre-match string
+        vstr_add_strn(&vstr_return, subj.begin, match->caps[0] - subj.begin);
+
+        // Get replacement string
+        const char* repl = mp_obj_str_get_str((mp_obj_is_callable(replace) ? mp_call_function_1(replace, MP_OBJ_FROM_PTR(match)) : replace));
+
+        // Append replacement string to result, substituting any regex groups
+        while (*repl != '\0') {
+            if (*repl == '\\') {
+                ++repl;
+                bool is_g_format = false;
+                if (*repl == 'g' && repl[1] == '<') {
+                    // Group specified with syntax "\g<number>"
+                    repl += 2;
+                    is_g_format = true;
+                }
+
+                if ('0' <= *repl && *repl <= '9') {
+                    // Group specified with syntax "\g<number>" or "\number"
+                    unsigned int match_no = 0;
+                    do {
+                        match_no = match_no * 10 + (*repl++ - '0');
+                    } while ('0' <= *repl && *repl <= '9');
+                    if (is_g_format && *repl == '>') {
+                        ++repl;
+                    }
+
+                    if (match_no >= (unsigned int)match->num_matches) {
+                        nlr_raise(mp_obj_new_exception_arg1(&mp_type_IndexError, MP_OBJ_NEW_SMALL_INT(match_no)));
+                    }
+
+                    const char *start_match = match->caps[match_no * 2];
+                    if (start_match != NULL) {
+                        // Add the substring matched by group
+                        const char *end_match = match->caps[match_no * 2 + 1];
+                        vstr_add_strn(&vstr_return, start_match, end_match - start_match);
+                    }
+                }
+            } else {
+                // Just add the current byte from the replacement string
+                vstr_add_byte(&vstr_return, *repl++);
+            }
+        }
+
+        // Move start pointer to end of last match
+        subj.begin = match->caps[1];
+
+        // Stop substitutions if count was given and gets to 0
+        if (count > 0 && --count == 0) {
+            break;
+        }
+    }
+
+    mp_local_free(match);
+
+    if (vstr_return.buf == NULL) {
+        // Optimisation for case of no substitutions
+        return where;
+    }
+
+    // Add post-match string
+    vstr_add_strn(&vstr_return, subj.begin, subj.end - subj.begin);
+
+    return mp_obj_new_str_from_vstr(mp_obj_get_type(where), &vstr_return);
+}
+
+STATIC mp_obj_t re_sub(size_t n_args, const mp_obj_t *args) {
+    return re_sub_helper(args[0], n_args, args);
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(re_sub_obj, 3, 5, re_sub);
+
+#endif
+
 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) },
+    #if MICROPY_PY_URE_SUB
+    { MP_ROM_QSTR(MP_QSTR_sub), MP_ROM_PTR(&re_sub_obj) },
+    #endif
 };
 
 STATIC MP_DEFINE_CONST_DICT(re_locals_dict, re_locals_dict_table);
@@ -232,11 +424,22 @@ STATIC mp_obj_t mod_re_search(size_t n_args, const mp_obj_t *args) {
 }
 MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_re_search_obj, 2, 4, mod_re_search);
 
+#if MICROPY_PY_URE_SUB
+STATIC mp_obj_t mod_re_sub(size_t n_args, const mp_obj_t *args) {
+    mp_obj_t self = mod_re_compile(1, args);
+    return re_sub_helper(self, n_args, args);
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_re_sub_obj, 3, 5, mod_re_sub);
+#endif
+
 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) },
+    #if MICROPY_PY_URE_SUB
+    { MP_ROM_QSTR(MP_QSTR_sub), MP_ROM_PTR(&mod_re_sub_obj) },
+    #endif
     { MP_ROM_QSTR(MP_QSTR_DEBUG), MP_ROM_INT(FLAG_DEBUG) },
 };
 

+ 23 - 23
extmod/moduselect.c

@@ -45,7 +45,7 @@
 
 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 (*ioctl)(mp_obj_t obj, mp_uint_t request, uintptr_t arg, int *errcode);
     mp_uint_t flags;
     mp_uint_t flags_ret;
 } poll_obj_t;
@@ -53,7 +53,7 @@ typedef struct _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) {
+        if (elem->value == MP_OBJ_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);
@@ -61,27 +61,27 @@ STATIC void poll_map_add(mp_map_t *poll_map, const mp_obj_t *obj, mp_uint_t obj_
             poll_obj->ioctl = stream_p->ioctl;
             poll_obj->flags = flags;
             poll_obj->flags_ret = 0;
-            elem->value = poll_obj;
+            elem->value = MP_OBJ_FROM_PTR(poll_obj);
         } else {
             // object exists; update its flags
             if (or_flags) {
-                ((poll_obj_t*)elem->value)->flags |= flags;
+                ((poll_obj_t*)MP_OBJ_TO_PTR(elem->value))->flags |= flags;
             } else {
-                ((poll_obj_t*)elem->value)->flags = flags;
+                ((poll_obj_t*)MP_OBJ_TO_PTR(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) {
+STATIC mp_uint_t poll_map_poll(mp_map_t *poll_map, size_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;
+        poll_obj_t *poll_obj = MP_OBJ_TO_PTR(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;
@@ -158,15 +158,15 @@ STATIC mp_obj_t select_select(uint n_args, const mp_obj_t *args) {
                 if (!MP_MAP_SLOT_IS_FILLED(&poll_map, i)) {
                     continue;
                 }
-                poll_obj_t *poll_obj = (poll_obj_t*)poll_map.table[i].value;
+                poll_obj_t *poll_obj = MP_OBJ_TO_PTR(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;
+                    ((mp_obj_list_t*)MP_OBJ_TO_PTR(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;
+                    ((mp_obj_list_t*)MP_OBJ_TO_PTR(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_obj_list_t*)MP_OBJ_TO_PTR(list_array[2]))->items[rwx_len[2]++] = poll_obj->obj;
                 }
             }
             mp_map_deinit(&poll_map);
@@ -191,7 +191,7 @@ typedef struct _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_obj_poll_t *self = MP_OBJ_TO_PTR(args[0]);
     mp_uint_t flags;
     if (n_args == 3) {
         flags = mp_obj_get_int(args[2]);
@@ -205,7 +205,7 @@ 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_obj_poll_t *self = MP_OBJ_TO_PTR(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;
@@ -214,18 +214,18 @@ 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_obj_poll_t *self = MP_OBJ_TO_PTR(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);
+    ((poll_obj_t*)MP_OBJ_TO_PTR(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];
+    mp_obj_poll_t *self = MP_OBJ_TO_PTR(args[0]);
 
     // work out timeout (its given already in ms)
     mp_uint_t timeout = -1;
@@ -258,18 +258,18 @@ STATIC mp_uint_t poll_poll_internal(uint n_args, const mp_obj_t *args) {
     return n_ready;
 }
 
-STATIC mp_obj_t poll_poll(uint n_args, const mp_obj_t *args) {
-    mp_obj_poll_t *self = args[0];
+STATIC mp_obj_t poll_poll(size_t n_args, const mp_obj_t *args) {
+    mp_obj_poll_t *self = MP_OBJ_TO_PTR(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);
+    mp_obj_list_t *ret_list = MP_OBJ_TO_PTR(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;
+        poll_obj_t *poll_obj = MP_OBJ_TO_PTR(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);
@@ -279,7 +279,7 @@ STATIC mp_obj_t poll_poll(uint n_args, const mp_obj_t *args) {
             }
         }
     }
-    return ret_list;
+    return MP_OBJ_FROM_PTR(ret_list);
 }
 MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_poll_obj, 1, 3, poll_poll);
 
@@ -312,7 +312,7 @@ STATIC mp_obj_t poll_iternext(mp_obj_t self_in) {
         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;
+        poll_obj_t *poll_obj = MP_OBJ_TO_PTR(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;
@@ -354,7 +354,7 @@ STATIC mp_obj_t select_poll(void) {
     mp_map_init(&poll->poll_map, 0);
     poll->iter_cnt = 0;
     poll->ret_tuple = MP_OBJ_NULL;
-    return poll;
+    return MP_OBJ_FROM_PTR(poll);
 }
 MP_DEFINE_CONST_FUN_OBJ_0(mp_select_poll_obj, select_poll);
 

+ 17 - 18
extmod/modussl_axtls.c

@@ -175,6 +175,17 @@ STATIC mp_uint_t socket_write(mp_obj_t o_in, const void *buf, mp_uint_t size, in
     return r;
 }
 
+STATIC mp_uint_t socket_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) {
+    mp_obj_ssl_socket_t *self = MP_OBJ_TO_PTR(o_in);
+    if (request == MP_STREAM_CLOSE && self->ssl_sock != NULL) {
+        ssl_free(self->ssl_sock);
+        ssl_ctx_free(self->ssl_ctx);
+        self->ssl_sock = NULL;
+    }
+    // Pass all requests down to the underlying socket
+    return mp_get_stream(self->sock)->ioctl(self->sock, request, arg, errcode);
+}
+
 STATIC mp_obj_t socket_setblocking(mp_obj_t self_in, mp_obj_t flag_in) {
     // Currently supports only blocking mode
     (void)self_in;
@@ -185,28 +196,15 @@ STATIC mp_obj_t socket_setblocking(mp_obj_t self_in, mp_obj_t flag_in) {
 }
 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) },
+    { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) },
 #if MICROPY_PY_USSL_FINALISER
-    { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&socket_close_obj) },
+    { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_stream_close_obj) },
 #endif
 };
 
@@ -215,6 +213,7 @@ STATIC MP_DEFINE_CONST_DICT(ussl_socket_locals_dict, ussl_socket_locals_dict_tab
 STATIC const mp_stream_p_t ussl_socket_stream_p = {
     .read = socket_read,
     .write = socket_write,
+    .ioctl = socket_ioctl,
 };
 
 STATIC const mp_obj_type_t ussl_socket_type = {
@@ -231,10 +230,10 @@ STATIC const mp_obj_type_t ussl_socket_type = {
 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_const_none} },
-        { MP_QSTR_cert, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
+        { MP_QSTR_key, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },
+        { MP_QSTR_cert, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },
         { 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} },
+        { MP_QSTR_server_hostname, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },
     };
 
     // TODO: Check that sock implements stream protocol

+ 23 - 28
extmod/modussl_mbedtls.c

@@ -73,19 +73,10 @@ STATIC void mbedtls_debug(void *ctx, int level, const char *file, int line, cons
 }
 #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);
+    const mp_stream_p_t *sock_stream = mp_get_stream(sock);
     int err;
 
     mp_uint_t out_sz = sock_stream->write(sock, buf, len, &err);
@@ -102,7 +93,7 @@ STATIC int _mbedtls_ssl_send(void *ctx, const byte *buf, size_t len) {
 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);
+    const mp_stream_p_t *sock_stream = mp_get_stream(sock);
     int err;
 
     mp_uint_t out_sz = sock_stream->read(sock, buf, len, &err);
@@ -118,12 +109,16 @@ STATIC int _mbedtls_ssl_recv(void *ctx, byte *buf, size_t len) {
 
 
 STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) {
+    // Verify the socket object has the full stream protocol
+    mp_get_stream_raise(sock, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL);
+
 #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->sock = sock;
 
     int ret;
     mbedtls_ssl_init(&o->ssl);
@@ -139,7 +134,7 @@ STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) {
 
     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));
+    ret = mbedtls_ctr_drbg_seed(&o->ctr_drbg, mbedtls_entropy_func, &o->entropy, seed, sizeof(seed));
     if (ret != 0) {
         goto cleanup;
     }
@@ -171,7 +166,6 @@ STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) {
         }
     }
 
-    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) {
@@ -274,20 +268,20 @@ STATIC mp_obj_t socket_setblocking(mp_obj_t self_in, mp_obj_t flag_in) {
 }
 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_uint_t socket_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) {
+    mp_obj_ssl_socket_t *self = MP_OBJ_TO_PTR(o_in);
+    if (request == MP_STREAM_CLOSE) {
+        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);
+    }
+    // Pass all requests down to the underlying socket
+    return mp_get_stream(self->sock)->ioctl(self->sock, request, arg, errcode);
 }
-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) },
@@ -295,9 +289,9 @@ STATIC const mp_rom_map_elem_t ussl_socket_locals_dict_table[] = {
     { 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) },
+    { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) },
 #if MICROPY_PY_USSL_FINALISER
-    { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&socket_close_obj) },
+    { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_stream_close_obj) },
 #endif
     { MP_ROM_QSTR(MP_QSTR_getpeercert), MP_ROM_PTR(&mod_ssl_getpeercert_obj) },
 };
@@ -307,6 +301,7 @@ STATIC MP_DEFINE_CONST_DICT(ussl_socket_locals_dict, ussl_socket_locals_dict_tab
 STATIC const mp_stream_p_t ussl_socket_stream_p = {
     .read = socket_read,
     .write = socket_write,
+    .ioctl = socket_ioctl,
 };
 
 STATIC const mp_obj_type_t ussl_socket_type = {

+ 2 - 1
extmod/moduzlib.c

@@ -53,7 +53,7 @@ STATIC unsigned char read_src_stream(TINF_DATA *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);
+    const mp_stream_p_t *stream = mp_get_stream(self->src_stream);
     int err;
     byte c;
     mp_uint_t out_sz = stream->read(self->src_stream, &c, 1, &err);
@@ -68,6 +68,7 @@ STATIC unsigned char read_src_stream(TINF_DATA *data) {
 
 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_get_stream_raise(args[0], MP_STREAM_OP_READ);
     mp_obj_decompio_t *o = m_new_obj(mp_obj_decompio_t);
     o->base.type = type;
     memset(&o->decomp, 0, sizeof(o->decomp));

+ 20 - 13
extmod/modwebrepl.c

@@ -35,7 +35,6 @@
 #include "py/mphal.h"
 #endif
 #include "extmod/modwebsocket.h"
-#include "genhdr/mpversion.h"
 
 #if MICROPY_PY_WEBREPL
 
@@ -76,7 +75,7 @@ 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);
+    const mp_stream_p_t *sock_stream = mp_get_stream(websock);
     int err;
     int old_opts = sock_stream->ioctl(websock, MP_STREAM_SET_DATA_OPTS, FRAME_BIN, &err);
     sock_stream->write(websock, buf, len, &err);
@@ -86,7 +85,7 @@ STATIC void write_webrepl(mp_obj_t websock, const void *buf, size_t len) {
 #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);
+    const mp_stream_p_t *sock_stream = mp_get_stream(websock);
     sock_stream->write(websock, str, sz, &err);
 }
 
@@ -110,8 +109,7 @@ STATIC mp_obj_t webrepl_make_new(const mp_obj_type_t *type, size_t n_args, size_
 }
 
 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);
+    const mp_stream_p_t *file_stream = mp_get_stream(self->cur_file);
     byte readbuf[2 + 256];
     int err;
     mp_uint_t out_sz = file_stream->read(self->cur_file, readbuf + 2, sizeof(readbuf) - 2, &err);
@@ -181,7 +179,7 @@ STATIC mp_uint_t _webrepl_read(mp_obj_t self_in, void *buf, mp_uint_t size, int
     // 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);
+    const mp_stream_p_t *sock_stream = mp_get_stream(self->sock);
     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) {
@@ -293,16 +291,24 @@ STATIC mp_uint_t webrepl_write(mp_obj_t self_in, const void *buf, mp_uint_t size
         // 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);
+    const mp_stream_p_t *stream_p = mp_get_stream(self->sock);
     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_uint_t webrepl_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) {
+    mp_obj_webrepl_t *self = MP_OBJ_TO_PTR(o_in);
+    (void)arg;
+    switch (request) {
+        case MP_STREAM_CLOSE:
+            // TODO: This is a place to do cleanup
+            mp_stream_close(self->sock);
+            return 0;
+
+        default:
+            *errcode = MP_EINVAL;
+            return MP_STREAM_ERROR;
+    }
 }
-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;
@@ -319,13 +325,14 @@ 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) },
+    { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_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,
+    .ioctl = webrepl_ioctl,
 };
 
 STATIC const mp_obj_type_t webrepl_type = {

+ 8 - 10
extmod/modwebsocket.c

@@ -59,6 +59,7 @@ STATIC mp_uint_t websocket_write(mp_obj_t self_in, const void *buf, mp_uint_t si
 
 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_get_stream_raise(args[0], MP_STREAM_OP_READ | MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL);
     mp_obj_websocket_t *o = m_new_obj(mp_obj_websocket_t);
     o->base.type = type;
     o->sock = args[0];
@@ -75,7 +76,7 @@ STATIC mp_obj_t websocket_make_new(const mp_obj_type_t *type, size_t n_args, siz
 
 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);
+    const mp_stream_p_t *stream_p = mp_get_stream(self->sock);
     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);
@@ -256,6 +257,11 @@ STATIC mp_uint_t websocket_write(mp_obj_t self_in, const void *buf, mp_uint_t si
 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_CLOSE:
+            // TODO: Send close signaling to the other side, otherwise it's
+            // abrupt close (connection abort).
+            mp_stream_close(self->sock);
+            return 0;
         case MP_STREAM_GET_DATA_OPTS:
             return self->ws_flags & FRAME_OPCODE_MASK;
         case MP_STREAM_SET_DATA_OPTS: {
@@ -269,21 +275,13 @@ STATIC mp_uint_t websocket_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t
     }
 }
 
-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) },
+    { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) },
 };
 STATIC MP_DEFINE_CONST_DICT(websocket_locals_dict, websocket_locals_dict_table);
 

+ 19 - 28
extmod/uos_dupterm.c

@@ -60,28 +60,32 @@ int mp_uos_dupterm_rx_chr(void) {
 
         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)) {
+            byte buf[1];
+            int errcode;
+            const mp_stream_p_t *stream_p = mp_get_stream(MP_STATE_VM(dupterm_objs[idx]));
+            mp_uint_t out_sz = stream_p->read(MP_STATE_VM(dupterm_objs[idx]), buf, 1, &errcode);
+            if (out_sz == 0) {
                 nlr_pop();
                 mp_uos_deactivate(idx, "dupterm: EOF received, deactivating\n", MP_OBJ_NULL);
+            } else if (out_sz == MP_STREAM_ERROR) {
+                // errcode is valid
+                if (mp_is_nonblocking_error(errcode)) {
+                    nlr_pop();
+                } else {
+                    mp_raise_OSError(errcode);
+                }
             } else {
-                mp_buffer_info_t bufinfo;
-                mp_get_buffer_raise(MP_STATE_VM(dupterm_arr_obj), &bufinfo, MP_BUFFER_READ);
+                // read 1 byte
                 nlr_pop();
-                if (*(byte*)bufinfo.buf == mp_interrupt_char) {
+                if (buf[0] == mp_interrupt_char) {
                     // Signal keyboard interrupt to be raised as soon as the VM resumes
                     mp_keyboard_interrupt();
                     return -2;
                 }
-                return *(byte*)bufinfo.buf;
+                return buf[0];
             }
         } else {
-            mp_uos_deactivate(idx, "dupterm: Exception in read() method, deactivating: ", nlr.ret_val);
+            mp_uos_deactivate(idx, "dupterm: Exception in read() method, deactivating: ", MP_OBJ_FROM_PTR(nlr.ret_val));
         }
     }
 
@@ -96,21 +100,10 @@ void mp_uos_dupterm_tx_strn(const char *str, size_t len) {
         }
         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;
+            mp_stream_write(MP_STATE_VM(dupterm_objs[idx]), str, len, MP_STREAM_RW_WRITE);
             nlr_pop();
         } else {
-            mp_uos_deactivate(idx, "dupterm: Exception in write() method, deactivating: ", nlr.ret_val);
+            mp_uos_deactivate(idx, "dupterm: Exception in write() method, deactivating: ", MP_OBJ_FROM_PTR(nlr.ret_val));
         }
     }
 }
@@ -132,10 +125,8 @@ STATIC mp_obj_t mp_uos_dupterm(size_t n_args, const mp_obj_t *args) {
     if (args[0] == mp_const_none) {
         MP_STATE_VM(dupterm_objs[idx]) = MP_OBJ_NULL;
     } else {
+        mp_get_stream_raise(args[0], MP_STREAM_OP_READ | MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL);
         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;

+ 4 - 2
extmod/uzlib/tinflate.c

@@ -394,9 +394,11 @@ static int tinf_inflate_uncompressed_block(TINF_DATA *d)
         unsigned int length, invlength;
 
         /* get length */
-        length = uzlib_get_byte(d) + 256 * uzlib_get_byte(d);
+        length = uzlib_get_byte(d);
+        length += 256 * uzlib_get_byte(d);
         /* get one's complement of length */
-        invlength = uzlib_get_byte(d) + 256 * uzlib_get_byte(d);
+        invlength = uzlib_get_byte(d);
+        invlength += 256 * uzlib_get_byte(d);
         /* check length */
         if (length != (~invlength & 0x0000ffff)) return TINF_DATA_ERROR;
 

+ 39 - 10
extmod/vfs.c

@@ -38,6 +38,10 @@
 #include "extmod/vfs_fat.h"
 #endif
 
+#if MICROPY_VFS_POSIX
+#include "extmod/vfs_posix.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)
@@ -124,21 +128,39 @@ mp_import_stat_t mp_vfs_import_stat(const char *path) {
     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);
+
+    // If the mounted object has the VFS protocol, call its import_stat helper
+    const mp_vfs_proto_t *proto = mp_obj_get_type(vfs->obj)->protocol;
+    if (proto != NULL) {
+        return proto->import_stat(MP_OBJ_TO_PTR(vfs->obj), path_out);
+    }
+
+    // delegate to vfs.stat() method
+    mp_obj_t path_o = mp_obj_new_str(path_out, strlen(path_out));
+    mp_obj_t stat;
+    nlr_buf_t nlr;
+    if (nlr_push(&nlr) == 0) {
+        stat = mp_vfs_proxy_call(vfs, MP_QSTR_stat, 1, &path_o);
+        nlr_pop();
+    } else {
+        // assume an exception means that the path is not found
+        return MP_IMPORT_STAT_NO_EXIST;
+    }
+    mp_obj_t *items;
+    mp_obj_get_array_fixed_n(stat, 10, &items);
+    mp_int_t st_mode = mp_obj_get_int(items[0]);
+    if (st_mode & MP_S_IFDIR) {
+        return MP_IMPORT_STAT_DIR;
+    } else {
+        return MP_IMPORT_STAT_FILE;
     }
-    #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} },
+        { MP_QSTR_readonly, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_false_obj)} },
+        { MP_QSTR_mkfs, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_false_obj)} },
     };
 
     // parse args
@@ -246,7 +268,14 @@ mp_obj_t mp_vfs_open(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_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);
+    #if MICROPY_VFS_POSIX
+    // If the file is an integer then delegate straight to the POSIX handler
+    if (MP_OBJ_IS_SMALL_INT(args[ARG_file].u_obj)) {
+        return mp_vfs_posix_file_open(&mp_type_textio, args[ARG_file].u_obj, args[ARG_mode].u_obj);
+    }
+    #endif
+
+    mp_vfs_mount_t *vfs = lookup_path(args[ARG_file].u_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);

+ 5 - 0
extmod/vfs.h

@@ -45,6 +45,11 @@
 #define BP_IOCTL_SEC_COUNT      (4)
 #define BP_IOCTL_SEC_SIZE       (5)
 
+// At the moment the VFS protocol just has import_stat, but could be extended to other methods
+typedef struct _mp_vfs_proto_t {
+    mp_import_stat_t (*import_stat)(void *self, const char *path);
+} mp_vfs_proto_t;
+
 typedef struct _mp_vfs_mount_t {
     const char *str; // mount point with leading /
     size_t len;

+ 8 - 1
extmod/vfs_fat.c

@@ -47,7 +47,8 @@
 
 #define mp_obj_fat_vfs_t fs_user_mount_t
 
-mp_import_stat_t fat_vfs_import_stat(fs_user_mount_t *vfs, const char *path) {
+STATIC mp_import_stat_t fat_vfs_import_stat(void *vfs_in, const char *path) {
+    fs_user_mount_t *vfs = vfs_in;
     FILINFO fno;
     assert(vfs != NULL);
     FRESULT res = f_stat(&vfs->fatfs, path, &fno);
@@ -421,11 +422,17 @@ STATIC const mp_rom_map_elem_t fat_vfs_locals_dict_table[] = {
 };
 STATIC MP_DEFINE_CONST_DICT(fat_vfs_locals_dict, fat_vfs_locals_dict_table);
 
+STATIC const mp_vfs_proto_t fat_vfs_proto = {
+    .import_stat = fat_vfs_import_stat,
+};
+
 const mp_obj_type_t mp_fat_vfs_type = {
     { &mp_type_type },
     .name = MP_QSTR_VfsFat,
     .make_new = fat_vfs_make_new,
+    .protocol = &fat_vfs_proto,
     .locals_dict = (mp_obj_dict_t*)&fat_vfs_locals_dict,
+
 };
 
 #endif // MICROPY_VFS_FAT

+ 2 - 1
extmod/vfs_fat.h

@@ -55,8 +55,9 @@ typedef struct _fs_user_mount_t {
 
 extern const byte fresult_to_errno_table[20];
 extern const mp_obj_type_t mp_fat_vfs_type;
+extern const mp_obj_type_t mp_type_vfs_fat_fileio;
+extern const mp_obj_type_t mp_type_vfs_fat_textio;
 
-mp_import_stat_t fat_vfs_import_stat(struct _fs_user_mount_t *vfs, const char *path);
 MP_DECLARE_CONST_FUN_OBJ_3(fat_vfs_open_obj);
 
 #endif // MICROPY_INCLUDED_EXTMOD_VFS_FAT_H

+ 66 - 122
extmod/vfs_fat_diskio.c

@@ -53,59 +53,6 @@ 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)                                                        */
 /*-----------------------------------------------------------------------*/
@@ -191,54 +138,21 @@ DRESULT disk_ioctl (
         return RES_PARERR;
     }
 
+    // First part: call the relevant method of the underlying block device
+    mp_obj_t ret = mp_const_none;
     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;
+        static const uint8_t op_map[8] = {
+            [CTRL_SYNC] = BP_IOCTL_SYNC,
+            [GET_SECTOR_COUNT] = BP_IOCTL_SEC_COUNT,
+            [GET_SECTOR_SIZE] = BP_IOCTL_SEC_SIZE,
+            [IOCTL_INIT] = BP_IOCTL_INIT,
+        };
+        uint8_t bp_op = op_map[cmd & 7];
+        if (bp_op != 0) {
+            vfs->u.ioctl[2] = MP_OBJ_NEW_SMALL_INT(bp_op);
+            vfs->u.ioctl[3] = MP_OBJ_NEW_SMALL_INT(0); // unused
+            ret = mp_call_method_n_kw(2, 0, vfs->u.ioctl);
         }
     } else {
         // old protocol with sync and count
@@ -247,37 +161,67 @@ DRESULT disk_ioctl (
                 if (vfs->u.old.sync[0] != MP_OBJ_NULL) {
                     mp_call_method_n_kw(0, 0, vfs->u.old.sync);
                 }
-                return RES_OK;
+                break;
 
-            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_COUNT:
+                ret = mp_call_method_n_kw(0, 0, vfs->u.old.count);
+                break;
 
             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;
+                // old protocol has fixed sector size of 512 bytes
+                break;
 
             case IOCTL_INIT:
-                *((DSTATUS*)buff) = disk_initialize(pdrv);
-                return RES_OK;
+                // old protocol doesn't have init
+                break;
+        }
+    }
 
-            case IOCTL_STATUS:
-                *((DSTATUS*)buff) = disk_status(pdrv);
-                return RES_OK;
+    // Second part: convert the result for return
+    switch (cmd) {
+        case CTRL_SYNC:
+            return RES_OK;
 
-            default:
-                return RES_PARERR;
+        case GET_SECTOR_COUNT: {
+            *((DWORD*)buff) = mp_obj_get_int(ret);
+            return RES_OK;
         }
+
+        case GET_SECTOR_SIZE: {
+            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:
+        case IOCTL_STATUS: {
+            DSTATUS stat;
+            if (ret != mp_const_none && MP_OBJ_SMALL_INT_VALUE(ret) != 0) {
+                // error initialising
+                stat = STA_NOINIT;
+            } else if (vfs->writeblocks[0] == MP_OBJ_NULL) {
+                stat = STA_PROTECT;
+            } else {
+                stat = 0;
+            }
+            *((DSTATUS*)buff) = stat;
+            return RES_OK;
+        }
+
+        default:
+            return RES_PARERR;
     }
 }
 

+ 19 - 27
extmod/vfs_fat_file.c

@@ -35,12 +35,6 @@
 #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,
@@ -103,22 +97,9 @@ STATIC mp_uint_t file_obj_write(mp_obj_t self_in, const void *buf, mp_uint_t siz
 }
 
 
-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]);
+    return mp_stream_close(args[0]);
 }
 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(file_obj___exit___obj, 4, 4, file_obj___exit__);
 
@@ -153,6 +134,17 @@ STATIC mp_uint_t file_obj_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg,
         }
         return 0;
 
+    } else if (request == MP_STREAM_CLOSE) {
+        // 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) {
+                *errcode = fresult_to_errno_table[res];
+                return MP_STREAM_ERROR;
+            }
+        }
+        return 0;
+
     } else {
         *errcode = MP_EINVAL;
         return MP_STREAM_ERROR;
@@ -191,11 +183,11 @@ STATIC mp_obj_t file_open(fs_user_mount_t *vfs, const mp_obj_type_t *type, mp_ar
                 break;
             #if MICROPY_PY_IO_FILEIO
             case 'b':
-                type = &mp_type_fileio;
+                type = &mp_type_vfs_fat_fileio;
                 break;
             #endif
             case 't':
-                type = &mp_type_textio;
+                type = &mp_type_vfs_fat_textio;
                 break;
         }
     }
@@ -234,10 +226,10 @@ STATIC const mp_rom_map_elem_t rawfile_locals_dict_table[] = {
     { 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_close), MP_ROM_PTR(&mp_stream_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___del__), MP_ROM_PTR(&mp_stream_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) },
 };
@@ -251,7 +243,7 @@ STATIC const mp_stream_p_t fileio_stream_p = {
     .ioctl = file_obj_ioctl,
 };
 
-const mp_obj_type_t mp_type_fileio = {
+const mp_obj_type_t mp_type_vfs_fat_fileio = {
     { &mp_type_type },
     .name = MP_QSTR_FileIO,
     .print = file_obj_print,
@@ -270,7 +262,7 @@ STATIC const mp_stream_p_t textio_stream_p = {
     .is_text = true,
 };
 
-const mp_obj_type_t mp_type_textio = {
+const mp_obj_type_t mp_type_vfs_fat_textio = {
     { &mp_type_type },
     .name = MP_QSTR_TextIOWrapper,
     .print = file_obj_print,
@@ -289,7 +281,7 @@ STATIC mp_obj_t fatfs_builtin_open_self(mp_obj_t self_in, mp_obj_t path, mp_obj_
     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);
+    return file_open(self, &mp_type_vfs_fat_textio, arg_vals);
 }
 MP_DEFINE_CONST_FUN_OBJ_3(fat_vfs_open_obj, fatfs_builtin_open_self);
 

+ 371 - 0
extmod/vfs_posix.c

@@ -0,0 +1,371 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017-2018 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/vfs.h"
+#include "extmod/vfs_posix.h"
+
+#if MICROPY_VFS_POSIX
+
+#include <string.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
+typedef struct _mp_obj_vfs_posix_t {
+    mp_obj_base_t base;
+    vstr_t root;
+    size_t root_len;
+    bool readonly;
+} mp_obj_vfs_posix_t;
+
+STATIC const char *vfs_posix_get_path_str(mp_obj_vfs_posix_t *self, mp_obj_t path) {
+    if (self->root_len == 0) {
+        return mp_obj_str_get_str(path);
+    } else {
+        self->root.len = self->root_len;
+        vstr_add_str(&self->root, mp_obj_str_get_str(path));
+        return vstr_null_terminated_str(&self->root);
+    }
+}
+
+STATIC mp_obj_t vfs_posix_get_path_obj(mp_obj_vfs_posix_t *self, mp_obj_t path) {
+    if (self->root_len == 0) {
+        return path;
+    } else {
+        self->root.len = self->root_len;
+        vstr_add_str(&self->root, mp_obj_str_get_str(path));
+        return mp_obj_new_str(self->root.buf, self->root.len);
+    }
+}
+
+STATIC mp_obj_t vfs_posix_fun1_helper(mp_obj_t self_in, mp_obj_t path_in, int (*f)(const char*)) {
+    mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
+    int ret = f(vfs_posix_get_path_str(self, path_in));
+    if (ret != 0) {
+        mp_raise_OSError(errno);
+    }
+    return mp_const_none;
+}
+
+STATIC mp_import_stat_t mp_vfs_posix_import_stat(void *self_in, const char *path) {
+    mp_obj_vfs_posix_t *self = self_in;
+    if (self->root_len != 0) {
+        self->root.len = self->root_len;
+        vstr_add_str(&self->root, path);
+        path = vstr_null_terminated_str(&self->root);
+    }
+    struct stat st;
+    if (stat(path, &st) == 0) {
+        if (S_ISDIR(st.st_mode)) {
+            return MP_IMPORT_STAT_DIR;
+        } else if (S_ISREG(st.st_mode)) {
+            return MP_IMPORT_STAT_FILE;
+        }
+    }
+    return MP_IMPORT_STAT_NO_EXIST;
+}
+
+STATIC mp_obj_t vfs_posix_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_vfs_posix_t *vfs = m_new_obj(mp_obj_vfs_posix_t);
+    vfs->base.type = type;
+    vstr_init(&vfs->root, 0);
+    if (n_args == 1) {
+        vstr_add_str(&vfs->root, mp_obj_str_get_str(args[0]));
+        vstr_add_char(&vfs->root, '/');
+    }
+    vfs->root_len = vfs->root.len;
+    vfs->readonly = false;
+
+    return MP_OBJ_FROM_PTR(vfs);
+}
+
+STATIC mp_obj_t vfs_posix_mount(mp_obj_t self_in, mp_obj_t readonly, mp_obj_t mkfs) {
+    mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
+    if (mp_obj_is_true(readonly)) {
+        self->readonly = true;
+    }
+    if (mp_obj_is_true(mkfs)) {
+        mp_raise_OSError(MP_EPERM);
+    }
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_3(vfs_posix_mount_obj, vfs_posix_mount);
+
+STATIC mp_obj_t vfs_posix_umount(mp_obj_t self_in) {
+    (void)self_in;
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(vfs_posix_umount_obj, vfs_posix_umount);
+
+STATIC mp_obj_t vfs_posix_open(mp_obj_t self_in, mp_obj_t path_in, mp_obj_t mode_in) {
+    mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
+    const char *mode = mp_obj_str_get_str(mode_in);
+    if (self->readonly
+        && (strchr(mode, 'w') != NULL || strchr(mode, 'a') != NULL || strchr(mode, '+') != NULL)) {
+        mp_raise_OSError(MP_EROFS);
+    }
+    if (!MP_OBJ_IS_SMALL_INT(path_in)) {
+        path_in = vfs_posix_get_path_obj(self, path_in);
+    }
+    return mp_vfs_posix_file_open(&mp_type_textio, path_in, mode_in);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_3(vfs_posix_open_obj, vfs_posix_open);
+
+STATIC mp_obj_t vfs_posix_chdir(mp_obj_t self_in, mp_obj_t path_in) {
+    return vfs_posix_fun1_helper(self_in, path_in, chdir);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_chdir_obj, vfs_posix_chdir);
+
+STATIC mp_obj_t vfs_posix_getcwd(mp_obj_t self_in) {
+    mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
+    char buf[MICROPY_ALLOC_PATH_MAX + 1];
+    const char *ret = getcwd(buf, sizeof(buf));
+    if (ret == NULL) {
+        mp_raise_OSError(errno);
+    }
+    ret += self->root_len;
+    return mp_obj_new_str(ret, strlen(ret));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(vfs_posix_getcwd_obj, vfs_posix_getcwd);
+
+typedef struct _vfs_posix_ilistdir_it_t {
+    mp_obj_base_t base;
+    mp_fun_1_t iternext;
+    bool is_str;
+    DIR *dir;
+} vfs_posix_ilistdir_it_t;
+
+STATIC mp_obj_t vfs_posix_ilistdir_it_iternext(mp_obj_t self_in) {
+    vfs_posix_ilistdir_it_t *self = MP_OBJ_TO_PTR(self_in);
+
+    if (self->dir == NULL) {
+        return MP_OBJ_STOP_ITERATION;
+    }
+
+    for (;;) {
+        struct dirent *dirent = readdir(self->dir);
+        if (dirent == NULL) {
+            closedir(self->dir);
+            self->dir = NULL;
+            return MP_OBJ_STOP_ITERATION;
+        }
+        const char *fn = dirent->d_name;
+
+        if (fn[0] == '.' && (fn[1] == 0 || fn[1] == '.')) {
+            // skip . and ..
+            continue;
+        }
+
+        // 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));
+        } else {
+            t->items[0] = mp_obj_new_bytes((const byte*)fn, strlen(fn));
+        }
+
+        #ifdef _DIRENT_HAVE_D_TYPE
+        #ifdef DTTOIF
+        t->items[1] = MP_OBJ_NEW_SMALL_INT(DTTOIF(dirent->d_type));
+        #else
+        if (dirent->d_type == DT_DIR) {
+            t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFDIR);
+        } else if (dirent->d_type == DT_REG) {
+            t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFREG);
+        } else {
+            t->items[1] = MP_OBJ_NEW_SMALL_INT(dirent->d_type);
+        }
+        #endif
+        #else
+        // DT_UNKNOWN should have 0 value on any reasonable system
+        t->items[1] = MP_OBJ_NEW_SMALL_INT(0);
+        #endif
+
+        #ifdef _DIRENT_HAVE_D_INO
+        t->items[2] = MP_OBJ_NEW_SMALL_INT(dirent->d_ino);
+        #else
+        t->items[2] = MP_OBJ_NEW_SMALL_INT(0);
+        #endif
+
+        return MP_OBJ_FROM_PTR(t);
+    }
+}
+
+STATIC mp_obj_t vfs_posix_ilistdir(mp_obj_t self_in, mp_obj_t path_in) {
+    mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
+    vfs_posix_ilistdir_it_t *iter = m_new_obj(vfs_posix_ilistdir_it_t);
+    iter->base.type = &mp_type_polymorph_iter;
+    iter->iternext = vfs_posix_ilistdir_it_iternext;
+    iter->is_str = mp_obj_get_type(path_in) == &mp_type_str;
+    const char *path = vfs_posix_get_path_str(self, path_in);
+    if (path[0] == '\0') {
+        path = ".";
+    }
+    iter->dir = opendir(path);
+    if (iter->dir == NULL) {
+        mp_raise_OSError(errno);
+    }
+    return MP_OBJ_FROM_PTR(iter);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_ilistdir_obj, vfs_posix_ilistdir);
+
+typedef struct _mp_obj_listdir_t {
+    mp_obj_base_t base;
+    mp_fun_1_t iternext;
+    DIR *dir;
+} mp_obj_listdir_t;
+
+STATIC mp_obj_t vfs_posix_mkdir(mp_obj_t self_in, mp_obj_t path_in) {
+    mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
+    int ret = mkdir(vfs_posix_get_path_str(self, path_in), 0777);
+    if (ret != 0) {
+        mp_raise_OSError(errno);
+    }
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_mkdir_obj, vfs_posix_mkdir);
+
+STATIC mp_obj_t vfs_posix_remove(mp_obj_t self_in, mp_obj_t path_in) {
+    return vfs_posix_fun1_helper(self_in, path_in, unlink);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_remove_obj, vfs_posix_remove);
+
+STATIC mp_obj_t vfs_posix_rename(mp_obj_t self_in, mp_obj_t old_path_in, mp_obj_t new_path_in) {
+    mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
+    const char *old_path = vfs_posix_get_path_str(self, old_path_in);
+    const char *new_path = vfs_posix_get_path_str(self, new_path_in);
+    int ret = rename(old_path, new_path);
+    if (ret != 0) {
+        mp_raise_OSError(errno);
+    }
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_3(vfs_posix_rename_obj, vfs_posix_rename);
+
+STATIC mp_obj_t vfs_posix_rmdir(mp_obj_t self_in, mp_obj_t path_in) {
+    return vfs_posix_fun1_helper(self_in, path_in, rmdir);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_rmdir_obj, vfs_posix_rmdir);
+
+STATIC mp_obj_t vfs_posix_stat(mp_obj_t self_in, mp_obj_t path_in) {
+    mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
+    struct stat sb;
+    int ret = stat(vfs_posix_get_path_str(self, path_in), &sb);
+    if (ret != 0) {
+        mp_raise_OSError(errno);
+    }
+    mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
+    t->items[0] = MP_OBJ_NEW_SMALL_INT(sb.st_mode);
+    t->items[1] = MP_OBJ_NEW_SMALL_INT(sb.st_ino);
+    t->items[2] = MP_OBJ_NEW_SMALL_INT(sb.st_dev);
+    t->items[3] = MP_OBJ_NEW_SMALL_INT(sb.st_nlink);
+    t->items[4] = MP_OBJ_NEW_SMALL_INT(sb.st_uid);
+    t->items[5] = MP_OBJ_NEW_SMALL_INT(sb.st_gid);
+    t->items[6] = MP_OBJ_NEW_SMALL_INT(sb.st_size);
+    t->items[7] = MP_OBJ_NEW_SMALL_INT(sb.st_atime);
+    t->items[8] = MP_OBJ_NEW_SMALL_INT(sb.st_mtime);
+    t->items[9] = MP_OBJ_NEW_SMALL_INT(sb.st_ctime);
+    return MP_OBJ_FROM_PTR(t);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_stat_obj, vfs_posix_stat);
+
+#ifdef __ANDROID__
+#define USE_STATFS 1
+#endif
+
+#if USE_STATFS
+#include <sys/vfs.h>
+#define STRUCT_STATVFS struct statfs
+#define STATVFS statfs
+#define F_FAVAIL sb.f_ffree
+#define F_NAMEMAX sb.f_namelen
+#define F_FLAG sb.f_flags
+#else
+#include <sys/statvfs.h>
+#define STRUCT_STATVFS struct statvfs
+#define STATVFS statvfs
+#define F_FAVAIL sb.f_favail
+#define F_NAMEMAX sb.f_namemax
+#define F_FLAG sb.f_flag
+#endif
+
+STATIC mp_obj_t vfs_posix_statvfs(mp_obj_t self_in, mp_obj_t path_in) {
+    mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
+    STRUCT_STATVFS sb;
+    const char *path = vfs_posix_get_path_str(self, path_in);
+    int ret = STATVFS(path, &sb);
+    if (ret != 0) {
+        mp_raise_OSError(errno);
+    }
+    mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
+    t->items[0] = MP_OBJ_NEW_SMALL_INT(sb.f_bsize);
+    t->items[1] = MP_OBJ_NEW_SMALL_INT(sb.f_frsize);
+    t->items[2] = MP_OBJ_NEW_SMALL_INT(sb.f_blocks);
+    t->items[3] = MP_OBJ_NEW_SMALL_INT(sb.f_bfree);
+    t->items[4] = MP_OBJ_NEW_SMALL_INT(sb.f_bavail);
+    t->items[5] = MP_OBJ_NEW_SMALL_INT(sb.f_files);
+    t->items[6] = MP_OBJ_NEW_SMALL_INT(sb.f_ffree);
+    t->items[7] = MP_OBJ_NEW_SMALL_INT(F_FAVAIL);
+    t->items[8] = MP_OBJ_NEW_SMALL_INT(F_FLAG);
+    t->items[9] = MP_OBJ_NEW_SMALL_INT(F_NAMEMAX);
+    return MP_OBJ_FROM_PTR(t);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_statvfs_obj, vfs_posix_statvfs);
+
+STATIC const mp_rom_map_elem_t vfs_posix_locals_dict_table[] = {
+    { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&vfs_posix_mount_obj) },
+    { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&vfs_posix_umount_obj) },
+    { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&vfs_posix_open_obj) },
+
+    { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&vfs_posix_chdir_obj) },
+    { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&vfs_posix_getcwd_obj) },
+    { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&vfs_posix_ilistdir_obj) },
+    { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&vfs_posix_mkdir_obj) },
+    { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&vfs_posix_remove_obj) },
+    { MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&vfs_posix_rename_obj) },
+    { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&vfs_posix_rmdir_obj) },
+    { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&vfs_posix_stat_obj) },
+    { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&vfs_posix_statvfs_obj) },
+};
+STATIC MP_DEFINE_CONST_DICT(vfs_posix_locals_dict, vfs_posix_locals_dict_table);
+
+STATIC const mp_vfs_proto_t vfs_posix_proto = {
+    .import_stat = mp_vfs_posix_import_stat,
+};
+
+const mp_obj_type_t mp_type_vfs_posix = {
+    { &mp_type_type },
+    .name = MP_QSTR_VfsPosix,
+    .make_new = vfs_posix_make_new,
+    .protocol = &vfs_posix_proto,
+    .locals_dict = (mp_obj_dict_t*)&vfs_posix_locals_dict,
+};
+
+#endif // MICROPY_VFS_POSIX

+ 38 - 0
extmod/vfs_posix.h

@@ -0,0 +1,38 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 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_POSIX_H
+#define MICROPY_INCLUDED_EXTMOD_VFS_POSIX_H
+
+#include "py/lexer.h"
+#include "py/obj.h"
+
+extern const mp_obj_type_t mp_type_vfs_posix;
+extern const mp_obj_type_t mp_type_vfs_posix_fileio;
+extern const mp_obj_type_t mp_type_vfs_posix_textio;
+
+mp_obj_t mp_vfs_posix_file_open(const mp_obj_type_t *type, mp_obj_t file_in, mp_obj_t mode_in);
+
+#endif // MICROPY_INCLUDED_EXTMOD_VFS_POSIX_H

+ 261 - 0
extmod/vfs_posix_file.c

@@ -0,0 +1,261 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013-2018 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/stream.h"
+#include "extmod/vfs_posix.h"
+
+#if MICROPY_VFS_POSIX
+
+#include <fcntl.h>
+
+#ifdef _WIN32
+#define fsync _commit
+#endif
+
+typedef struct _mp_obj_vfs_posix_file_t {
+    mp_obj_base_t base;
+    int fd;
+} mp_obj_vfs_posix_file_t;
+
+#ifdef MICROPY_CPYTHON_COMPAT
+STATIC void check_fd_is_open(const mp_obj_vfs_posix_file_t *o) {
+    if (o->fd < 0) {
+        nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "I/O operation on closed file"));
+    }
+}
+#else
+#define check_fd_is_open(o)
+#endif
+
+STATIC void vfs_posix_file_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
+    (void)kind;
+    mp_obj_vfs_posix_file_t *self = MP_OBJ_TO_PTR(self_in);
+    mp_printf(print, "<io.%s %d>", mp_obj_get_type_str(self_in), self->fd);
+}
+
+mp_obj_t mp_vfs_posix_file_open(const mp_obj_type_t *type, mp_obj_t file_in, mp_obj_t mode_in) {
+    mp_obj_vfs_posix_file_t *o = m_new_obj(mp_obj_vfs_posix_file_t);
+    const char *mode_s = mp_obj_str_get_str(mode_in);
+
+    int mode_rw = 0, mode_x = 0;
+    while (*mode_s) {
+        switch (*mode_s++) {
+            case 'r':
+                mode_rw = O_RDONLY;
+                break;
+            case 'w':
+                mode_rw = O_WRONLY;
+                mode_x = O_CREAT | O_TRUNC;
+                break;
+            case 'a':
+                mode_rw = O_WRONLY;
+                mode_x = O_CREAT | O_APPEND;
+                break;
+            case '+':
+                mode_rw = O_RDWR;
+                break;
+            #if MICROPY_PY_IO_FILEIO
+            // If we don't have io.FileIO, then files are in text mode implicitly
+            case 'b':
+                type = &mp_type_vfs_posix_fileio;
+                break;
+            case 't':
+                type = &mp_type_vfs_posix_textio;
+                break;
+            #endif
+        }
+    }
+
+    o->base.type = type;
+
+    mp_obj_t fid = file_in;
+
+    if (MP_OBJ_IS_SMALL_INT(fid)) {
+        o->fd = MP_OBJ_SMALL_INT_VALUE(fid);
+        return MP_OBJ_FROM_PTR(o);
+    }
+
+    const char *fname = mp_obj_str_get_str(fid);
+    int fd = open(fname, mode_x | mode_rw, 0644);
+    if (fd == -1) {
+        mp_raise_OSError(errno);
+    }
+    o->fd = fd;
+    return MP_OBJ_FROM_PTR(o);
+}
+
+STATIC mp_obj_t vfs_posix_file_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+    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_arg_val_t arg_vals[MP_ARRAY_SIZE(allowed_args)];
+    mp_arg_parse_all_kw_array(n_args, n_kw, args, MP_ARRAY_SIZE(allowed_args), allowed_args, arg_vals);
+    return mp_vfs_posix_file_open(type, arg_vals[0].u_obj, arg_vals[1].u_obj);
+}
+
+STATIC mp_obj_t vfs_posix_file_fileno(mp_obj_t self_in) {
+    mp_obj_vfs_posix_file_t *self = MP_OBJ_TO_PTR(self_in);
+    check_fd_is_open(self);
+    return MP_OBJ_NEW_SMALL_INT(self->fd);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(vfs_posix_file_fileno_obj, vfs_posix_file_fileno);
+
+STATIC mp_obj_t vfs_posix_file___exit__(size_t n_args, const mp_obj_t *args) {
+    (void)n_args;
+    return mp_stream_close(args[0]);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(vfs_posix_file___exit___obj, 4, 4, vfs_posix_file___exit__);
+
+STATIC mp_uint_t vfs_posix_file_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) {
+    mp_obj_vfs_posix_file_t *o = MP_OBJ_TO_PTR(o_in);
+    check_fd_is_open(o);
+    mp_int_t r = read(o->fd, buf, size);
+    if (r == -1) {
+        *errcode = errno;
+        return MP_STREAM_ERROR;
+    }
+    return r;
+}
+
+STATIC mp_uint_t vfs_posix_file_write(mp_obj_t o_in, const void *buf, mp_uint_t size, int *errcode) {
+    mp_obj_vfs_posix_file_t *o = MP_OBJ_TO_PTR(o_in);
+    check_fd_is_open(o);
+    #if MICROPY_PY_OS_DUPTERM
+    if (o->fd <= STDERR_FILENO) {
+        mp_hal_stdout_tx_strn(buf, size);
+        return size;
+    }
+    #endif
+    mp_int_t r = write(o->fd, buf, size);
+    while (r == -1 && errno == EINTR) {
+        if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) {
+            mp_obj_t obj = MP_STATE_VM(mp_pending_exception);
+            MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL;
+            nlr_raise(obj);
+        }
+        r = write(o->fd, buf, size);
+    }
+    if (r == -1) {
+        *errcode = errno;
+        return MP_STREAM_ERROR;
+    }
+    return r;
+}
+
+STATIC mp_uint_t vfs_posix_file_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) {
+    mp_obj_vfs_posix_file_t *o = MP_OBJ_TO_PTR(o_in);
+    check_fd_is_open(o);
+    switch (request) {
+        case MP_STREAM_FLUSH:
+            if (fsync(o->fd) < 0) {
+                *errcode = errno;
+                return MP_STREAM_ERROR;
+            }
+            return 0;
+        case MP_STREAM_SEEK: {
+            struct mp_stream_seek_t *s = (struct mp_stream_seek_t*)arg;
+            off_t off = lseek(o->fd, s->offset, s->whence);
+            if (off == (off_t)-1) {
+                *errcode = errno;
+                return MP_STREAM_ERROR;
+            }
+            s->offset = off;
+            return 0;
+        }
+        case MP_STREAM_CLOSE:
+            close(o->fd);
+            #ifdef MICROPY_CPYTHON_COMPAT
+            o->fd = -1;
+            #endif
+            return 0;
+        default:
+            *errcode = EINVAL;
+            return MP_STREAM_ERROR;
+    }
+}
+
+STATIC const mp_rom_map_elem_t rawfile_locals_dict_table[] = {
+    { MP_ROM_QSTR(MP_QSTR_fileno), MP_ROM_PTR(&vfs_posix_file_fileno_obj) },
+    { 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_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_flush), MP_ROM_PTR(&mp_stream_flush_obj) },
+    { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) },
+    { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) },
+    { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&vfs_posix_file___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 = vfs_posix_file_read,
+    .write = vfs_posix_file_write,
+    .ioctl = vfs_posix_file_ioctl,
+};
+
+const mp_obj_type_t mp_type_vfs_posix_fileio = {
+    { &mp_type_type },
+    .name = MP_QSTR_FileIO,
+    .print = vfs_posix_file_print,
+    .make_new = vfs_posix_file_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 = vfs_posix_file_read,
+    .write = vfs_posix_file_write,
+    .ioctl = vfs_posix_file_ioctl,
+    .is_text = true,
+};
+
+const mp_obj_type_t mp_type_vfs_posix_textio = {
+    { &mp_type_type },
+    .name = MP_QSTR_TextIOWrapper,
+    .print = vfs_posix_file_print,
+    .make_new = vfs_posix_file_make_new,
+    .getiter = mp_identity_getiter,
+    .iternext = mp_stream_unbuffered_iter,
+    .protocol = &textio_stream_p,
+    .locals_dict = (mp_obj_dict_t*)&rawfile_locals_dict,
+};
+
+const mp_obj_vfs_posix_file_t mp_sys_stdin_obj  = {{&mp_type_textio}, STDIN_FILENO};
+const mp_obj_vfs_posix_file_t mp_sys_stdout_obj = {{&mp_type_textio}, STDOUT_FILENO};
+const mp_obj_vfs_posix_file_t mp_sys_stderr_obj = {{&mp_type_textio}, STDERR_FILENO};
+
+#endif // MICROPY_VFS_POSIX

+ 0 - 2
lib/oofatfs/ff.h

@@ -50,8 +50,6 @@ typedef uint32_t DWORD;
 /* This type MUST be 64-bit (Remove this for C89 compatibility) */
 typedef uint64_t QWORD;
 
-#define FFCONF_H "lib/oofatfs/ffconf.h"
-
 #include FFCONF_H          /* FatFs configuration options */
 
 #if _FATFS != _FFCONF

+ 124 - 0
lib/utils/mpirq.c

@@ -0,0 +1,124 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015 Daniel Campora
+ *               2018 Tobias Badertscher
+ *
+ * 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/runtime.h"
+#include "py/gc.h"
+#include "lib/utils/mpirq.h"
+
+/******************************************************************************
+ DECLARE PUBLIC DATA
+ ******************************************************************************/
+
+const mp_arg_t mp_irq_init_args[] = {
+    { MP_QSTR_handler, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },
+    { MP_QSTR_trigger, MP_ARG_INT, {.u_int = 0} },
+    { MP_QSTR_hard, MP_ARG_BOOL, {.u_bool = false} },
+};
+
+/******************************************************************************
+ DECLARE PRIVATE DATA
+ ******************************************************************************/
+
+/******************************************************************************
+ DEFINE PUBLIC FUNCTIONS
+ ******************************************************************************/
+
+mp_irq_obj_t *mp_irq_new(const mp_irq_methods_t *methods, mp_obj_t parent) {
+    mp_irq_obj_t *self = m_new0(mp_irq_obj_t, 1);
+    self->base.type = &mp_irq_type;
+    self->methods = (mp_irq_methods_t*)methods;
+    self->parent = parent;
+    self->handler = mp_const_none;
+    self->ishard = false;
+    return self;
+}
+
+void mp_irq_handler(mp_irq_obj_t *self) {
+    if (self->handler != mp_const_none) {
+        if (self->ishard) {
+            // When executing code within a handler we must lock the GC to prevent
+            // any memory allocations.
+            gc_lock();
+            nlr_buf_t nlr;
+            if (nlr_push(&nlr) == 0) {
+                mp_call_function_1(self->handler, self->parent);
+                nlr_pop();
+            } else {
+                // Uncaught exception; disable the callback so that it doesn't run again
+                self->methods->trigger(self->parent, 0);
+                self->handler = mp_const_none;
+                printf("Uncaught exception in IRQ callback handler\n");
+                mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val));
+            }
+            gc_unlock();
+        } else {
+            // Schedule call to user function
+            mp_sched_schedule(self->handler, self->parent);
+        }
+    }
+}
+
+/******************************************************************************/
+// MicroPython bindings
+
+STATIC mp_obj_t mp_irq_flags(mp_obj_t self_in) {
+    mp_irq_obj_t *self = MP_OBJ_TO_PTR(self_in);
+    return mp_obj_new_int(self->methods->info(self->parent, MP_IRQ_INFO_FLAGS));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_irq_flags_obj, mp_irq_flags);
+
+STATIC mp_obj_t mp_irq_trigger(size_t n_args, const mp_obj_t *args) {
+    mp_irq_obj_t *self = MP_OBJ_TO_PTR(args[0]);
+    mp_obj_t ret_obj = mp_obj_new_int(self->methods->info(self->parent, MP_IRQ_INFO_TRIGGERS));
+    if (n_args == 2) {
+        // Set trigger
+        self->methods->trigger(self->parent, mp_obj_get_int(args[1]));
+    }
+    return ret_obj;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_irq_trigger_obj, 1, 2, mp_irq_trigger);
+
+STATIC mp_obj_t mp_irq_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, 0, false);
+    mp_irq_handler(MP_OBJ_TO_PTR(self_in));
+    return mp_const_none;
+}
+
+STATIC const mp_rom_map_elem_t mp_irq_locals_dict_table[] = {
+    { MP_ROM_QSTR(MP_QSTR_flags),               MP_ROM_PTR(&mp_irq_flags_obj) },
+    { MP_ROM_QSTR(MP_QSTR_trigger),             MP_ROM_PTR(&mp_irq_trigger_obj) },
+};
+STATIC MP_DEFINE_CONST_DICT(mp_irq_locals_dict, mp_irq_locals_dict_table);
+
+const mp_obj_type_t mp_irq_type = {
+    { &mp_type_type },
+    .name = MP_QSTR_irq,
+    .call = mp_irq_call,
+    .locals_dict = (mp_obj_dict_t*)&mp_irq_locals_dict,
+};

+ 82 - 0
lib/utils/mpirq.h

@@ -0,0 +1,82 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015 Daniel Campora
+ *
+ * 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_MPIRQ_H
+#define MICROPY_INCLUDED_LIB_UTILS_MPIRQ_H
+
+/******************************************************************************
+ DEFINE CONSTANTS
+ ******************************************************************************/
+
+enum {
+    MP_IRQ_ARG_INIT_handler = 0,
+    MP_IRQ_ARG_INIT_trigger,
+    MP_IRQ_ARG_INIT_hard,
+    MP_IRQ_ARG_INIT_NUM_ARGS,
+};
+
+/******************************************************************************
+ DEFINE TYPES
+ ******************************************************************************/
+
+typedef mp_obj_t (*mp_irq_init_t)(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
+typedef mp_uint_t (*mp_irq_uint_method_one_uint_para_t)(mp_obj_t self, mp_uint_t trigger);
+typedef mp_uint_t (*mp_irq_int_method_one_para_t)(mp_obj_t self, mp_uint_t info_type);
+
+enum {
+    MP_IRQ_INFO_FLAGS,
+    MP_IRQ_INFO_TRIGGERS,
+    MP_IRQ_INFO_CNT
+};
+
+typedef struct _mp_irq_methods_t {
+    mp_irq_init_t init;
+    mp_irq_uint_method_one_uint_para_t trigger;
+    mp_irq_int_method_one_para_t info;
+} mp_irq_methods_t;
+
+typedef struct _mp_irq_obj_t {
+    mp_obj_base_t base;
+    mp_irq_methods_t *methods;
+    mp_obj_t parent;
+    mp_obj_t handler;
+    bool ishard;
+} mp_irq_obj_t;
+
+/******************************************************************************
+ DECLARE EXPORTED DATA
+ ******************************************************************************/
+
+extern const mp_arg_t mp_irq_init_args[];
+extern const mp_obj_type_t mp_irq_type;
+
+/******************************************************************************
+ DECLARE PUBLIC FUNCTIONS
+ ******************************************************************************/
+
+mp_irq_obj_t *mp_irq_new(const mp_irq_methods_t *methods, mp_obj_t parent);
+void mp_irq_handler(mp_irq_obj_t *self);
+
+#endif // MICROPY_INCLUDED_LIB_UTILS_MPIRQ_H

+ 12 - 16
lib/utils/printf.c

@@ -26,8 +26,6 @@
 
 #include "py/mpconfig.h"
 
-#if MICROPY_USE_INTERNAL_PRINTF
-
 #include <stdint.h>
 #include <string.h>
 #include <stdarg.h>
@@ -39,6 +37,18 @@
 #include "py/formatfloat.h"
 #endif
 
+#if MICROPY_DEBUG_PRINTERS
+int DEBUG_printf(const char *fmt, ...) {
+    va_list ap;
+    va_start(ap, fmt);
+    int ret = mp_vprintf(MICROPY_DEBUG_PRINTER, fmt, ap);
+    va_end(ap);
+    return ret;
+}
+#endif
+
+#if MICROPY_USE_INTERNAL_PRINTF
+
 #undef putchar  // Some stdlibs have a #define for putchar
 int printf(const char *fmt, ...);
 int vprintf(const char *fmt, va_list ap);
@@ -59,20 +69,6 @@ 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;

+ 10 - 8
lib/utils/pyexec.c

@@ -82,11 +82,7 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input
                 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) {
-                #if MICROPY_PY_IO
                 lex = mp_lexer_new_from_file(source);
-                #else
-                mp_raise_msg(&mp_type_RuntimeError, "script compilation not supported");
-                #endif
             } else {
                 lex = (mp_lexer_t*)source;
             }
@@ -118,11 +114,11 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input
             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)) {
+        if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(((mp_obj_base_t*)nlr.ret_val)->type), MP_OBJ_FROM_PTR(&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);
+            mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val));
             ret = 0;
         }
     }
@@ -135,8 +131,8 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input
         {
             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",
+            printf("qstr:\n  n_pool=%u\n  n_qstr=%u\n  "
+                   "n_str_data_bytes=%u\n  n_total_bytes=%u\n",
                    (unsigned)n_pool, (unsigned)n_qstr, (unsigned)n_str_data_bytes, (unsigned)n_total_bytes);
         }
 
@@ -423,6 +419,12 @@ friendly_repl_reset:
         }
         #endif
 
+        // If the GC is locked at this point there is no way out except a reset,
+        // so force the GC to be unlocked to help the user debug what went wrong.
+        if (MP_STATE_MEM(gc_lock_depth) != 0) {
+            MP_STATE_MEM(gc_lock_depth) = 0;
+        }
+
         vstr_reset(&line);
         int ret = readline(&line, ">>> ");
         mp_parse_input_kind_t parse_input_kind = MP_PARSE_SINGLE_INPUT;

+ 1 - 0
lib/utils/pyexec.h

@@ -50,6 +50,7 @@ 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_obj_t pyb_set_repl_info(mp_obj_t o_value);
 
 MP_DECLARE_CONST_FUN_OBJ_1(pyb_set_repl_info_obj);
 

+ 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));
+}

+ 163 - 0
lib/utils/sys_stdio_mphal.c

@@ -0,0 +1,163 @@
+/*
+ * 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/obj.h"
+#include "py/stream.h"
+#include "py/mperrno.h"
+#include "py/mphal.h"
+
+// TODO make stdin, stdout and stderr writable objects so they can
+// be changed by Python code.  This requires some changes, as these
+// objects are in a read-only module (py/modsys.c).
+
+/******************************************************************************/
+// MicroPython bindings
+
+#define STDIO_FD_IN  (0)
+#define STDIO_FD_OUT (1)
+#define STDIO_FD_ERR (2)
+
+typedef struct _sys_stdio_obj_t {
+    mp_obj_base_t base;
+    int fd;
+} sys_stdio_obj_t;
+
+#if MICROPY_PY_SYS_STDIO_BUFFER
+STATIC const sys_stdio_obj_t stdio_buffer_obj;
+#endif
+
+void stdio_obj_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
+    sys_stdio_obj_t *self = MP_OBJ_TO_PTR(self_in);
+    mp_printf(print, "<io.FileIO %d>", self->fd);
+}
+
+STATIC mp_uint_t stdio_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) {
+    sys_stdio_obj_t *self = MP_OBJ_TO_PTR(self_in);
+    if (self->fd == STDIO_FD_IN) {
+        for (uint i = 0; i < size; i++) {
+            int c = mp_hal_stdin_rx_chr();
+            if (c == '\r') {
+                c = '\n';
+            }
+            ((byte*)buf)[i] = c;
+        }
+        return size;
+    } else {
+        *errcode = MP_EPERM;
+        return MP_STREAM_ERROR;
+    }
+}
+
+STATIC mp_uint_t stdio_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) {
+    sys_stdio_obj_t *self = MP_OBJ_TO_PTR(self_in);
+    if (self->fd == STDIO_FD_OUT || self->fd == STDIO_FD_ERR) {
+        mp_hal_stdout_tx_strn_cooked(buf, size);
+        return size;
+    } else {
+        *errcode = MP_EPERM;
+        return MP_STREAM_ERROR;
+    }
+}
+
+STATIC mp_obj_t stdio_obj___exit__(size_t n_args, const mp_obj_t *args) {
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(stdio_obj___exit___obj, 4, 4, stdio_obj___exit__);
+
+// TODO gc hook to close the file if not already closed
+
+STATIC const mp_rom_map_elem_t stdio_locals_dict_table[] = {
+#if MICROPY_PY_SYS_STDIO_BUFFER
+    { MP_ROM_QSTR(MP_QSTR_buffer), MP_ROM_PTR(&stdio_buffer_obj) },
+#endif
+    { 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_close), MP_ROM_PTR(&mp_identity_obj) },
+    { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_identity_obj) },
+    { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) },
+    { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&stdio_obj___exit___obj) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(stdio_locals_dict, stdio_locals_dict_table);
+
+STATIC const mp_stream_p_t stdio_obj_stream_p = {
+    .read = stdio_read,
+    .write = stdio_write,
+    .is_text = true,
+};
+
+STATIC const mp_obj_type_t stdio_obj_type = {
+    { &mp_type_type },
+    .name = MP_QSTR_FileIO,
+    // TODO .make_new?
+    .print = stdio_obj_print,
+    .getiter = mp_identity_getiter,
+    .iternext = mp_stream_unbuffered_iter,
+    .protocol = &stdio_obj_stream_p,
+    .locals_dict = (mp_obj_dict_t*)&stdio_locals_dict,
+};
+
+const sys_stdio_obj_t mp_sys_stdin_obj = {{&stdio_obj_type}, .fd = STDIO_FD_IN};
+const sys_stdio_obj_t mp_sys_stdout_obj = {{&stdio_obj_type}, .fd = STDIO_FD_OUT};
+const sys_stdio_obj_t mp_sys_stderr_obj = {{&stdio_obj_type}, .fd = STDIO_FD_ERR};
+
+#if MICROPY_PY_SYS_STDIO_BUFFER
+STATIC mp_uint_t stdio_buffer_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) {
+    for (uint i = 0; i < size; i++) {
+        ((byte*)buf)[i] = mp_hal_stdin_rx_chr();
+    }
+    return size;
+}
+
+STATIC mp_uint_t stdio_buffer_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) {
+    mp_hal_stdout_tx_strn(buf, size);
+    return size;
+}
+
+STATIC const mp_stream_p_t stdio_buffer_obj_stream_p = {
+    .read = stdio_buffer_read,
+    .write = stdio_buffer_write,
+    .is_text = false,
+};
+
+STATIC const mp_obj_type_t stdio_buffer_obj_type = {
+    { &mp_type_type },
+    .name = MP_QSTR_FileIO,
+    .print = stdio_obj_print,
+    .getiter = mp_identity_getiter,
+    .iternext = mp_stream_unbuffered_iter,
+    .protocol = &stdio_buffer_obj_stream_p,
+    .locals_dict = (mp_obj_dict_t*)&stdio_locals_dict,
+};
+
+STATIC const sys_stdio_obj_t stdio_buffer_obj = {{&stdio_buffer_obj_type}, .fd = 0}; // fd unused
+#endif

+ 3 - 9
port/genhdr/mpversion.h

@@ -1,10 +1,4 @@
 // This file was generated by py/makeversionhdr.py
-#define MICROPY_GIT_TAG "v1.9.3-477-g7b0a020-dirty"
-#define MICROPY_GIT_HASH "7b0a020-dirty"
-#define MICROPY_BUILD_DATE "2018-03-21"
-#define MICROPY_VERSION_MAJOR (1)
-#define MICROPY_VERSION_MINOR (9)
-#define MICROPY_VERSION_MICRO (3)
-#define MICROPY_VERSION_STRING "1.9.3"
-#define MICROPY_PY_SYS_PLATFORM "rt-thread"
-
+#define MICROPY_GIT_TAG "v1.9.4-773-gafecc12-dirty"
+#define MICROPY_GIT_HASH "afecc12-dirty"
+#define MICROPY_BUILD_DATE "2019-01-09"

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

@@ -667,5 +667,17 @@ QDEF(MP_QSTR_wb, (const byte*)"\x70\x02" "wb")
 QDEF(MP_QSTR_IP_ADD_MEMBERSHIP, (const byte*)"\x6f\x11" "IP_ADD_MEMBERSHIP")
 QDEF(MP_QSTR_sendall, (const byte*)"\x38\x07" "sendall")
 QDEF(MP_QSTR_sha1, (const byte*)"\x8e\x04" "sha1")
+QDEF(MP_QSTR_readbit, (const byte*)"\x08\x07" "readbit")
+QDEF(MP_QSTR_readbyte, (const byte*)"\x7d\x08" "readbyte")
+QDEF(MP_QSTR_writebit, (const byte*)"\xc7\x08" "writebit")
+QDEF(MP_QSTR_flags, (const byte*)"\xfa\x05" "flags")
+QDEF(MP_QSTR_trigger, (const byte*)"\x9d\x07" "trigger")
+QDEF(MP_QSTR_handler, (const byte*)"\xdd\x07" "handler")
+QDEF(MP_QSTR_crc8, (const byte*)"\xcf\x04" "crc8")
+QDEF(MP_QSTR_hard, (const byte*)"\xda\x04" "hard")
+QDEF(MP_QSTR_irq, (const byte*)"\x8f\x03" "irq")
+QDEF(MP_QSTR_onewire, (const byte*)"\x28\x07" "onewire")
+QDEF(MP_QSTR___dir__, (const byte*)"\x7a\x07" "__dir__")
+QDEF(MP_QSTR___int__, (const byte*)"\x16\x07" "__int__")
 
 // This file was automatically generated by makeqstrdata.py

+ 1 - 0
port/mpconfigport.h

@@ -288,6 +288,7 @@ typedef long mp_off_t;
 
 #define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len)
 
+#define MICROPY_PY_SYS_PLATFORM "rt-thread"
 #define MICROPY_HW_BOARD_NAME "Universal python platform"
 #define MICROPY_HW_MCU_NAME   "RT-Thread"
 #define MICROPY_PY_PATH       "/libs/mpy/"

+ 0 - 23
port/mphalport.c

@@ -56,29 +56,6 @@ void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
     }
 }
 
-void mp_hal_stdout_tx_str(const char *str) {
-    mp_hal_stdout_tx_strn(str, strlen(str));
-}
-
-void mp_hal_stdout_tx_strn_cooked(const char *str, uint32_t len) {
-    const char *last = str;
-    while (len--) {
-        if (*str == '\n') {
-            if (str > last) {
-                mp_hal_stdout_tx_strn(last, str - last);
-            }
-            mp_hal_stdout_tx_strn("\r\n", 2);
-            ++str;
-            last = str;
-        } else {
-            ++str;
-        }
-    }
-    if (str > last) {
-        mp_hal_stdout_tx_strn(last, str - last);
-    }
-}
-
 mp_uint_t mp_hal_ticks_us(void) {
     return rt_tick_get() * 1000000UL / RT_TICK_PER_SECOND;
 }

+ 8 - 0
port/mpy_main.c

@@ -142,6 +142,8 @@ void mpy_main(const char *filename) {
     mp_thread_deinit();
 #endif
 
+    gc_sweep_all();
+
     mp_deinit();
 
     rt_free(heap);
@@ -167,6 +169,12 @@ void MP_WEAK __assert_func(const char *file, int line, const char *func, const c
 }
 #endif
 
+#if !MICROPY_VFS
+mp_lexer_t *mp_lexer_new_from_file(const char *filename) {
+    mp_raise_OSError(MP_ENOENT);
+}
+#endif
+
 #include <stdarg.h>
 
 int DEBUG_printf(const char *format, ...)

+ 7 - 2
py/argcheck.c

@@ -29,14 +29,19 @@
 
 #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) {
+void mp_arg_check_num_sig(size_t n_args, size_t n_kw, uint32_t sig) {
     // TODO maybe take the function name as an argument so we can print nicer error messages
 
+    // The reverse of MP_OBJ_FUN_MAKE_SIG
+    bool takes_kw = sig & 1;
+    size_t n_args_min = sig >> 17;
+    size_t n_args_max = (sig >> 1) & 0xffff;
+
     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");
+            mp_raise_TypeError("function doesn't take keyword arguments");
         }
     }
 

+ 23 - 12
py/asmarm.c

@@ -273,6 +273,21 @@ void asm_arm_mov_reg_local_addr(asm_arm_t *as, uint rd, int local_num) {
     emit_al(as, asm_arm_op_add_imm(rd, ASM_ARM_REG_SP, local_num << 2));
 }
 
+void asm_arm_mov_reg_pcrel(asm_arm_t *as, uint reg_dest, 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 -= 12 + 8; // adjust for load of rel, and then PC+8 prefetch of add_reg_reg_reg
+
+    // To load rel int reg_dest, insert immediate into code and jump over it
+    emit_al(as, 0x59f0000 | (reg_dest << 12)); // ldr rd, [pc]
+    emit_al(as, 0xa000000); // b pc
+    emit(as, rel);
+
+    // Do reg_dest += PC
+    asm_arm_add_reg_reg_reg(as, reg_dest, reg_dest, ASM_ARM_REG_PC);
+}
+
 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);
@@ -347,19 +362,15 @@ 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;
-    }
+void asm_arm_bl_ind(asm_arm_t *as, uint fun_id, uint reg_temp) {
+    // The table offset should fit into the ldr instruction
+    assert(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]
+}
 
-    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);
+void asm_arm_bx_reg(asm_arm_t *as, uint reg_src) {
+    emit_al(as, 0x012fff10 | reg_src);
 }
 
 #endif // MICROPY_EMIT_ARM

+ 14 - 5
py/asmarm.h

@@ -98,6 +98,7 @@ 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_mov_reg_pcrel(asm_arm_t *as, uint reg_dest, uint label);
 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);
 
@@ -120,7 +121,11 @@ 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);
+void asm_arm_bl_ind(asm_arm_t *as, uint fun_id, uint reg_temp);
+void asm_arm_bx_reg(asm_arm_t *as, uint reg_src);
+
+// Holds a pointer to mp_fun_table
+#define ASM_ARM_REG_FUN_TABLE ASM_ARM_REG_R7
 
 #if GENERIC_ASM_API
 
@@ -144,18 +149,21 @@ void asm_arm_bl_ind(asm_arm_t *as, void *fun_ptr, uint fun_id, uint reg_temp);
 #define REG_LOCAL_3 ASM_ARM_REG_R6
 #define REG_LOCAL_NUM (3)
 
+// Holds a pointer to mp_fun_table
+#define REG_FUN_TABLE ASM_ARM_REG_FUN_TABLE
+
 #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) \
+#define ASM_JUMP_IF_REG_ZERO(as, reg, label, bool_test) \
     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) \
+#define ASM_JUMP_IF_REG_NONZERO(as, reg, label, bool_test) \
     do { \
         asm_arm_cmp_reg_i8(as, reg, 0); \
         asm_arm_bcc_label(as, ASM_ARM_CC_NE, label); \
@@ -165,14 +173,15 @@ void asm_arm_bl_ind(asm_arm_t *as, void *fun_ptr, uint fun_id, uint reg_temp);
         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_JUMP_REG(as, reg) asm_arm_bx_reg((as), (reg))
+#define ASM_CALL_IND(as, idx) asm_arm_bl_ind(as, idx, ASM_ARM_REG_R3)
 
 #define ASM_MOV_LOCAL_REG(as, local_num, reg_src) asm_arm_mov_local_reg((as), (local_num), (reg_src))
 #define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_arm_mov_reg_i32((as), (reg_dest), (imm))
-#define ASM_MOV_REG_ALIGNED_IMM(as, reg_dest, imm) asm_arm_mov_reg_i32((as), (reg_dest), (imm))
 #define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_arm_mov_reg_local((as), (reg_dest), (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_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_arm_mov_reg_local_addr((as), (reg_dest), (local_num))
+#define ASM_MOV_REG_PCREL(as, reg_dest, label) asm_arm_mov_reg_pcrel((as), (reg_dest), (label))
 
 #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))

+ 48 - 37
py/asmthumb.c

@@ -36,6 +36,8 @@
 #include "py/mphal.h"
 #include "py/asmthumb.h"
 
+#define UNSIGNED_FIT5(x) ((uint32_t)(x) < 32)
+#define UNSIGNED_FIT7(x) ((uint32_t)(x) < 128)
 #define UNSIGNED_FIT8(x) (((x) & 0xffffff00) == 0)
 #define UNSIGNED_FIT16(x) (((x) & 0xffff0000) == 0)
 #define SIGNED_FIT8(x) (((x) & 0xffffff80) == 0) || (((x) & 0xffffff80) == 0xffffff80)
@@ -43,6 +45,15 @@
 #define SIGNED_FIT12(x) (((x) & 0xfffff800) == 0) || (((x) & 0xfffff800) == 0xfffff800)
 #define SIGNED_FIT23(x) (((x) & 0xffc00000) == 0) || (((x) & 0xffc00000) == 0xffc00000)
 
+// Note: these actually take an imm12 but the high-bit is not encoded here
+#define OP_ADD_W_RRI_HI(reg_src) (0xf200 | (reg_src))
+#define OP_ADD_W_RRI_LO(reg_dest, imm11) ((imm11 << 4 & 0x7000) | reg_dest << 8 | (imm11 & 0xff))
+#define OP_SUB_W_RRI_HI(reg_src) (0xf2a0 | (reg_src))
+#define OP_SUB_W_RRI_LO(reg_dest, imm11) ((imm11 << 4 & 0x7000) | reg_dest << 8 | (imm11 & 0xff))
+
+#define OP_LDR_W_HI(reg_base) (0xf8d0 | (reg_base))
+#define OP_LDR_W_LO(reg_dest, imm12) ((reg_dest) << 12 | (imm12))
+
 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);
 }
@@ -51,7 +62,7 @@ void asm_thumb_end_pass(asm_thumb_t *as) {
     (void)as;
     // could check labels are resolved...
 
-    #if defined(MCU_SERIES_F7)
+    #if __ICACHE_PRESENT == 1
     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);
@@ -89,6 +100,7 @@ STATIC void asm_thumb_write_word32(asm_thumb_t *as, int w32) {
 #define OP_POP_RLIST(rlolist)       (0xbc00 | (rlolist))
 #define OP_POP_RLIST_PC(rlolist)    (0xbc00 | 0x0100 | (rlolist))
 
+// The number of words must fit in 7 unsigned bits
 #define OP_ADD_SP(num_words) (0xb000 | (num_words))
 #define OP_SUB_SP(num_words) (0xb080 | (num_words))
 
@@ -142,7 +154,11 @@ void asm_thumb_entry(asm_thumb_t *as, int num_locals) {
     }
     asm_thumb_op16(as, OP_PUSH_RLIST_LR(reglist));
     if (stack_adjust > 0) {
-        asm_thumb_op16(as, OP_SUB_SP(stack_adjust));
+        if (UNSIGNED_FIT7(stack_adjust)) {
+            asm_thumb_op16(as, OP_SUB_SP(stack_adjust));
+        } else {
+            asm_thumb_op32(as, OP_SUB_W_RRI_HI(ASM_THUMB_REG_SP), OP_SUB_W_RRI_LO(ASM_THUMB_REG_SP, stack_adjust * 4));
+        }
     }
     as->push_reglist = reglist;
     as->stack_adjust = stack_adjust;
@@ -150,7 +166,11 @@ void asm_thumb_entry(asm_thumb_t *as, int num_locals) {
 
 void asm_thumb_exit(asm_thumb_t *as) {
     if (as->stack_adjust > 0) {
-        asm_thumb_op16(as, OP_ADD_SP(as->stack_adjust));
+        if (UNSIGNED_FIT7(as->stack_adjust)) {
+            asm_thumb_op16(as, OP_ADD_SP(as->stack_adjust));
+        } else {
+            asm_thumb_op32(as, OP_ADD_W_RRI_HI(ASM_THUMB_REG_SP), OP_ADD_W_RRI_LO(ASM_THUMB_REG_SP, as->stack_adjust * 4));
+        }
     }
     asm_thumb_op16(as, OP_POP_RLIST_PC(as->push_reglist));
 }
@@ -269,21 +289,6 @@ void asm_thumb_mov_reg_i32_optimised(asm_thumb_t *as, uint reg_dest, int 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))
 
@@ -310,6 +315,27 @@ void asm_thumb_mov_reg_local_addr(asm_thumb_t *as, uint rlo_dest, int local_num)
     asm_thumb_op16(as, OP_ADD_REG_SP_OFFSET(rlo_dest, word_offset));
 }
 
+void asm_thumb_mov_reg_pcrel(asm_thumb_t *as, uint rlo_dest, uint label) {
+    mp_uint_t dest = get_label_dest(as, label);
+    mp_int_t rel = dest - as->base.code_offset;
+    rel -= 4 + 4; // adjust for mov_reg_i16 and then PC+4 prefetch of add_reg_reg
+    rel |= 1; // to stay in Thumb state when jumping to this address
+    asm_thumb_mov_reg_i16(as, ASM_THUMB_OP_MOVW, rlo_dest, rel); // 4 bytes
+    asm_thumb_add_reg_reg(as, rlo_dest, ASM_THUMB_REG_R15); // 2 bytes
+}
+
+static inline void asm_thumb_ldr_reg_reg_i12(asm_thumb_t *as, uint reg_dest, uint reg_base, uint word_offset) {
+    asm_thumb_op32(as, OP_LDR_W_HI(reg_base), OP_LDR_W_LO(reg_dest, word_offset * 4));
+}
+
+void asm_thumb_ldr_reg_reg_i12_optimised(asm_thumb_t *as, uint reg_dest, uint reg_base, uint word_offset) {
+    if (reg_dest < ASM_THUMB_REG_R8 && reg_base < ASM_THUMB_REG_R8 && UNSIGNED_FIT5(word_offset)) {
+        asm_thumb_ldr_rlo_rlo_i5(as, reg_dest, reg_base, word_offset);
+    } else {
+        asm_thumb_ldr_reg_reg_i12(as, reg_dest, reg_base, 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))
@@ -355,25 +381,10 @@ void asm_thumb_bcc_label(asm_thumb_t *as, int cond, uint label) {
 #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));
-    }
+void asm_thumb_bl_ind(asm_thumb_t *as, uint fun_id, uint reg_temp) {
+    // Load ptr to function from table, indexed by fun_id, then call it
+    asm_thumb_ldr_reg_reg_i12_optimised(as, reg_temp, ASM_THUMB_REG_FUN_TABLE, fun_id);
+    asm_thumb_op16(as, OP_BLX(reg_temp));
 }
 
 #endif // MICROPY_EMIT_THUMB || MICROPY_EMIT_INLINE_THUMB

+ 37 - 7
py/asmthumb.h

@@ -26,6 +26,7 @@
 #ifndef MICROPY_INCLUDED_PY_ASMTHUMB_H
 #define MICROPY_INCLUDED_PY_ASMTHUMB_H
 
+#include <assert.h>
 #include "py/misc.h"
 #include "py/asmbase.h"
 
@@ -45,6 +46,7 @@
 #define ASM_THUMB_REG_R13 (13)
 #define ASM_THUMB_REG_R14 (14)
 #define ASM_THUMB_REG_R15 (15)
+#define ASM_THUMB_REG_SP  (ASM_THUMB_REG_R13)
 #define ASM_THUMB_REG_LR  (REG_R14)
 
 #define ASM_THUMB_CC_EQ (0x0)
@@ -179,6 +181,26 @@ 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 5: hi register operations (add, cmp, mov, bx)
+// For add/cmp/mov, at least one of the args must be a high register
+
+#define ASM_THUMB_FORMAT_5_ADD (0x4400)
+#define ASM_THUMB_FORMAT_5_BX (0x4700)
+
+#define ASM_THUMB_FORMAT_5_ENCODE(op, r_dest, r_src) \
+    ((op) | ((r_dest) << 4 & 0x0080) | ((r_src) << 3) | ((r_dest) & 0x0007))
+
+static inline void asm_thumb_format_5(asm_thumb_t *as, uint op, uint r_dest, uint r_src) {
+    asm_thumb_op16(as, ASM_THUMB_FORMAT_5_ENCODE(op, r_dest, r_src));
+}
+
+static inline void asm_thumb_add_reg_reg(asm_thumb_t *as, uint r_dest, uint r_src) {
+    asm_thumb_format_5(as, ASM_THUMB_FORMAT_5_ADD, r_dest, r_src);
+}
+static inline void asm_thumb_bx_reg(asm_thumb_t *as, uint r_src) {
+    asm_thumb_format_5(as, ASM_THUMB_FORMAT_5_BX, 0, r_src);
+}
+
 // FORMAT 9: load/store with immediate offset
 // For word transfers the offset must be aligned, and >>2
 
@@ -228,14 +250,19 @@ 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_mov_reg_pcrel(asm_thumb_t *as, uint rlo_dest, uint label);
+
+void asm_thumb_ldr_reg_reg_i12_optimised(asm_thumb_t *as, uint reg_dest, uint reg_base, uint byte_offset); // 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
+void asm_thumb_bl_ind(asm_thumb_t *as, uint fun_id, uint reg_temp); // convenience
+
+// Holds a pointer to mp_fun_table
+#define ASM_THUMB_REG_FUN_TABLE ASM_THUMB_REG_R7
 
 #if GENERIC_ASM_API
 
@@ -260,18 +287,20 @@ void asm_thumb_bl_ind(asm_thumb_t *as, void *fun_ptr, uint fun_id, uint reg_temp
 #define REG_LOCAL_3 ASM_THUMB_REG_R6
 #define REG_LOCAL_NUM (3)
 
+#define REG_FUN_TABLE ASM_THUMB_REG_FUN_TABLE
+
 #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) \
+#define ASM_JUMP_IF_REG_ZERO(as, reg, label, bool_test) \
     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) \
+#define ASM_JUMP_IF_REG_NONZERO(as, reg, label, bool_test) \
     do { \
         asm_thumb_cmp_rlo_i8(as, reg, 0); \
         asm_thumb_bcc_label(as, ASM_THUMB_CC_NE, label); \
@@ -281,14 +310,15 @@ void asm_thumb_bl_ind(asm_thumb_t *as, void *fun_ptr, uint fun_id, uint reg_temp
         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_JUMP_REG(as, reg) asm_thumb_bx_reg((as), (reg))
+#define ASM_CALL_IND(as, idx) asm_thumb_bl_ind(as, idx, ASM_THUMB_REG_R3)
 
 #define ASM_MOV_LOCAL_REG(as, local_num, reg) asm_thumb_mov_local_reg((as), (local_num), (reg))
 #define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_thumb_mov_reg_i32_optimised((as), (reg_dest), (imm))
-#define ASM_MOV_REG_ALIGNED_IMM(as, reg_dest, imm) asm_thumb_mov_reg_i32_aligned((as), (reg_dest), (imm))
 #define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_thumb_mov_reg_local((as), (reg_dest), (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_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_thumb_mov_reg_local_addr((as), (reg_dest), (local_num))
+#define ASM_MOV_REG_PCREL(as, rlo_dest, label) asm_thumb_mov_reg_pcrel((as), (rlo_dest), (label))
 
 #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))
@@ -300,7 +330,7 @@ void asm_thumb_bl_ind(asm_thumb_t *as, void *fun_ptr, uint fun_id, uint reg_temp
 #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_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_thumb_ldr_reg_reg_i12_optimised((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)

+ 55 - 55
py/asmx64.c

@@ -73,8 +73,10 @@
 #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_TEST_R64_WITH_RM64 (0x85) /* /r */
 #define OPCODE_JMP_REL8          (0xeb)
 #define OPCODE_JMP_REL32         (0xe9)
+#define OPCODE_JMP_RM64          (0xff) /* /4 */
 #define OPCODE_JCC_REL8          (0x70) /* | jcc type */
 #define OPCODE_JCC_REL32_A       (0x0f)
 #define OPCODE_JCC_REL32_B       (0x80) /* | jcc type */
@@ -181,21 +183,22 @@ STATIC void asm_x64_write_word32_to(asm_x64_t *as, int offset, int 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));
+    uint8_t rm_disp;
+    if (disp_offset == 0 && (disp_r64 & 7) != ASM_X64_REG_RBP) {
+        rm_disp = MODRM_RM_DISP0;
     } 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));
+        rm_disp = MODRM_RM_DISP8;
     } else {
-        asm_x64_write_byte_1(as, MODRM_R64(r64) | MODRM_RM_DISP32 | MODRM_RM_R64(disp_r64));
+        rm_disp = MODRM_RM_DISP32;
+    }
+    asm_x64_write_byte_1(as, MODRM_R64(r64) | rm_disp | MODRM_RM_R64(disp_r64));
+    if ((disp_r64 & 7) == ASM_X64_REG_RSP) {
+        // Special case for rsp and r12, they need a SIB byte
+        asm_x64_write_byte_1(as, 0x24);
+    }
+    if (rm_disp == MODRM_RM_DISP8) {
+        asm_x64_write_byte_1(as, IMM32_L0(disp_offset));
+    } else if (rm_disp == MODRM_RM_DISP32) {
         asm_x64_write_word32(as, disp_offset);
     }
 }
@@ -361,15 +364,6 @@ void asm_x64_mov_i64_to_r64_optimised(asm_x64_t *as, int64_t src_i64, int dest_r
     }
 }
 
-// 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);
 }
@@ -465,17 +459,25 @@ void asm_x64_cmp_i32_with_r32(asm_x64_t *as, int src_i32, int src_r32) {
 */
 
 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);
+    assert(src_r64_a < 8);
+    assert(src_r64_b < 8);
     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_test_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_TEST_R64_WITH_RM64);
+}
+
 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));
 }
 
+void asm_x64_jmp_reg(asm_x64_t *as, int src_r64) {
+    assert(src_r64 < 8);
+    asm_x64_write_byte_2(as, OPCODE_JMP_RM64, MODRM_R64(4) | MODRM_RM_REG | MODRM_RM_R64(src_r64));
+}
+
 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];
@@ -528,63 +530,72 @@ 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) {
     assert(num_locals >= 0);
     asm_x64_push_r64(as, ASM_X64_REG_RBP);
-    asm_x64_mov_r64_r64(as, ASM_X64_REG_RBP, ASM_X64_REG_RSP);
-    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);
+    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);
     as->num_locals = num_locals;
 }
 
 void asm_x64_exit(asm_x64_t *as) {
+    asm_x64_sub_r64_i32(as, ASM_X64_REG_RSP, -as->num_locals * WORD_SIZE);
     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_pop_r64(as, ASM_X64_REG_RBP);
     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
+//  - RSP points to the first local
 //
-//                          | RBP
-//                          v
+//  | RSP
+//  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;
+STATIC int asm_x64_local_offset_from_rsp(asm_x64_t *as, int local_num) {
+    (void)as;
+    // Stack is full descending, RSP points to local0
+    return 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);
+    asm_x64_mov_mem64_to_r64(as, ASM_X64_REG_RSP, asm_x64_local_offset_from_rsp(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));
+    asm_x64_mov_r64_to_mem64(as, src_r64, ASM_X64_REG_RSP, asm_x64_local_offset_from_rsp(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);
+    int offset = asm_x64_local_offset_from_rsp(as, local_num);
     if (offset == 0) {
-        asm_x64_mov_r64_r64(as, dest_r64, ASM_X64_REG_RBP);
+        asm_x64_mov_r64_r64(as, dest_r64, ASM_X64_REG_RSP);
     } else {
-        asm_x64_lea_disp_to_r64(as, ASM_X64_REG_RBP, offset, dest_r64);
+        asm_x64_lea_disp_to_r64(as, ASM_X64_REG_RSP, offset, dest_r64);
     }
 }
 
+void asm_x64_mov_reg_pcrel(asm_x64_t *as, int dest_r64, mp_uint_t label) {
+    mp_uint_t dest = get_label_dest(as, label);
+    mp_int_t rel = dest - (as->base.code_offset + 7);
+    asm_x64_write_byte_3(as, REX_PREFIX | REX_W | REX_R_FROM_R64(dest_r64), OPCODE_LEA_MEM_TO_R64, MODRM_R64(dest_r64) | MODRM_RM_R64(5));
+    asm_x64_write_word32(as, rel);
+}
+
 /*
 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));
+    asm_x64_push_disp(as, ASM_X64_REG_RSP, asm_x64_local_offset_from_rsp(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_mov_r64_r64(as, temp_r64, ASM_X64_REG_RSP);
+    asm_x64_add_i32_to_r32(as, asm_x64_local_offset_from_rsp(as, local_num), temp_r64);
     asm_x64_push_r64(as, temp_r64);
 }
 */
@@ -610,21 +621,10 @@ void asm_x64_call_i1(asm_x64_t *as, void* func, int i1) {
 }
 */
 
-void asm_x64_call_ind(asm_x64_t *as, void *ptr, int temp_r64) {
+void asm_x64_call_ind(asm_x64_t *as, size_t fun_id, 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_mov_mem64_to_r64(as, ASM_X64_REG_FUN_TABLE, fun_id * WORD_SIZE, temp_r64);
     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

+ 25 - 8
py/asmx64.h

@@ -85,7 +85,6 @@ 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);
@@ -104,7 +103,9 @@ 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_test_r64_with_r64(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_reg(asm_x64_t *as, int src_r64);
 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);
@@ -112,7 +113,11 @@ 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);
+void asm_x64_mov_reg_pcrel(asm_x64_t *as, int dest_r64, mp_uint_t label);
+void asm_x64_call_ind(asm_x64_t* as, size_t fun_id, int temp_r32);
+
+// Holds a pointer to mp_fun_table
+#define ASM_X64_REG_FUN_TABLE ASM_X64_REG_RBP
 
 #if GENERIC_ASM_API
 
@@ -139,20 +144,31 @@ void asm_x64_call_ind(asm_x64_t* as, void* ptr, int temp_r32);
 #define REG_LOCAL_3 ASM_X64_REG_R13
 #define REG_LOCAL_NUM (3)
 
+// Holds a pointer to mp_fun_table
+#define REG_FUN_TABLE ASM_X64_REG_FUN_TABLE
+
 #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) \
+#define ASM_JUMP_IF_REG_ZERO(as, reg, label, bool_test) \
     do { \
-        asm_x64_test_r8_with_r8(as, reg, reg); \
+        if (bool_test) { \
+            asm_x64_test_r8_with_r8((as), (reg), (reg)); \
+        } else { \
+            asm_x64_test_r64_with_r64((as), (reg), (reg)); \
+        } \
         asm_x64_jcc_label(as, ASM_X64_CC_JZ, label); \
     } while (0)
-#define ASM_JUMP_IF_REG_NONZERO(as, reg, label) \
+#define ASM_JUMP_IF_REG_NONZERO(as, reg, label, bool_test) \
     do { \
-        asm_x64_test_r8_with_r8(as, reg, reg); \
+        if (bool_test) { \
+            asm_x64_test_r8_with_r8((as), (reg), (reg)); \
+        } else { \
+            asm_x64_test_r64_with_r64((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) \
@@ -160,14 +176,15 @@ void asm_x64_call_ind(asm_x64_t* as, void* ptr, int temp_r32);
         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_JUMP_REG(as, reg) asm_x64_jmp_reg((as), (reg))
+#define ASM_CALL_IND(as, idx) asm_x64_call_ind(as, idx, ASM_X64_REG_RAX)
 
 #define ASM_MOV_LOCAL_REG(as, local_num, reg_src) asm_x64_mov_r64_to_local((as), (reg_src), (local_num))
 #define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_x64_mov_i64_to_r64_optimised((as), (imm), (reg_dest))
-#define ASM_MOV_REG_ALIGNED_IMM(as, reg_dest, imm) asm_x64_mov_i64_to_r64_aligned((as), (imm), (reg_dest))
 #define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_x64_mov_local_to_r64((as), (local_num), (reg_dest))
 #define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_x64_mov_r64_r64((as), (reg_dest), (reg_src))
 #define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_x64_mov_local_addr_to_r64((as), (local_num), (reg_dest))
+#define ASM_MOV_REG_PCREL(as, reg_dest, label) asm_x64_mov_reg_pcrel((as), (reg_dest), (label))
 
 #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))

+ 68 - 54
py/asmx86.c

@@ -73,8 +73,10 @@
 #define OPCODE_CMP_R32_WITH_RM32 (0x39)
 //#define OPCODE_CMP_RM32_WITH_R32 (0x3b)
 #define OPCODE_TEST_R8_WITH_RM8  (0x84) /* /r */
+#define OPCODE_TEST_R32_WITH_RM32 (0x85) /* /r */
 #define OPCODE_JMP_REL8          (0xeb)
 #define OPCODE_JMP_REL32         (0xe9)
+#define OPCODE_JMP_RM32          (0xff) /* /4 */
 #define OPCODE_JCC_REL8          (0x70) /* | jcc type */
 #define OPCODE_JCC_REL32_A       (0x0f)
 #define OPCODE_JCC_REL32_B       (0x80) /* | jcc type */
@@ -135,14 +137,22 @@ STATIC void asm_x86_write_word32(asm_x86_t *as, int 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);
-
+    uint8_t rm_disp;
     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));
+        rm_disp = MODRM_RM_DISP0;
     } 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));
+        rm_disp = MODRM_RM_DISP8;
     } else {
-        asm_x86_write_byte_1(as, MODRM_R32(r32) | MODRM_RM_DISP32 | MODRM_RM_R32(disp_r32));
+        rm_disp = MODRM_RM_DISP32;
+    }
+    asm_x86_write_byte_1(as, MODRM_R32(r32) | rm_disp | MODRM_RM_R32(disp_r32));
+    if (disp_r32 == ASM_X86_REG_ESP) {
+        // Special case for esp, it needs a SIB byte
+        asm_x86_write_byte_1(as, 0x24);
+    }
+    if (rm_disp == MODRM_RM_DISP8) {
+        asm_x86_write_byte_1(as, IMM32_L0(disp_offset));
+    } else if (rm_disp == MODRM_RM_DISP32) {
         asm_x86_write_word32(as, disp_offset);
     }
 }
@@ -151,9 +161,11 @@ STATIC void asm_x86_generic_r32_r32(asm_x86_t *as, int dest_r32, int src_r32, in
     asm_x86_write_byte_2(as, op, MODRM_R32(src_r32) | MODRM_RM_REG | MODRM_RM_R32(dest_r32));
 }
 
+#if 0
 STATIC void asm_x86_nop(asm_x86_t *as) {
     asm_x86_write_byte_1(as, OPCODE_NOP);
 }
+#endif
 
 STATIC void asm_x86_push_r32(asm_x86_t *as, int src_r32) {
     asm_x86_write_byte_1(as, OPCODE_PUSH_R32 | src_r32);
@@ -229,15 +241,6 @@ void asm_x86_mov_i32_to_r32(asm_x86_t *as, int32_t src_i32, int 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);
 }
@@ -312,7 +315,7 @@ void asm_x86_sar_r32_by_imm(asm_x86_t *as, int r32, int 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));
+    asm_x86_generic_r32_r32(as, src_r32_b, src_r32_a, OPCODE_CMP_R32_WITH_RM32);
 }
 
 #if 0
@@ -328,16 +331,21 @@ void asm_x86_cmp_i32_with_r32(asm_x86_t *as, int src_i32, int src_r32) {
 #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_test_r32_with_r32(asm_x86_t *as, int src_r32_a, int src_r32_b) {
+    asm_x86_generic_r32_r32(as, src_r32_b, src_r32_a, OPCODE_TEST_R32_WITH_RM32);
+}
+
 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));
 }
 
+void asm_x86_jmp_reg(asm_x86_t *as, int src_r32) {
+    asm_x86_write_byte_2(as, OPCODE_JMP_RM32, MODRM_R32(4) | MODRM_RM_REG | MODRM_RM_R32(src_r32));
+}
+
 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];
@@ -390,87 +398,103 @@ 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, int num_locals) {
     assert(num_locals >= 0);
     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
+    num_locals |= 1; // make it odd so stack is aligned on 16 byte boundary
+    asm_x86_sub_r32_i32(as, ASM_X86_REG_ESP, num_locals * WORD_SIZE);
     as->num_locals = num_locals;
 }
 
 void asm_x86_exit(asm_x86_t *as) {
+    asm_x86_sub_r32_i32(as, ASM_X86_REG_ESP, -as->num_locals * WORD_SIZE);
     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_pop_r32(as, ASM_X86_REG_EBP);
     asm_x86_ret(as);
 }
 
+STATIC int asm_x86_arg_offset_from_esp(asm_x86_t *as, size_t arg_num) {
+    // Above esp are: locals, 4 saved registers, return eip, arguments
+    return (as->num_locals + 4 + 1 + arg_num) * WORD_SIZE;
+}
+
 #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);
+    asm_x86_push_disp(as, ASM_X86_REG_ESP, asm_x86_arg_offset_from_esp(as, src_arg_num));
 }
 #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);
+    asm_x86_mov_mem32_to_r32(as, ASM_X86_REG_ESP, asm_x86_arg_offset_from_esp(as, src_arg_num), 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);
+    asm_x86_mov_r32_to_mem32(as, src_r32, ASM_X86_REG_ESP, asm_x86_arg_offset_from_esp(as, dest_arg_num));
 }
 #endif
 
 // locals:
 //  - stored on the stack in ascending order
 //  - numbered 0 through as->num_locals-1
-//  - EBP points above the last local
+//  - ESP points to the first local
 //
-//                          | EBP
-//                          v
+//  | ESP
+//  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;
+STATIC int asm_x86_local_offset_from_esp(asm_x86_t *as, int local_num) {
+    (void)as;
+    // Stack is full descending, ESP points to local0
+    return 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);
+    asm_x86_mov_mem32_to_r32(as, ASM_X86_REG_ESP, asm_x86_local_offset_from_esp(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));
+    asm_x86_mov_r32_to_mem32(as, src_r32, ASM_X86_REG_ESP, asm_x86_local_offset_from_esp(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);
+    int offset = asm_x86_local_offset_from_esp(as, local_num);
     if (offset == 0) {
-        asm_x86_mov_r32_r32(as, dest_r32, ASM_X86_REG_EBP);
+        asm_x86_mov_r32_r32(as, dest_r32, ASM_X86_REG_ESP);
     } else {
-        asm_x86_lea_disp_to_r32(as, ASM_X86_REG_EBP, offset, dest_r32);
+        asm_x86_lea_disp_to_r32(as, ASM_X86_REG_ESP, offset, dest_r32);
     }
 }
 
+void asm_x86_mov_reg_pcrel(asm_x86_t *as, int dest_r32, mp_uint_t label) {
+    asm_x86_write_byte_1(as, OPCODE_CALL_REL32);
+    asm_x86_write_word32(as, 0);
+    mp_uint_t dest = get_label_dest(as, label);
+    mp_int_t rel = dest - as->base.code_offset;
+    asm_x86_pop_r32(as, dest_r32);
+    // PC rel is usually a forward reference, so need to assume it's large
+    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, rel);
+}
+
 #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));
+    asm_x86_push_disp(as, ASM_X86_REG_ESP, asm_x86_local_offset_from_esp(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_mov_r32_r32(as, temp_r32, ASM_X86_REG_ESP);
+    asm_x86_add_i32_to_r32(as, asm_x86_local_offset_from_esp(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) {
+void asm_x86_call_ind(asm_x86_t *as, size_t fun_id, 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) {
@@ -488,20 +512,10 @@ void asm_x86_call_ind(asm_x86_t *as, void *ptr, mp_uint_t n_args, int temp_r32)
     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
+
+    // Load the pointer to the function and make the call
+    asm_x86_mov_mem32_to_r32(as, ASM_X86_REG_FUN_TABLE, fun_id * WORD_SIZE, temp_r32);
     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) {

+ 25 - 8
py/asmx86.h

@@ -84,7 +84,6 @@ static inline void asm_x86_end_pass(asm_x86_t *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);
@@ -101,7 +100,9 @@ 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_test_r32_with_r32(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_reg(asm_x86_t *as, int src_r86);
 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, int num_locals);
@@ -110,7 +111,11 @@ 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);
+void asm_x86_mov_reg_pcrel(asm_x86_t *as, int dest_r64, mp_uint_t label);
+void asm_x86_call_ind(asm_x86_t* as, size_t fun_id, mp_uint_t n_args, int temp_r32);
+
+// Holds a pointer to mp_fun_table
+#define ASM_X86_REG_FUN_TABLE ASM_X86_REG_EBP
 
 #if GENERIC_ASM_API
 
@@ -137,20 +142,31 @@ void asm_x86_call_ind(asm_x86_t* as, void* ptr, mp_uint_t n_args, int temp_r32);
 #define REG_LOCAL_3 ASM_X86_REG_EDI
 #define REG_LOCAL_NUM (3)
 
+// Holds a pointer to mp_fun_table
+#define REG_FUN_TABLE ASM_X86_REG_FUN_TABLE
+
 #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) \
+#define ASM_JUMP_IF_REG_ZERO(as, reg, label, bool_test) \
     do { \
-        asm_x86_test_r8_with_r8(as, reg, reg); \
+        if (bool_test) { \
+            asm_x86_test_r8_with_r8(as, reg, reg); \
+        } else { \
+            asm_x86_test_r32_with_r32(as, reg, reg); \
+        } \
         asm_x86_jcc_label(as, ASM_X86_CC_JZ, label); \
     } while (0)
-#define ASM_JUMP_IF_REG_NONZERO(as, reg, label) \
+#define ASM_JUMP_IF_REG_NONZERO(as, reg, label, bool_test) \
     do { \
-        asm_x86_test_r8_with_r8(as, reg, reg); \
+        if (bool_test) { \
+            asm_x86_test_r8_with_r8(as, reg, reg); \
+        } else { \
+            asm_x86_test_r32_with_r32(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) \
@@ -158,14 +174,15 @@ void asm_x86_call_ind(asm_x86_t* as, void* ptr, mp_uint_t n_args, int temp_r32);
         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_JUMP_REG(as, reg) asm_x86_jmp_reg((as), (reg))
+#define ASM_CALL_IND(as, idx) asm_x86_call_ind(as, idx, mp_f_n_args[idx], ASM_X86_REG_EAX)
 
 #define ASM_MOV_LOCAL_REG(as, local_num, reg_src) asm_x86_mov_r32_to_local((as), (reg_src), (local_num))
 #define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_x86_mov_i32_to_r32((as), (imm), (reg_dest))
-#define ASM_MOV_REG_ALIGNED_IMM(as, reg_dest, imm) asm_x86_mov_i32_to_r32_aligned((as), (imm), (reg_dest))
 #define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_x86_mov_local_to_r32((as), (local_num), (reg_dest))
 #define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_x86_mov_r32_r32((as), (reg_dest), (reg_src))
 #define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_x86_mov_local_addr_to_r32((as), (local_num), (reg_dest))
+#define ASM_MOV_REG_PCREL(as, reg_dest, label) asm_x86_mov_reg_pcrel((as), (reg_dest), (label))
 
 #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))

+ 65 - 16
py/asmxtensa.c

@@ -37,6 +37,7 @@
 #define WORD_SIZE (4)
 #define SIGNED_FIT8(x) ((((x) & 0xffffff80) == 0) || (((x) & 0xffffff80) == 0xffffff80))
 #define SIGNED_FIT12(x) ((((x) & 0xfffff800) == 0) || (((x) & 0xfffff800) == 0xfffff800))
+#define NUM_REGS_SAVED (5)
 
 void asm_xtensa_end_pass(asm_xtensa_t *as) {
     as->num_const = as->cur_const;
@@ -67,26 +68,37 @@ void asm_xtensa_entry(asm_xtensa_t *as, int num_locals) {
     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);
+    // adjust the stack-pointer to store a0, a12, a13, a14, a15 and locals, 16-byte aligned
+    as->stack_adjust = (((NUM_REGS_SAVED + num_locals) * WORD_SIZE) + 15) & ~15;
+    if (SIGNED_FIT8(-as->stack_adjust)) {
+        asm_xtensa_op_addi(as, ASM_XTENSA_REG_A1, ASM_XTENSA_REG_A1, -as->stack_adjust);
+    } else {
+        asm_xtensa_op_movi(as, ASM_XTENSA_REG_A9, as->stack_adjust);
+        asm_xtensa_op_sub(as, ASM_XTENSA_REG_A1, ASM_XTENSA_REG_A1, ASM_XTENSA_REG_A9);
+    }
 
-    // save return value (a0) and callee-save registers (a12, a13, a14)
+    // save return value (a0) and callee-save registers (a12, a13, a14, a15)
     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);
+    for (int i = 1; i < NUM_REGS_SAVED; ++i) {
+        asm_xtensa_op_s32i_n(as, ASM_XTENSA_REG_A11 + i, ASM_XTENSA_REG_A1, i);
+    }
 }
 
 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);
+    for (int i = NUM_REGS_SAVED - 1; i >= 1; --i) {
+        asm_xtensa_op_l32i_n(as, ASM_XTENSA_REG_A11 + i, ASM_XTENSA_REG_A1, i);
+    }
     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);
+    if (SIGNED_FIT8(as->stack_adjust)) {
+        asm_xtensa_op_addi(as, ASM_XTENSA_REG_A1, ASM_XTENSA_REG_A1, as->stack_adjust);
+    } else {
+        asm_xtensa_op_movi(as, ASM_XTENSA_REG_A9, as->stack_adjust);
+        asm_xtensa_op_add_n(as, ASM_XTENSA_REG_A1, ASM_XTENSA_REG_A1, ASM_XTENSA_REG_A9);
+    }
+
     asm_xtensa_op_ret_n(as);
 }
 
@@ -149,7 +161,8 @@ void asm_xtensa_mov_reg_i32(asm_xtensa_t *as, uint reg_dest, uint32_t 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);
+        uint32_t const_table_offset = (uint8_t*)as->const_table - as->base.code_base;
+        asm_xtensa_op_l32r(as, reg_dest, as->base.code_offset, const_table_offset + as->cur_const * WORD_SIZE);
         // store the constant in the table
         if (as->const_table != NULL) {
             as->const_table[as->cur_const] = i32;
@@ -159,16 +172,52 @@ 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) {
-    asm_xtensa_op_s32i(as, reg_src, ASM_XTENSA_REG_A1, 4 + local_num);
+    asm_xtensa_op_s32i(as, reg_src, ASM_XTENSA_REG_A1, NUM_REGS_SAVED + 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);
+    asm_xtensa_op_l32i(as, reg_dest, ASM_XTENSA_REG_A1, NUM_REGS_SAVED + 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);
+    uint off = (NUM_REGS_SAVED + local_num) * WORD_SIZE;
+    if (SIGNED_FIT8(off)) {
+        asm_xtensa_op_addi(as, reg_dest, ASM_XTENSA_REG_A1, off);
+    } else {
+        asm_xtensa_op_movi(as, reg_dest, off);
+        asm_xtensa_op_add_n(as, reg_dest, reg_dest, ASM_XTENSA_REG_A1);
+    }
+}
+
+void asm_xtensa_mov_reg_pcrel(asm_xtensa_t *as, uint reg_dest, uint label) {
+    // Get relative offset from PC
+    uint32_t dest = get_label_dest(as, label);
+    int32_t rel = dest - as->base.code_offset;
+    rel -= 3 + 3; // account for 3 bytes of movi instruction, 3 bytes call0 adjustment
+    asm_xtensa_op_movi(as, reg_dest, rel); // imm has 12-bit range
+
+    // Use call0 to get PC+3 into a0
+    // call0 destination must be aligned on 4 bytes:
+    //  - code_offset&3=0: off=0, pad=1
+    //  - code_offset&3=1: off=0, pad=0
+    //  - code_offset&3=2: off=1, pad=3
+    //  - code_offset&3=3: off=1, pad=2
+    uint32_t off = as->base.code_offset >> 1 & 1;
+    uint32_t pad = (5 - as->base.code_offset) & 3;
+    asm_xtensa_op_call0(as, off);
+    mp_asm_base_get_cur_to_write_bytes(&as->base, pad);
+
+    // Add PC to relative offset
+    asm_xtensa_op_add_n(as, reg_dest, reg_dest, ASM_XTENSA_REG_A0);
+}
+
+void asm_xtensa_call_ind(asm_xtensa_t *as, uint idx) {
+    if (idx < 16) {
+        asm_xtensa_op_l32i_n(as, ASM_XTENSA_REG_A0, ASM_XTENSA_REG_FUN_TABLE, idx);
+    } else {
+        asm_xtensa_op_l32i(as, ASM_XTENSA_REG_A0, ASM_XTENSA_REG_FUN_TABLE, idx);
+    }
+    asm_xtensa_op_callx0(as, ASM_XTENSA_REG_A0);
 }
 
 #endif // MICROPY_EMIT_XTENSA || MICROPY_EMIT_INLINE_XTENSA

+ 20 - 12
py/asmxtensa.h

@@ -113,12 +113,12 @@ 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_add_n(asm_xtensa_t *as, uint reg_dest, uint reg_src_a, uint reg_src_b) {
+    asm_xtensa_op16(as, ASM_XTENSA_ENCODE_RRRN(10, 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));
+    asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 12, reg_src, reg_dest, imm8 & 0xff));
 }
 
 static inline void asm_xtensa_op_and(asm_xtensa_t *as, uint reg_dest, uint reg_src_a, uint reg_src_b) {
@@ -133,6 +133,10 @@ static inline void asm_xtensa_op_bccz(asm_xtensa_t *as, uint cond, uint reg_src,
     asm_xtensa_op24(as, ASM_XTENSA_ENCODE_BRI12(6, reg_src, cond, 1, rel12 & 0xfff));
 }
 
+static inline void asm_xtensa_op_call0(asm_xtensa_t *as, int32_t rel18) {
+    asm_xtensa_op24(as, ASM_XTENSA_ENCODE_CALL(5, 0, rel18 & 0x3ffff));
+}
+
 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));
 }
@@ -238,6 +242,11 @@ 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);
+void asm_xtensa_mov_reg_pcrel(asm_xtensa_t *as, uint reg_dest, uint label);
+void asm_xtensa_call_ind(asm_xtensa_t *as, uint idx);
+
+// Holds a pointer to mp_fun_table
+#define ASM_XTENSA_REG_FUN_TABLE ASM_XTENSA_REG_A15
 
 #if GENERIC_ASM_API
 
@@ -262,30 +271,29 @@ void asm_xtensa_mov_reg_local_addr(asm_xtensa_t *as, uint reg_dest, int local_nu
 #define REG_LOCAL_3 ASM_XTENSA_REG_A14
 #define REG_LOCAL_NUM (3)
 
+#define REG_FUN_TABLE ASM_XTENSA_REG_FUN_TABLE
+
 #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) \
+#define ASM_JUMP_IF_REG_ZERO(as, reg, label, bool_test) \
     asm_xtensa_bccz_reg_label(as, ASM_XTENSA_CCZ_EQ, reg, label)
-#define ASM_JUMP_IF_REG_NONZERO(as, reg, label) \
+#define ASM_JUMP_IF_REG_NONZERO(as, reg, label, bool_test) \
     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_JUMP_REG(as, reg) asm_xtensa_op_jx((as), (reg))
+#define ASM_CALL_IND(as, idx) asm_xtensa_call_ind((as), (idx))
 
 #define ASM_MOV_LOCAL_REG(as, local_num, reg_src) asm_xtensa_mov_local_reg((as), (local_num), (reg_src))
 #define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_xtensa_mov_reg_i32((as), (reg_dest), (imm))
-#define ASM_MOV_REG_ALIGNED_IMM(as, reg_dest, imm) asm_xtensa_mov_reg_i32((as), (reg_dest), (imm))
 #define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_xtensa_mov_reg_local((as), (reg_dest), (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_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_xtensa_mov_reg_local_addr((as), (reg_dest), (local_num))
+#define ASM_MOV_REG_PCREL(as, reg_dest, label) asm_xtensa_mov_reg_pcrel((as), (reg_dest), (label))
 
 #define ASM_LSL_REG_REG(as, reg_dest, reg_shift) \
     do { \
@@ -300,7 +308,7 @@ void asm_xtensa_mov_reg_local_addr(asm_xtensa_t *as, uint reg_dest, int local_nu
 #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_ADD_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_add_n((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))
 

+ 9 - 7
py/bc.c

@@ -292,7 +292,7 @@ continue2:;
 //     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:
+// MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE is enabled (and they take a qstr):
 //     MP_BC_LOAD_NAME
 //     MP_BC_LOAD_GLOBAL
 //     MP_BC_LOAD_ATTR
@@ -386,18 +386,20 @@ 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) {
+        if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC) {
+            if (*ip == MP_BC_LOAD_NAME
+                || *ip == MP_BC_LOAD_GLOBAL
+                || *ip == MP_BC_LOAD_ATTR
+                || *ip == MP_BC_STORE_ATTR) {
+                ip += 1;
+            }
+        }
         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) {

+ 1 - 0
py/builtin.h

@@ -106,6 +106,7 @@ 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_ucryptolib;
 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;

+ 0 - 4
py/builtinevex.c

@@ -137,12 +137,8 @@ STATIC mp_obj_t eval_exec_helper(size_t n_args, const mp_obj_t *args, mp_parse_i
     // 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) {
-        #if MICROPY_PY_IO
         lex = mp_lexer_new_from_file(str);
         parse_input_kind = MP_PARSE_FILE_INPUT;
-        #else
-        mp_raise_msg(&mp_type_RuntimeError, "script compilation not supported");
-        #endif
     } else {
         lex = mp_lexer_new_from_str_len(MP_QSTR__lt_string_gt_, str, str_len, 0);
     }

+ 4 - 4
py/builtinhelp.c

@@ -100,7 +100,7 @@ STATIC void mp_help_print_modules(void) {
     // print the list of modules in a column-first order
     #define NUM_COLUMNS (4)
     #define COLUMN_WIDTH (18)
-    mp_uint_t len;
+    size_t len;
     mp_obj_t *items;
     mp_obj_list_get(list, &len, &items);
     unsigned int num_rows = (len + NUM_COLUMNS - 1) / NUM_COLUMNS;
@@ -145,13 +145,13 @@ STATIC void mp_help_print_obj(const mp_obj_t obj) {
 
     mp_map_t *map = NULL;
     if (type == &mp_type_module) {
-        map = mp_obj_dict_get_map(mp_obj_module_get_globals(obj));
+        map = &mp_obj_module_get_globals(obj)->map;
     } 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 (type->locals_dict != NULL) {
+            map = &type->locals_dict->map;
         }
     }
     if (map != NULL) {

+ 1 - 1
py/builtinimport.c

@@ -222,7 +222,7 @@ STATIC void do_load(mp_obj_t module_obj, vstr_t *file) {
     #endif
 
     // If we can compile scripts then load the file and compile and execute it.
-    #if MICROPY_ENABLE_COMPILER && MICROPY_PY_IO
+    #if MICROPY_ENABLE_COMPILER
     {
         mp_lexer_t *lex = mp_lexer_new_from_file(file_str);
         do_load_from_lexer(module_obj, lex);

Разница между файлами не показана из-за своего большого размера
+ 242 - 230
py/compile.c


+ 0 - 9
py/compile.h

@@ -30,15 +30,6 @@
 #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);

+ 70 - 76
py/emit.h

@@ -51,21 +51,53 @@ typedef enum {
 
 #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)
+// Kind for emit_id_ops->local()
+#define MP_EMIT_IDOP_LOCAL_FAST (0)
+#define MP_EMIT_IDOP_LOCAL_DEREF (1)
+
+// Kind for emit_id_ops->global()
+#define MP_EMIT_IDOP_GLOBAL_NAME (0)
+#define MP_EMIT_IDOP_GLOBAL_GLOBAL (1)
+
+// Kind for emit->import()
+#define MP_EMIT_IMPORT_NAME (0)
+#define MP_EMIT_IMPORT_FROM (1)
+#define MP_EMIT_IMPORT_STAR (2)
+
+// Kind for emit->subscr()
+#define MP_EMIT_SUBSCR_LOAD (0)
+#define MP_EMIT_SUBSCR_STORE (1)
+#define MP_EMIT_SUBSCR_DELETE (2)
+
+// Kind for emit->attr()
+#define MP_EMIT_ATTR_LOAD (0)
+#define MP_EMIT_ATTR_STORE (1)
+#define MP_EMIT_ATTR_DELETE (2)
+
+// Kind for emit->setup_block()
+#define MP_EMIT_SETUP_BLOCK_WITH (0)
+#define MP_EMIT_SETUP_BLOCK_EXCEPT (2)
+#define MP_EMIT_SETUP_BLOCK_FINALLY (3)
+
+// Kind for emit->build()
+#define MP_EMIT_BUILD_TUPLE (0)
+#define MP_EMIT_BUILD_LIST (1)
+#define MP_EMIT_BUILD_MAP (3)
+#define MP_EMIT_BUILD_SET (6)
+#define MP_EMIT_BUILD_SLICE (8)
+
+// Kind for emit->yield()
+#define MP_EMIT_YIELD_VALUE (0)
+#define MP_EMIT_YIELD_FROM (1)
 
 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);
+    void (*local)(emit_t *emit, qstr qst, mp_uint_t local_num, int kind);
+    void (*global)(emit_t *emit, qstr qst, int kind);
 } 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);
@@ -77,22 +109,16 @@ typedef struct _emit_method_table_t {
     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 (*import)(emit_t *emit, qstr qst, int kind);
     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 (*subscr)(emit_t *emit, int kind);
+    void (*attr)(emit_t *emit, qstr qst, int kind);
     void (*dup_top)(emit_t *emit);
     void (*dup_top_two)(emit_t *emit);
     void (*pop_top)(emit_t *emit);
@@ -101,12 +127,9 @@ typedef struct _emit_method_table_t {
     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 (*unwind_jump)(emit_t *emit, mp_uint_t label, mp_uint_t except_depth);
+    void (*setup_block)(emit_t *emit, mp_uint_t label, int kind);
     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);
@@ -115,16 +138,8 @@ typedef struct _emit_method_table_t {
     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 (*build)(emit_t *emit, mp_uint_t n_args, int kind);
     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);
@@ -134,8 +149,7 @@ typedef struct _emit_method_table_t {
     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);
+    void (*yield)(emit_t *emit, int kind);
 
     // these methods are used to control entry to/exit from an exception handler
     // they may or may not emit code
@@ -143,7 +157,12 @@ typedef struct _emit_method_table_t {
     void (*end_except_handler)(emit_t *emit);
 } emit_method_table_t;
 
-void mp_emit_common_get_id_for_load(scope_t *scope, qstr qst);
+int mp_native_type_from_qstr(qstr qst);
+
+static inline void mp_emit_common_get_id_for_load(scope_t *scope, qstr qst) {
+    scope_find_or_add_id(scope, qst, ID_INFO_KIND_GLOBAL_IMPLICIT);
+}
+
 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);
 
@@ -159,11 +178,11 @@ 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);
+emit_t *emit_native_x64_new(mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels);
+emit_t *emit_native_x86_new(mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels);
+emit_t *emit_native_thumb_new(mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels);
+emit_t *emit_native_arm_new(mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels);
+emit_t *emit_native_xtensa_new(mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels);
 
 void emit_bc_set_max_num_labels(emit_t* emit, mp_uint_t max_num_labels);
 
@@ -180,36 +199,24 @@ 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_load_local(emit_t *emit, qstr qst, mp_uint_t local_num, int kind);
+void mp_emit_bc_load_global(emit_t *emit, qstr qst, int kind);
+void mp_emit_bc_store_local(emit_t *emit, qstr qst, mp_uint_t local_num, int kind);
+void mp_emit_bc_store_global(emit_t *emit, qstr qst, int kind);
+void mp_emit_bc_delete_local(emit_t *emit, qstr qst, mp_uint_t local_num, int kind);
+void mp_emit_bc_delete_global(emit_t *emit, qstr qst, int kind);
 
 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_import(emit_t *emit, qstr qst, int kind);
 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_subscr(emit_t *emit, int kind);
+void mp_emit_bc_attr(emit_t *emit, qstr qst, int kind);
 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);
@@ -219,12 +226,8 @@ 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_setup_block(emit_t *emit, mp_uint_t label, int kind);
 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);
@@ -233,16 +236,8 @@ 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_build(emit_t *emit, mp_uint_t n_args, int kind);
 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);
@@ -252,8 +247,7 @@ void mp_emit_bc_call_function(emit_t *emit, mp_uint_t n_positional, mp_uint_t n_
 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_yield(emit_t *emit, int kind);
 void mp_emit_bc_start_except_handler(emit_t *emit);
 void mp_emit_bc_end_except_handler(emit_t *emit);
 

+ 111 - 192
py/emitbc.c

@@ -315,7 +315,7 @@ void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) {
     emit->last_source_line = 1;
     #ifndef NDEBUG
     // With debugging enabled labels are checked for unique assignment
-    if (pass < MP_PASS_EMIT) {
+    if (pass < MP_PASS_EMIT && emit->label_offsets != NULL) {
         memset(emit->label_offsets, -1, emit->max_num_labels * sizeof(mp_uint_t));
     }
     #endif
@@ -331,6 +331,10 @@ void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) {
             // the highest slot in the state (fastn[0], see vm.c).
             n_state = 1;
         }
+        #if MICROPY_DEBUG_VM_STACK_OVERFLOW
+        // An extra slot in the stack is needed to detect VM stack overflow
+        n_state += 1;
+        #endif
         emit_write_code_info_uint(emit, n_state);
         emit_write_code_info_uint(emit, scope->exc_stack_size);
     }
@@ -504,19 +508,19 @@ void mp_emit_bc_label_assign(emit_t *emit, mp_uint_t l) {
     }
 }
 
-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_import(emit_t *emit, qstr qst, int kind) {
+    MP_STATIC_ASSERT(MP_BC_IMPORT_NAME + MP_EMIT_IMPORT_NAME == MP_BC_IMPORT_NAME);
+    MP_STATIC_ASSERT(MP_BC_IMPORT_NAME + MP_EMIT_IMPORT_FROM == MP_BC_IMPORT_FROM);
+    if (kind == MP_EMIT_IMPORT_FROM) {
+        emit_bc_pre(emit, 1);
+    } else {
+        emit_bc_pre(emit, -1);
+    }
+    if (kind == MP_EMIT_IMPORT_STAR) {
+        emit_write_bytecode_byte(emit, MP_BC_IMPORT_STAR);
+    } else {
+        emit_write_bytecode_byte_qstr(emit, MP_BC_IMPORT_NAME + kind, qst);
+    }
 }
 
 void mp_emit_bc_load_const_tok(emit_t *emit, mp_token_kind_t tok) {
@@ -556,43 +560,24 @@ void mp_emit_bc_load_null(emit_t *emit) {
     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 mp_emit_bc_load_local(emit_t *emit, qstr qst, mp_uint_t local_num, int kind) {
+    MP_STATIC_ASSERT(MP_BC_LOAD_FAST_N + MP_EMIT_IDOP_LOCAL_FAST == MP_BC_LOAD_FAST_N);
+    MP_STATIC_ASSERT(MP_BC_LOAD_FAST_N + MP_EMIT_IDOP_LOCAL_DEREF == MP_BC_LOAD_DEREF);
     (void)qst;
     emit_bc_pre(emit, 1);
-    if (local_num <= 15) {
+    if (kind == MP_EMIT_IDOP_LOCAL_FAST && 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);
+        emit_write_bytecode_byte_uint(emit, MP_BC_LOAD_FAST_N + kind, local_num);
     }
 }
 
-void mp_emit_bc_load_global(emit_t *emit, qstr qst) {
+void mp_emit_bc_load_global(emit_t *emit, qstr qst, int kind) {
+    MP_STATIC_ASSERT(MP_BC_LOAD_NAME + MP_EMIT_IDOP_GLOBAL_NAME == MP_BC_LOAD_NAME);
+    MP_STATIC_ASSERT(MP_BC_LOAD_NAME + MP_EMIT_IDOP_GLOBAL_GLOBAL == MP_BC_LOAD_GLOBAL);
     (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);
+    emit_write_bytecode_byte_qstr(emit, MP_BC_LOAD_NAME + kind, qst);
     if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC) {
         emit_write_bytecode_byte(emit, 0);
     }
@@ -608,80 +593,68 @@ void mp_emit_bc_load_build_class(emit_t *emit) {
     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);
+void mp_emit_bc_subscr(emit_t *emit, int kind) {
+    if (kind == MP_EMIT_SUBSCR_LOAD) {
+        emit_bc_pre(emit, -1);
+        emit_write_bytecode_byte(emit, MP_BC_LOAD_SUBSCR);
     } else {
-        emit_write_bytecode_byte_uint(emit, MP_BC_STORE_FAST_N, local_num);
+        if (kind == MP_EMIT_SUBSCR_DELETE) {
+            mp_emit_bc_load_null(emit);
+            mp_emit_bc_rot_three(emit);
+        }
+        emit_bc_pre(emit, -3);
+        emit_write_bytecode_byte(emit, MP_BC_STORE_SUBSCR);
     }
 }
 
-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);
+void mp_emit_bc_attr(emit_t *emit, qstr qst, int kind) {
+    if (kind == MP_EMIT_ATTR_LOAD) {
+        emit_bc_pre(emit, 0);
+        emit_write_bytecode_byte_qstr(emit, MP_BC_LOAD_ATTR, qst);
+    } else {
+        if (kind == MP_EMIT_ATTR_DELETE) {
+            mp_emit_bc_load_null(emit);
+            mp_emit_bc_rot_two(emit);
+        }
+        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 mp_emit_bc_store_local(emit_t *emit, qstr qst, mp_uint_t local_num, int kind) {
+    MP_STATIC_ASSERT(MP_BC_STORE_FAST_N + MP_EMIT_IDOP_LOCAL_FAST == MP_BC_STORE_FAST_N);
+    MP_STATIC_ASSERT(MP_BC_STORE_FAST_N + MP_EMIT_IDOP_LOCAL_DEREF == MP_BC_STORE_DEREF);
     (void)qst;
-    emit_write_bytecode_byte_uint(emit, MP_BC_DELETE_FAST, local_num);
+    emit_bc_pre(emit, -1);
+    if (kind == MP_EMIT_IDOP_LOCAL_FAST && 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 + kind, 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_store_global(emit_t *emit, qstr qst, int kind) {
+    MP_STATIC_ASSERT(MP_BC_STORE_NAME + MP_EMIT_IDOP_GLOBAL_NAME == MP_BC_STORE_NAME);
+    MP_STATIC_ASSERT(MP_BC_STORE_NAME + MP_EMIT_IDOP_GLOBAL_GLOBAL == MP_BC_STORE_GLOBAL);
+    emit_bc_pre(emit, -1);
+    emit_write_bytecode_byte_qstr(emit, MP_BC_STORE_NAME + kind, qst);
 }
 
-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_local(emit_t *emit, qstr qst, mp_uint_t local_num, int kind) {
+    MP_STATIC_ASSERT(MP_BC_DELETE_FAST + MP_EMIT_IDOP_LOCAL_FAST == MP_BC_DELETE_FAST);
+    MP_STATIC_ASSERT(MP_BC_DELETE_FAST + MP_EMIT_IDOP_LOCAL_DEREF == MP_BC_DELETE_DEREF);
+    (void)qst;
+    emit_write_bytecode_byte_uint(emit, MP_BC_DELETE_FAST + kind, local_num);
 }
 
-void mp_emit_bc_delete_global(emit_t *emit, qstr qst) {
+void mp_emit_bc_delete_global(emit_t *emit, qstr qst, int kind) {
+    MP_STATIC_ASSERT(MP_BC_DELETE_NAME + MP_EMIT_IDOP_GLOBAL_NAME == MP_BC_DELETE_NAME);
+    MP_STATIC_ASSERT(MP_BC_DELETE_NAME + MP_EMIT_IDOP_GLOBAL_GLOBAL == MP_BC_DELETE_GLOBAL);
     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);
+    emit_write_bytecode_byte_qstr(emit, MP_BC_DELETE_NAME + kind, qst);
 }
 
 void mp_emit_bc_dup_top(emit_t *emit) {
@@ -750,11 +723,18 @@ void mp_emit_bc_unwind_jump(emit_t *emit, mp_uint_t label, mp_uint_t except_dept
     }
 }
 
-void mp_emit_bc_setup_with(emit_t *emit, mp_uint_t label) {
+void mp_emit_bc_setup_block(emit_t *emit, mp_uint_t label, int kind) {
+    MP_STATIC_ASSERT(MP_BC_SETUP_WITH + MP_EMIT_SETUP_BLOCK_WITH == MP_BC_SETUP_WITH);
+    MP_STATIC_ASSERT(MP_BC_SETUP_WITH + MP_EMIT_SETUP_BLOCK_EXCEPT == MP_BC_SETUP_EXCEPT);
+    MP_STATIC_ASSERT(MP_BC_SETUP_WITH + MP_EMIT_SETUP_BLOCK_FINALLY == MP_BC_SETUP_FINALLY);
+    if (kind == MP_EMIT_SETUP_BLOCK_WITH) {
     // 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);
+        emit_bc_pre(emit, 2);
+    } else {
+        emit_bc_pre(emit, 0);
+    }
+    emit_write_bytecode_byte_unsigned_label(emit, MP_BC_SETUP_WITH + kind, label);
 }
 
 void mp_emit_bc_with_cleanup(emit_t *emit, mp_uint_t label) {
@@ -763,17 +743,7 @@ void mp_emit_bc_with_cleanup(emit_t *emit, mp_uint_t label) {
     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);
+    emit_bc_pre(emit, -4); // cancel the 2 above, plus the 2 from mp_emit_bc_setup_block(MP_EMIT_SETUP_BLOCK_WITH)
 }
 
 void mp_emit_bc_end_finally(emit_t *emit) {
@@ -827,19 +797,18 @@ 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) {
-    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_build(emit_t *emit, mp_uint_t n_args, int kind) {
+    MP_STATIC_ASSERT(MP_BC_BUILD_TUPLE + MP_EMIT_BUILD_TUPLE == MP_BC_BUILD_TUPLE);
+    MP_STATIC_ASSERT(MP_BC_BUILD_TUPLE + MP_EMIT_BUILD_LIST == MP_BC_BUILD_LIST);
+    MP_STATIC_ASSERT(MP_BC_BUILD_TUPLE + MP_EMIT_BUILD_MAP == MP_BC_BUILD_MAP);
+    MP_STATIC_ASSERT(MP_BC_BUILD_TUPLE + MP_EMIT_BUILD_SET == MP_BC_BUILD_SET);
+    MP_STATIC_ASSERT(MP_BC_BUILD_TUPLE + MP_EMIT_BUILD_SLICE == MP_BC_BUILD_SLICE);
+    if (kind == MP_EMIT_BUILD_MAP) {
+        emit_bc_pre(emit, 1);
+    } else {
+        emit_bc_pre(emit, 1 - n_args);
+    }
+    emit_write_bytecode_byte_uint(emit, MP_BC_BUILD_TUPLE + kind, n_args);
 }
 
 void mp_emit_bc_store_map(emit_t *emit) {
@@ -847,20 +816,6 @@ void mp_emit_bc_store_map(emit_t *emit) {
     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;
@@ -942,16 +897,11 @@ void mp_emit_bc_raise_varargs(emit_t *emit, mp_uint_t 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);
+void mp_emit_bc_yield(emit_t *emit, int kind) {
+    MP_STATIC_ASSERT(MP_BC_YIELD_VALUE + 1 == MP_BC_YIELD_FROM);
+    emit_bc_pre(emit, -kind);
     emit->scope->scope_flags |= MP_SCOPE_FLAG_GENERATOR;
-    emit_write_bytecode_byte(emit, MP_BC_YIELD_FROM);
+    emit_write_bytecode_byte(emit, MP_BC_YIELD_VALUE + kind);
 }
 
 void mp_emit_bc_start_except_handler(emit_t *emit) {
@@ -964,7 +914,6 @@ void mp_emit_bc_end_except_handler(emit_t *emit) {
 
 #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,
@@ -972,41 +921,29 @@ const emit_method_table_t emit_bc_method_table = {
     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_local,
         mp_emit_bc_load_global,
     },
     {
-        mp_emit_bc_store_fast,
-        mp_emit_bc_store_deref,
-        mp_emit_bc_store_name,
+        mp_emit_bc_store_local,
         mp_emit_bc_store_global,
     },
     {
-        mp_emit_bc_delete_fast,
-        mp_emit_bc_delete_deref,
-        mp_emit_bc_delete_name,
+        mp_emit_bc_delete_local,
         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_import,
     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_subscr,
+    mp_emit_bc_attr,
     mp_emit_bc_dup_top,
     mp_emit_bc_dup_top_two,
     mp_emit_bc_pop_top,
@@ -1016,11 +953,8 @@ const emit_method_table_t emit_bc_method_table = {
     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_setup_block,
     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,
@@ -1029,16 +963,8 @@ const emit_method_table_t emit_bc_method_table = {
     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_build,
     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,
@@ -1048,31 +974,24 @@ const emit_method_table_t emit_bc_method_table = {
     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_yield,
 
     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_local,
     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_local,
     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_local,
     mp_emit_bc_delete_global,
 };
 #endif

+ 6 - 22
py/emitcommon.c

@@ -30,26 +30,10 @@
 
 #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) {
+    id_info_t *id = scope_find_or_add_id(scope, qst, ID_INFO_KIND_GLOBAL_IMPLICIT);
+    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;
     }
@@ -63,14 +47,14 @@ void mp_emit_common_id_op(emit_t *emit, const mp_emit_method_table_id_ops_t *emi
 
     // call the emit backend with the correct code
     if (id->kind == ID_INFO_KIND_GLOBAL_IMPLICIT) {
-        emit_method_table->name(emit, qst);
+        emit_method_table->global(emit, qst, MP_EMIT_IDOP_GLOBAL_NAME);
     } else if (id->kind == ID_INFO_KIND_GLOBAL_EXPLICIT) {
-        emit_method_table->global(emit, qst);
+        emit_method_table->global(emit, qst, MP_EMIT_IDOP_GLOBAL_GLOBAL);
     } else if (id->kind == ID_INFO_KIND_LOCAL) {
-        emit_method_table->fast(emit, qst, id->local_num);
+        emit_method_table->local(emit, qst, id->local_num, MP_EMIT_IDOP_LOCAL_FAST);
     } else {
         assert(id->kind == ID_INFO_KIND_CELL || id->kind == ID_INFO_KIND_FREE);
-        emit_method_table->deref(emit, qst, id->local_num);
+        emit_method_table->local(emit, qst, id->local_num, MP_EMIT_IDOP_LOCAL_DEREF);
     }
 }
 

+ 12 - 8
py/emitglue.c

@@ -76,6 +76,9 @@ void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code,
     #endif
 
 #ifdef DEBUG_PRINT
+    #if !MICROPY_DEBUG_PRINTERS
+    const size_t len = 0;
+    #endif
     DEBUG_printf("assign byte code: code=%p len=" UINT_FMT " flags=%x\n", code, len, (uint)scope_flags);
 #endif
 #if MICROPY_DEBUG_PRINTERS
@@ -131,10 +134,12 @@ mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, mp_obj_t def_ar
     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);
+            fun = mp_obj_new_fun_native(def_args, def_kw_args, rc->data.u_native.fun_data, rc->data.u_native.const_table);
+            // Check for a generator function, and if so change the type of the object
+            if ((rc->scope_flags & MP_SCOPE_FLAG_GENERATOR) != 0) {
+                ((mp_obj_base_t*)MP_OBJ_TO_PTR(fun))->type = &mp_type_native_gen_wrap;
+            }
             break;
         #endif
         #if MICROPY_EMIT_INLINE_ASM
@@ -146,14 +151,13 @@ mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, mp_obj_t def_ar
             // 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);
+            // check for generator functions and if so change the type of the object
+            if ((rc->scope_flags & MP_SCOPE_FLAG_GENERATOR) != 0) {
+                ((mp_obj_base_t*)MP_OBJ_TO_PTR(fun))->type = &mp_type_gen_wrap;
+            }
             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;
 }
 

+ 9 - 0
py/emitglue.h

@@ -30,6 +30,15 @@
 
 // These variables and functions glue the code emitters to the runtime.
 
+// 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,
+};
+
 typedef enum {
     MP_CODE_UNUSED,
     MP_CODE_RESERVED,

+ 1 - 1
py/emitinlinethumb.c

@@ -301,7 +301,7 @@ STATIC uint32_t get_arg_i(emit_inline_asm_t *emit, const char *op, mp_parse_node
     }
     uint32_t i = mp_obj_get_int_truncated(o);
     if ((i & (~fit_mask)) != 0) {
-        emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, "'%s' integer 0x%x does not fit in mask 0x%x", op, i, fit_mask));
+        emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, "'%s' integer 0x%x doesn't fit in mask 0x%x", op, i, fit_mask));
         return 0;
     }
     return i;

+ 1 - 1
py/emitinlinextensa.c

@@ -171,7 +171,7 @@ STATIC uint32_t get_arg_i(emit_inline_asm_t *emit, const char *op, mp_parse_node
     }
     uint32_t i = mp_obj_get_int_truncated(o);
     if (min != max && ((int)i < min || (int)i > max)) {
-        emit_inline_xtensa_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, "'%s' integer %d is not within range %d..%d", op, i, min, max));
+        emit_inline_xtensa_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, "'%s' integer %d isn't within range %d..%d", op, i, min, max));
         return 0;
     }
     return i;

+ 20 - 0
py/emitnarm.c

@@ -0,0 +1,20 @@
+// ARM specific stuff
+
+#include "py/mpconfig.h"
+
+#if MICROPY_EMIT_ARM
+
+// This is defined so that the assembler exports generic assembler API macros
+#define GENERIC_ASM_API (1)
+#include "py/asmarm.h"
+
+// Word indices of REG_LOCAL_x in nlr_buf_t
+#define NLR_BUF_IDX_LOCAL_1 (3) // r4
+#define NLR_BUF_IDX_LOCAL_2 (4) // r5
+#define NLR_BUF_IDX_LOCAL_3 (5) // r6
+
+#define N_ARM (1)
+#define EXPORT_FUN(name) emit_native_arm_##name
+#include "py/emitnative.c"
+
+#endif

Разница между файлами не показана из-за своего большого размера
+ 609 - 337
py/emitnative.c


+ 20 - 0
py/emitnthumb.c

@@ -0,0 +1,20 @@
+// thumb specific stuff
+
+#include "py/mpconfig.h"
+
+#if MICROPY_EMIT_THUMB
+
+// this is defined so that the assembler exports generic assembler API macros
+#define GENERIC_ASM_API (1)
+#include "py/asmthumb.h"
+
+// Word indices of REG_LOCAL_x in nlr_buf_t
+#define NLR_BUF_IDX_LOCAL_1 (3) // r4
+#define NLR_BUF_IDX_LOCAL_2 (4) // r5
+#define NLR_BUF_IDX_LOCAL_3 (5) // r6
+
+#define N_THUMB (1)
+#define EXPORT_FUN(name) emit_native_thumb_##name
+#include "py/emitnative.c"
+
+#endif

+ 20 - 0
py/emitnx64.c

@@ -0,0 +1,20 @@
+// x64 specific stuff
+
+#include "py/mpconfig.h"
+
+#if MICROPY_EMIT_X64
+
+// This is defined so that the assembler exports generic assembler API macros
+#define GENERIC_ASM_API (1)
+#include "py/asmx64.h"
+
+// Word indices of REG_LOCAL_x in nlr_buf_t
+#define NLR_BUF_IDX_LOCAL_1 (5) // rbx
+#define NLR_BUF_IDX_LOCAL_2 (6) // r12
+#define NLR_BUF_IDX_LOCAL_3 (7) // r13
+
+#define N_X64 (1)
+#define EXPORT_FUN(name) emit_native_x64_##name
+#include "py/emitnative.c"
+
+#endif

+ 76 - 0
py/emitnx86.c

@@ -0,0 +1,76 @@
+// x86 specific stuff
+
+#include "py/mpconfig.h"
+#include "py/runtime0.h"
+
+#if MICROPY_EMIT_X86
+
+// This is defined so that the assembler exports generic assembler API macros
+#define GENERIC_ASM_API (1)
+#include "py/asmx86.h"
+
+// Word indices of REG_LOCAL_x in nlr_buf_t
+#define NLR_BUF_IDX_LOCAL_1 (5) // ebx
+#define NLR_BUF_IDX_LOCAL_2 (7) // esi
+#define NLR_BUF_IDX_LOCAL_3 (6) // edi
+
+// x86 needs a table to know how many args a given function has
+STATIC byte mp_f_n_args[MP_F_NUMBER_OF] = {
+    [MP_F_CONVERT_OBJ_TO_NATIVE] = 2,
+    [MP_F_CONVERT_NATIVE_TO_OBJ] = 2,
+    [MP_F_NATIVE_SWAP_GLOBALS] = 1,
+    [MP_F_LOAD_NAME] = 1,
+    [MP_F_LOAD_GLOBAL] = 1,
+    [MP_F_LOAD_BUILD_CLASS] = 0,
+    [MP_F_LOAD_ATTR] = 2,
+    [MP_F_LOAD_METHOD] = 3,
+    [MP_F_LOAD_SUPER_METHOD] = 2,
+    [MP_F_STORE_NAME] = 2,
+    [MP_F_STORE_GLOBAL] = 2,
+    [MP_F_STORE_ATTR] = 3,
+    [MP_F_OBJ_SUBSCR] = 3,
+    [MP_F_OBJ_IS_TRUE] = 1,
+    [MP_F_UNARY_OP] = 2,
+    [MP_F_BINARY_OP] = 3,
+    [MP_F_BUILD_TUPLE] = 2,
+    [MP_F_BUILD_LIST] = 2,
+    [MP_F_LIST_APPEND] = 2,
+    [MP_F_BUILD_MAP] = 1,
+    [MP_F_STORE_MAP] = 3,
+    #if MICROPY_PY_BUILTINS_SET
+    [MP_F_BUILD_SET] = 2,
+    [MP_F_STORE_SET] = 2,
+    #endif
+    [MP_F_MAKE_FUNCTION_FROM_RAW_CODE] = 3,
+    [MP_F_NATIVE_CALL_FUNCTION_N_KW] = 3,
+    [MP_F_CALL_METHOD_N_KW] = 3,
+    [MP_F_CALL_METHOD_N_KW_VAR] = 3,
+    [MP_F_NATIVE_GETITER] = 2,
+    [MP_F_NATIVE_ITERNEXT] = 1,
+    [MP_F_NLR_PUSH] = 1,
+    [MP_F_NLR_POP] = 0,
+    [MP_F_NATIVE_RAISE] = 1,
+    [MP_F_IMPORT_NAME] = 3,
+    [MP_F_IMPORT_FROM] = 2,
+    [MP_F_IMPORT_ALL] = 1,
+    #if MICROPY_PY_BUILTINS_SLICE
+    [MP_F_NEW_SLICE] = 3,
+    #endif
+    [MP_F_UNPACK_SEQUENCE] = 3,
+    [MP_F_UNPACK_EX] = 3,
+    [MP_F_DELETE_NAME] = 1,
+    [MP_F_DELETE_GLOBAL] = 1,
+    [MP_F_NEW_CELL] = 1,
+    [MP_F_MAKE_CLOSURE_FROM_RAW_CODE] = 3,
+    [MP_F_ARG_CHECK_NUM_SIG] = 3,
+    [MP_F_SETUP_CODE_STATE] = 4,
+    [MP_F_SMALL_INT_FLOOR_DIVIDE] = 2,
+    [MP_F_SMALL_INT_MODULO] = 2,
+    [MP_F_NATIVE_YIELD_FROM] = 3,
+};
+
+#define N_X86 (1)
+#define EXPORT_FUN(name) emit_native_x86_##name
+#include "py/emitnative.c"
+
+#endif

+ 20 - 0
py/emitnxtensa.c

@@ -0,0 +1,20 @@
+// Xtensa specific stuff
+
+#include "py/mpconfig.h"
+
+#if MICROPY_EMIT_XTENSA
+
+// this is defined so that the assembler exports generic assembler API macros
+#define GENERIC_ASM_API (1)
+#include "py/asmxtensa.h"
+
+// Word indices of REG_LOCAL_x in nlr_buf_t
+#define NLR_BUF_IDX_LOCAL_1 (8) // a12
+#define NLR_BUF_IDX_LOCAL_2 (9) // a13
+#define NLR_BUF_IDX_LOCAL_3 (10) // a14
+
+#define N_XTENSA (1)
+#define EXPORT_FUN(name) emit_native_xtensa_##name
+#include "py/emitnative.c"
+
+#endif

+ 17 - 4
py/gc.c

@@ -328,7 +328,9 @@ void gc_collect_start(void) {
     // correctly in the mp_state_ctx structure.  We scan nlr_top, dict_locals,
     // dict_globals, then the root pointer section of mp_state_vm.
     void **ptrs = (void**)(void*)&mp_state_ctx;
-    gc_collect_root(ptrs, offsetof(mp_state_ctx_t, vm.qstr_last_chunk) / sizeof(void*));
+    size_t root_start = offsetof(mp_state_ctx_t, thread.dict_locals);
+    size_t root_end = offsetof(mp_state_ctx_t, vm.qstr_last_chunk);
+    gc_collect_root(ptrs + root_start / sizeof(void*), (root_end - root_start) / sizeof(void*));
 
     #if MICROPY_ENABLE_PYSTACK
     // Trace root pointers from the Python stack.
@@ -360,6 +362,13 @@ void gc_collect_end(void) {
     GC_EXIT();
 }
 
+void gc_sweep_all(void) {
+    GC_ENTER();
+    MP_STATE_MEM(gc_lock_depth)++;
+    MP_STATE_MEM(gc_stack_overflow) = 0;
+    gc_collect_end();
+}
+
 void gc_info(gc_info_t *info) {
     GC_ENTER();
     info->total = MP_STATE_MEM(gc_pool_end) - MP_STATE_MEM(gc_pool_start);
@@ -424,7 +433,8 @@ void gc_info(gc_info_t *info) {
     GC_EXIT();
 }
 
-void *gc_alloc(size_t n_bytes, bool has_finaliser) {
+void *gc_alloc(size_t n_bytes, unsigned int alloc_flags) {
+    bool has_finaliser = alloc_flags & GC_ALLOC_FLAG_HAS_FINALISER;
     size_t n_blocks = ((n_bytes + BYTES_PER_BLOCK - 1) & (~(BYTES_PER_BLOCK - 1))) / BYTES_PER_BLOCK;
     DEBUG_printf("gc_alloc(" UINT_FMT " bytes -> " UINT_FMT " blocks)\n", n_bytes, n_blocks);
 
@@ -444,13 +454,14 @@ void *gc_alloc(size_t n_bytes, bool has_finaliser) {
     size_t i;
     size_t end_block;
     size_t start_block;
-    size_t n_free = 0;
+    size_t n_free;
     int collected = !MP_STATE_MEM(gc_auto_collect_enabled);
 
     #if MICROPY_GC_ALLOC_THRESHOLD
     if (!collected && MP_STATE_MEM(gc_alloc_amount) >= MP_STATE_MEM(gc_alloc_threshold)) {
         GC_EXIT();
         gc_collect();
+        collected = 1;
         GC_ENTER();
     }
     #endif
@@ -458,6 +469,7 @@ void *gc_alloc(size_t n_bytes, bool has_finaliser) {
     for (;;) {
 
         // look for a run of n_blocks available blocks
+        n_free = 0;
         for (i = MP_STATE_MEM(gc_last_free_atb_index); i < MP_STATE_MEM(gc_alloc_table_byte_len); i++) {
             byte a = MP_STATE_MEM(gc_alloc_table_start)[i];
             if (ATB_0_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 0; goto found; } } else { n_free = 0; }
@@ -900,7 +912,8 @@ void gc_dump_alloc_table(void) {
     GC_EXIT();
 }
 
-#if DEBUG_PRINT
+#if 0
+// For testing the GC functions
 void gc_test(void) {
     mp_uint_t len = 500;
     mp_uint_t *heap = malloc(len);

+ 8 - 1
py/gc.h

@@ -45,7 +45,14 @@ void gc_collect_start(void);
 void gc_collect_root(void **ptrs, size_t len);
 void gc_collect_end(void);
 
-void *gc_alloc(size_t n_bytes, bool has_finaliser);
+// Use this function to sweep the whole heap and run all finalisers
+void gc_sweep_all(void);
+
+enum {
+    GC_ALLOC_FLAG_HAS_FINALISER = 1,
+};
+
+void *gc_alloc(size_t n_bytes, unsigned int alloc_flags);
 void gc_free(void *ptr); // does not call finaliser
 size_t gc_nbytes(const void *ptr);
 void *gc_realloc(void *ptr, size_t n_bytes, bool allow_move);

+ 11 - 11
py/grammar.h

@@ -122,8 +122,8 @@ DEF_RULE_NC(augassign, or(12), tok(DEL_PLUS_EQUAL), tok(DEL_MINUS_EQUAL), tok(DE
 DEF_RULE(del_stmt, c(del_stmt), and(2), tok(KW_DEL), rule(exprlist))
 DEF_RULE(pass_stmt, c(generic_all_nodes), and(1), tok(KW_PASS))
 DEF_RULE_NC(flow_stmt, or(5), rule(break_stmt), rule(continue_stmt), rule(return_stmt), rule(raise_stmt), rule(yield_stmt))
-DEF_RULE(break_stmt, c(break_stmt), and(1), tok(KW_BREAK))
-DEF_RULE(continue_stmt, c(continue_stmt), and(1), tok(KW_CONTINUE))
+DEF_RULE(break_stmt, c(break_cont_stmt), and(1), tok(KW_BREAK))
+DEF_RULE(continue_stmt, c(break_cont_stmt), and(1), tok(KW_CONTINUE))
 DEF_RULE(return_stmt, c(return_stmt), and(2), tok(KW_RETURN), opt_rule(testlist))
 DEF_RULE(yield_stmt, c(yield_stmt), and(1), rule(yield_expr))
 DEF_RULE(raise_stmt, c(raise_stmt), and(2), tok(KW_RAISE), opt_rule(raise_stmt_arg))
@@ -157,8 +157,8 @@ DEF_RULE_NC(as_name, and_ident(2), tok(KW_AS), tok(NAME))
 DEF_RULE_NC(import_as_names, list_with_end, rule(import_as_name), tok(DEL_COMMA))
 DEF_RULE_NC(dotted_as_names, list, rule(dotted_as_name), tok(DEL_COMMA))
 DEF_RULE_NC(dotted_name, list, tok(NAME), tok(DEL_PERIOD))
-DEF_RULE(global_stmt, c(global_stmt), and(2), tok(KW_GLOBAL), rule(name_list))
-DEF_RULE(nonlocal_stmt, c(nonlocal_stmt), and(2), tok(KW_NONLOCAL), rule(name_list))
+DEF_RULE(global_stmt, c(global_nonlocal_stmt), and(2), tok(KW_GLOBAL), rule(name_list))
+DEF_RULE(nonlocal_stmt, c(global_nonlocal_stmt), and(2), tok(KW_NONLOCAL), rule(name_list))
 DEF_RULE_NC(name_list, list, tok(NAME), tok(DEL_COMMA))
 DEF_RULE(assert_stmt, c(assert_stmt), and(3), tok(KW_ASSERT), rule(test), opt_rule(assert_stmt_extra))
 DEF_RULE_NC(assert_stmt_extra, and_ident(2), tok(DEL_COMMA), rule(test))
@@ -231,8 +231,8 @@ DEF_RULE(lambdef_nocond, c(lambdef), and_blank(4), tok(KW_LAMBDA), opt_rule(vara
 // power: atom_expr ['**' factor]
 // atom_expr: 'await' atom trailer* | atom trailer*
 
-DEF_RULE(or_test, c(or_test), list, rule(and_test), tok(KW_OR))
-DEF_RULE(and_test, c(and_test), list, rule(not_test), tok(KW_AND))
+DEF_RULE(or_test, c(or_and_test), list, rule(and_test), tok(KW_OR))
+DEF_RULE(and_test, c(or_and_test), list, rule(not_test), tok(KW_AND))
 DEF_RULE_NC(not_test, or(2), rule(not_test_2), rule(comparison))
 DEF_RULE(not_test_2, c(not_test_2), and(2), tok(KW_NOT), rule(not_test))
 DEF_RULE(comparison, c(comparison), list, rule(expr), rule(comp_op))
@@ -241,9 +241,9 @@ DEF_RULE_NC(comp_op_not_in, and(2), tok(KW_NOT), tok(KW_IN))
 DEF_RULE_NC(comp_op_is, and(2), tok(KW_IS), opt_rule(comp_op_is_not))
 DEF_RULE_NC(comp_op_is_not, and(1), tok(KW_NOT))
 DEF_RULE(star_expr, c(star_expr), and(2), tok(OP_STAR), rule(expr))
-DEF_RULE(expr, c(expr), list, rule(xor_expr), tok(OP_PIPE))
-DEF_RULE(xor_expr, c(xor_expr), list, rule(and_expr), tok(OP_CARET))
-DEF_RULE(and_expr, c(and_expr), list, rule(shift_expr), tok(OP_AMPERSAND))
+DEF_RULE(expr, c(binary_op), list, rule(xor_expr), tok(OP_PIPE))
+DEF_RULE(xor_expr, c(binary_op), list, rule(and_expr), tok(OP_CARET))
+DEF_RULE(and_expr, c(binary_op), list, rule(shift_expr), tok(OP_AMPERSAND))
 DEF_RULE(shift_expr, c(term), list, rule(arith_expr), rule(shift_op))
 DEF_RULE_NC(shift_op, or(2), tok(OP_DBL_LESS), tok(OP_DBL_MORE))
 DEF_RULE(arith_expr, c(term), list, rule(term), rule(arith_op))
@@ -290,8 +290,8 @@ DEF_RULE(trailer_period, c(trailer_period), and(2), tok(DEL_PERIOD), tok(NAME))
 #if MICROPY_PY_BUILTINS_SLICE
 DEF_RULE(subscriptlist, c(generic_tuple), list_with_end, rule(subscript), tok(DEL_COMMA))
 DEF_RULE_NC(subscript, or(2), rule(subscript_3), rule(subscript_2))
-DEF_RULE(subscript_2, c(subscript_2), and_ident(2), rule(test), opt_rule(subscript_3))
-DEF_RULE(subscript_3, c(subscript_3), and(2), tok(DEL_COLON), opt_rule(subscript_3b))
+DEF_RULE(subscript_2, c(subscript), and_ident(2), rule(test), opt_rule(subscript_3))
+DEF_RULE(subscript_3, c(subscript), and(2), tok(DEL_COLON), opt_rule(subscript_3b))
 DEF_RULE_NC(subscript_3b, or(2), rule(subscript_3c), rule(subscript_3d))
 DEF_RULE_NC(subscript_3c, and(2), tok(DEL_COLON), opt_rule(test))
 DEF_RULE_NC(subscript_3d, and_ident(2), rule(test), opt_rule(sliceop))

+ 2 - 0
py/lexer.c

@@ -590,6 +590,8 @@ void mp_lexer_to_next(mp_lexer_t *lex) {
                 }
                 vstr_add_char(&lex->vstr, CUR_CHAR(lex));
                 next_char(lex);
+            } else if (is_char(lex, '_')) {
+                next_char(lex);
             } else {
                 break;
             }

+ 3 - 0
py/makeqstrdata.py

@@ -114,6 +114,9 @@ def parse_input_headers(infiles):
                 if ident == "":
                     # Sort empty qstr above all still
                     order = -200000
+                elif ident == "__dir__":
+                    # Put __dir__ after empty qstr for builtin dir() to work
+                    order = -190000
                 elif ident.startswith("__"):
                     order -= 100000
                 qstrs[ident] = (order, ident, qstr)

+ 4 - 20
py/makeversionhdr.py

@@ -46,15 +46,7 @@ def get_version_info_from_git():
     except OSError:
         return None
 
-    # Try to extract MicroPython version from git tag
-    if git_tag.startswith("v"):
-        ver = git_tag[1:].split("-")[0].split(".")
-        if len(ver) == 2:
-            ver.append("0")
-    else:
-        ver = ["0", "0", "1"]
-
-    return git_tag, git_hash, ver
+    return git_tag, git_hash
 
 def get_version_info_from_docs_conf():
     with open(os.path.join(os.path.dirname(sys.argv[0]), "..", "docs", "conf.py")) as f:
@@ -62,10 +54,7 @@ def get_version_info_from_docs_conf():
             if line.startswith("version = release = '"):
                 ver = line.strip().split(" = ")[2].strip("'")
                 git_tag = "v" + ver
-                ver = ver.split(".")
-                if len(ver) == 2:
-                    ver.append("0")
-                return git_tag, "<no hash>", ver
+                return git_tag, "<no hash>"
     return None
 
 def make_version_header(filename):
@@ -74,7 +63,7 @@ def make_version_header(filename):
     if info is None:
         info = get_version_info_from_docs_conf()
 
-    git_tag, git_hash, ver = info
+    git_tag, git_hash = info
 
     # Generate the file with the git and version info
     file_data = """\
@@ -82,12 +71,7 @@ def make_version_header(filename):
 #define MICROPY_GIT_TAG "%s"
 #define MICROPY_GIT_HASH "%s"
 #define MICROPY_BUILD_DATE "%s"
-#define MICROPY_VERSION_MAJOR (%s)
-#define MICROPY_VERSION_MINOR (%s)
-#define MICROPY_VERSION_MICRO (%s)
-#define MICROPY_VERSION_STRING "%s.%s.%s"
-""" % (git_tag, git_hash, datetime.date.today().strftime("%Y-%m-%d"),
-    ver[0], ver[1], ver[2], ver[0], ver[1], ver[2])
+""" % (git_tag, git_hash, datetime.date.today().strftime("%Y-%m-%d"))
 
     # Check if the file contents changed from last time
     write_file = True

+ 8 - 0
py/malloc.c

@@ -62,6 +62,13 @@
 #define realloc(ptr, n) gc_realloc(ptr, n, true)
 #define realloc_ext(ptr, n, mv) gc_realloc(ptr, n, mv)
 #else
+
+// GC is disabled.  Use system malloc/realloc/free.
+
+#if MICROPY_ENABLE_FINALISER
+#error MICROPY_ENABLE_FINALISER requires MICROPY_ENABLE_GC
+#endif
+
 STATIC void *realloc_ext(void *ptr, size_t n_bytes, bool allow_move) {
     if (allow_move) {
         return realloc(ptr, n_bytes);
@@ -72,6 +79,7 @@ STATIC void *realloc_ext(void *ptr, size_t n_bytes, bool allow_move) {
         return NULL;
     }
 }
+
 #endif // MICROPY_ENABLE_GC
 
 void *m_malloc(size_t num_bytes) {

+ 4 - 4
py/map.c

@@ -423,13 +423,13 @@ void mp_set_clear(mp_set_t *set) {
 #if defined(DEBUG_PRINT) && DEBUG_PRINT
 void mp_map_dump(mp_map_t *map) {
     for (size_t i = 0; i < map->alloc; i++) {
-        if (map->table[i].key != NULL) {
+        if (map->table[i].key != MP_OBJ_NULL) {
             mp_obj_print(map->table[i].key, PRINT_REPR);
         } else {
-            printf("(nil)");
+            DEBUG_printf("(nil)");
         }
-        printf(": %p\n", map->table[i].value);
+        DEBUG_printf(": %p\n", map->table[i].value);
     }
-    printf("---\n");
+    DEBUG_printf("---\n");
 }
 #endif

+ 3 - 0
py/misc.h

@@ -50,6 +50,9 @@ typedef unsigned int uint;
 #define _MP_STRINGIFY(x) #x
 #define MP_STRINGIFY(x) _MP_STRINGIFY(x)
 
+// Static assertion macro
+#define MP_STATIC_ASSERT(cond) ((void)sizeof(char[1 - 2 * !(cond)]))
+
 /** memory allocation ******************************************/
 
 // TODO make a lazy m_renew that can increase by a smaller amount than requested (but by at least 1 more element)

+ 7 - 6
py/mkrules.mk

@@ -46,10 +46,7 @@ vpath %.c . $(TOP)
 $(BUILD)/%.o: %.c
 	$(call compile_c)
 
-# List all native flags since the current build system doesn't have
-# the MicroPython configuration available. However, these flags are
-# needed to extract all qstrings
-QSTR_GEN_EXTRA_CFLAGS += -DNO_QSTR -DN_X64 -DN_X86 -DN_THUMB -DN_ARM -DN_XTENSA
+QSTR_GEN_EXTRA_CFLAGS += -DNO_QSTR
 QSTR_GEN_EXTRA_CFLAGS += -I$(BUILD)/tmp
 
 vpath %.c . $(TOP)
@@ -69,9 +66,13 @@ $(BUILD)/%.pp: %.c
 # to get built before we try to compile any of them.
 $(OBJ): | $(HEADER_BUILD)/qstrdefs.generated.h $(HEADER_BUILD)/mpversion.h
 
-$(HEADER_BUILD)/qstr.i.last: $(SRC_QSTR) | $(HEADER_BUILD)/mpversion.h
+# The logic for qstr regeneration is:
+# - if anything in QSTR_GLOBAL_DEPENDENCIES is newer, then process all source files ($^)
+# - else, if list of newer prerequisites ($?) is not empty, then process just these ($?)
+# - else, process all source files ($^) [this covers "make -B" which can set $? to empty]
+$(HEADER_BUILD)/qstr.i.last: $(SRC_QSTR) $(QSTR_GLOBAL_DEPENDENCIES) | $(HEADER_BUILD)/mpversion.h
 	$(ECHO) "GEN $@"
-	$(Q)$(CPP) $(QSTR_GEN_EXTRA_CFLAGS) $(CFLAGS) $(if $?,$?,$^) >$(HEADER_BUILD)/qstr.i.last;
+	$(Q)$(CPP) $(QSTR_GEN_EXTRA_CFLAGS) $(CFLAGS) $(if $(filter $?,$(QSTR_GLOBAL_DEPENDENCIES)),$^,$(if $?,$?,$^)) >$(HEADER_BUILD)/qstr.i.last;
 
 $(HEADER_BUILD)/qstr.split: $(HEADER_BUILD)/qstr.i.last
 	$(ECHO) "GEN $@"

+ 51 - 11
py/modbuiltins.c

@@ -186,10 +186,17 @@ STATIC mp_obj_t mp_builtin_dir(size_t n_args, const mp_obj_t *args) {
         // Make a list of names in the given object
         // Implemented by probing all possible qstrs with mp_load_method_maybe
         size_t nqstr = QSTR_TOTAL();
-        for (size_t i = 1; i < nqstr; ++i) {
+        for (size_t i = MP_QSTR_ + 1; i < nqstr; ++i) {
             mp_obj_t dest[2];
-            mp_load_method_maybe(args[0], i, dest);
+            mp_load_method_protected(args[0], i, dest, false);
             if (dest[0] != MP_OBJ_NULL) {
+                #if MICROPY_PY_ALL_SPECIAL_METHODS
+                // Support for __dir__: see if we can dispatch to this special method
+                // This relies on MP_QSTR__dir__ being first after MP_QSTR_
+                if (i == MP_QSTR___dir__ && dest[1] != MP_OBJ_NULL) {
+                    return mp_call_method_n_kw(0, 0, dest);
+                }
+                #endif
                 mp_obj_list_append(dir, MP_OBJ_NEW_QSTR(i));
             }
         }
@@ -210,7 +217,12 @@ STATIC mp_obj_t mp_builtin_hash(mp_obj_t o_in) {
 MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_hash_obj, mp_builtin_hash);
 
 STATIC mp_obj_t mp_builtin_hex(mp_obj_t o_in) {
+    #if MICROPY_PY_BUILTINS_STR_OP_MODULO
     return mp_binary_op(MP_BINARY_OP_MODULO, MP_OBJ_NEW_QSTR(MP_QSTR__percent__hash_x), o_in);
+    #else
+    mp_obj_t args[] = { MP_OBJ_NEW_QSTR(MP_QSTR__brace_open__colon__hash_x_brace_close_), o_in };
+    return mp_obj_str_format(MP_ARRAY_SIZE(args), args, NULL);
+    #endif
 }
 MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_hex_obj, mp_builtin_hex);
 
@@ -315,7 +327,12 @@ STATIC mp_obj_t mp_builtin_next(mp_obj_t o) {
 MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_next_obj, mp_builtin_next);
 
 STATIC mp_obj_t mp_builtin_oct(mp_obj_t o_in) {
+    #if MICROPY_PY_BUILTINS_STR_OP_MODULO
     return mp_binary_op(MP_BINARY_OP_MODULO, MP_OBJ_NEW_QSTR(MP_QSTR__percent__hash_o), o_in);
+    #else
+    mp_obj_t args[] = { MP_OBJ_NEW_QSTR(MP_QSTR__brace_open__colon__hash_o_brace_close_), o_in };
+    return mp_obj_str_format(MP_ARRAY_SIZE(args), args, NULL);
+    #endif
 }
 MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_oct_obj, mp_builtin_oct);
 
@@ -379,7 +396,7 @@ STATIC mp_obj_t mp_builtin_print(size_t n_args, const mp_obj_t *pos_args, mp_map
     mp_arg_parse_all(0, NULL, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, u.args);
 
     #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES
-    // TODO file may not be a concrete object (eg it could be a small-int)
+    mp_get_stream_raise(u.args[ARG_file].u_obj, MP_STREAM_OP_WRITE);
     mp_print_t print = {MP_OBJ_TO_PTR(u.args[ARG_file].u_obj), mp_stream_write_adaptor};
     #endif
 
@@ -438,7 +455,36 @@ MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_repr_obj, mp_builtin_repr);
 STATIC mp_obj_t mp_builtin_round(size_t n_args, const mp_obj_t *args) {
     mp_obj_t o_in = args[0];
     if (MP_OBJ_IS_INT(o_in)) {
-        return o_in;
+        if (n_args <= 1) {
+            return o_in;
+        }
+
+        #if !MICROPY_PY_BUILTINS_ROUND_INT
+        mp_raise_NotImplementedError(NULL);
+        #else
+        mp_int_t num_dig = mp_obj_get_int(args[1]);
+        if (num_dig >= 0) {
+            return o_in;
+        }
+
+        mp_obj_t mult = mp_binary_op(MP_BINARY_OP_POWER, MP_OBJ_NEW_SMALL_INT(10), MP_OBJ_NEW_SMALL_INT(-num_dig));
+        mp_obj_t half_mult =  mp_binary_op(MP_BINARY_OP_FLOOR_DIVIDE, mult, MP_OBJ_NEW_SMALL_INT(2));
+        mp_obj_t modulo = mp_binary_op(MP_BINARY_OP_MODULO, o_in, mult);
+        mp_obj_t rounded = mp_binary_op(MP_BINARY_OP_SUBTRACT, o_in, modulo);
+        if (mp_obj_is_true(mp_binary_op(MP_BINARY_OP_MORE, half_mult, modulo))) {
+            return rounded;
+        } else if (mp_obj_is_true(mp_binary_op(MP_BINARY_OP_MORE, modulo, half_mult))) {
+            return mp_binary_op(MP_BINARY_OP_ADD, rounded, mult);
+        } else {
+            // round to even number
+            mp_obj_t floor = mp_binary_op(MP_BINARY_OP_FLOOR_DIVIDE, o_in, mult);
+            if (mp_obj_is_true(mp_binary_op(MP_BINARY_OP_AND, floor, MP_OBJ_NEW_SMALL_INT(1)))) {
+                return mp_binary_op(MP_BINARY_OP_ADD, rounded, mult);
+            } else {
+                return rounded;
+            }
+        }
+        #endif
     }
 #if MICROPY_PY_BUILTINS_FLOAT
     mp_float_t val = mp_obj_get_float(o_in);
@@ -525,14 +571,8 @@ MP_DEFINE_CONST_FUN_OBJ_2(mp_builtin_delattr_obj, mp_builtin_delattr);
 
 STATIC mp_obj_t mp_builtin_hasattr(mp_obj_t object_in, mp_obj_t attr_in) {
     qstr attr = mp_obj_str_get_qstr(attr_in);
-
     mp_obj_t dest[2];
-    // TODO: https://docs.python.org/3/library/functions.html?highlight=hasattr#hasattr
-    // explicitly says "This is implemented by calling getattr(object, name) and seeing
-    // whether it raises an AttributeError or not.", so we should explicitly wrap this
-    // in nlr_push and handle exception.
-    mp_load_method_maybe(object_in, attr, dest);
-
+    mp_load_method_protected(object_in, attr, dest, false);
     return mp_obj_new_bool(dest[0] != MP_OBJ_NULL);
 }
 MP_DEFINE_CONST_FUN_OBJ_2(mp_builtin_hasattr_obj, mp_builtin_hasattr);

+ 69 - 0
py/modio.c

@@ -30,6 +30,8 @@
 #include "py/runtime.h"
 #include "py/builtin.h"
 #include "py/stream.h"
+#include "py/binary.h"
+#include "py/objarray.h"
 #include "py/objstringio.h"
 #include "py/frozenmod.h"
 
@@ -38,6 +40,70 @@
 extern const mp_obj_type_t mp_type_fileio;
 extern const mp_obj_type_t mp_type_textio;
 
+#if MICROPY_PY_IO_IOBASE
+
+STATIC const mp_obj_type_t mp_type_iobase;
+
+STATIC mp_obj_base_t iobase_singleton = {&mp_type_iobase};
+
+STATIC mp_obj_t iobase_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(&iobase_singleton);
+}
+
+STATIC mp_uint_t iobase_read_write(mp_obj_t obj, void *buf, mp_uint_t size, int *errcode, qstr qst) {
+    mp_obj_t dest[3];
+    mp_load_method(obj, qst, dest);
+    mp_obj_array_t ar = {{&mp_type_bytearray}, BYTEARRAY_TYPECODE, 0, size, buf};
+    dest[2] = MP_OBJ_FROM_PTR(&ar);
+    mp_obj_t ret = mp_call_method_n_kw(1, 0, dest);
+    if (ret == mp_const_none) {
+        *errcode = MP_EAGAIN;
+        return MP_STREAM_ERROR;
+    } else {
+        return mp_obj_get_int(ret);
+    }
+}
+STATIC mp_uint_t iobase_read(mp_obj_t obj, void *buf, mp_uint_t size, int *errcode) {
+    return iobase_read_write(obj, buf, size, errcode, MP_QSTR_readinto);
+}
+
+STATIC mp_uint_t iobase_write(mp_obj_t obj, const void *buf, mp_uint_t size, int *errcode) {
+    return iobase_read_write(obj, (void*)buf, size, errcode, MP_QSTR_write);
+}
+
+STATIC mp_uint_t iobase_ioctl(mp_obj_t obj, mp_uint_t request, uintptr_t arg, int *errcode) {
+    mp_obj_t dest[4];
+    mp_load_method(obj, MP_QSTR_ioctl, dest);
+    dest[2] = mp_obj_new_int_from_uint(request);
+    dest[3] = mp_obj_new_int_from_uint(arg);
+    mp_int_t ret = mp_obj_get_int(mp_call_method_n_kw(2, 0, dest));
+    if (ret >= 0) {
+        return ret;
+    } else {
+        *errcode = -ret;
+        return MP_STREAM_ERROR;
+    }
+}
+
+STATIC const mp_stream_p_t iobase_p = {
+    .read = iobase_read,
+    .write = iobase_write,
+    .ioctl = iobase_ioctl,
+};
+
+STATIC const mp_obj_type_t mp_type_iobase = {
+    { &mp_type_type },
+    .name = MP_QSTR_IOBase,
+    .make_new = iobase_make_new,
+    .protocol = &iobase_p,
+};
+
+#endif // MICROPY_PY_IO_IOBASE
+
 #if MICROPY_PY_IO_BUFFEREDWRITER
 typedef struct _mp_obj_bufwriter_t {
     mp_obj_base_t base;
@@ -187,6 +253,9 @@ STATIC const mp_rom_map_elem_t mp_module_io_globals_table[] = {
     // Note: mp_builtin_open_obj should be defined by port, it's not
     // part of the core.
     { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) },
+    #if MICROPY_PY_IO_IOBASE
+    { MP_ROM_QSTR(MP_QSTR_IOBase), MP_ROM_PTR(&mp_type_iobase) },
+    #endif
     #if MICROPY_PY_IO_RESOURCE_STREAM
     { MP_ROM_QSTR(MP_QSTR_resource_stream), MP_ROM_PTR(&resource_stream_obj) },
     #endif

+ 69 - 2
py/modmath.c

@@ -169,7 +169,7 @@ MATH_FUN_1(gamma, tgamma)
 // lgamma(x): return the natural logarithm of the gamma function of x
 MATH_FUN_1(lgamma, lgamma)
 #endif
-//TODO: factorial, fsum
+//TODO: fsum
 
 // Function that takes a variable number of arguments
 
@@ -187,7 +187,7 @@ STATIC mp_obj_t mp_math_log(size_t n_args, const mp_obj_t *args) {
         if (base <= (mp_float_t)0.0) {
             math_error();
         } else if (base == (mp_float_t)1.0) {
-            mp_raise_msg(&mp_type_ZeroDivisionError, "division by zero");
+            mp_raise_msg(&mp_type_ZeroDivisionError, "divide by zero");
         }
         return mp_obj_new_float(l / MICROPY_FLOAT_C_FUN(log)(base));
     }
@@ -232,6 +232,70 @@ STATIC mp_obj_t mp_math_degrees(mp_obj_t x_obj) {
 }
 STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_math_degrees_obj, mp_math_degrees);
 
+#if MICROPY_PY_MATH_FACTORIAL
+
+#if MICROPY_OPT_MATH_FACTORIAL
+
+// factorial(x): slightly efficient recursive implementation
+STATIC mp_obj_t mp_math_factorial_inner(mp_uint_t start, mp_uint_t end) {
+    if (start == end) {
+        return mp_obj_new_int(start);
+    } else if (end - start == 1) {
+        return mp_binary_op(MP_BINARY_OP_MULTIPLY, MP_OBJ_NEW_SMALL_INT(start), MP_OBJ_NEW_SMALL_INT(end));
+    } else if (end - start == 2) {
+        mp_obj_t left = MP_OBJ_NEW_SMALL_INT(start);
+        mp_obj_t middle = MP_OBJ_NEW_SMALL_INT(start + 1);
+        mp_obj_t right = MP_OBJ_NEW_SMALL_INT(end);
+        mp_obj_t tmp = mp_binary_op(MP_BINARY_OP_MULTIPLY, left, middle);
+        return mp_binary_op(MP_BINARY_OP_MULTIPLY, tmp, right);
+    } else {
+        mp_uint_t middle = start + ((end - start) >> 1);
+        mp_obj_t left = mp_math_factorial_inner(start, middle);
+        mp_obj_t right = mp_math_factorial_inner(middle + 1, end);
+        return mp_binary_op(MP_BINARY_OP_MULTIPLY, left, right);
+    }
+}
+STATIC mp_obj_t mp_math_factorial(mp_obj_t x_obj) {
+    mp_int_t max = mp_obj_get_int(x_obj);
+    if (max < 0) {
+        mp_raise_msg(&mp_type_ValueError, "negative factorial");
+    } else if (max == 0) {
+        return MP_OBJ_NEW_SMALL_INT(1);
+    }
+    return mp_math_factorial_inner(1, max);
+}
+
+#else
+
+// factorial(x): squared difference implementation
+// based on http://www.luschny.de/math/factorial/index.html
+STATIC mp_obj_t mp_math_factorial(mp_obj_t x_obj) {
+    mp_int_t max = mp_obj_get_int(x_obj);
+    if (max < 0) {
+        mp_raise_msg(&mp_type_ValueError, "negative factorial");
+    } else if (max <= 1) {
+        return MP_OBJ_NEW_SMALL_INT(1);
+    }
+    mp_int_t h = max >> 1;
+    mp_int_t q = h * h;
+    mp_int_t r = q << 1;
+    if (max & 1) {
+        r *= max;
+    }
+    mp_obj_t prod = MP_OBJ_NEW_SMALL_INT(r);
+    for (mp_int_t num = 1; num < max - 2; num += 2) {
+        q -= num;
+        prod = mp_binary_op(MP_BINARY_OP_MULTIPLY, prod, MP_OBJ_NEW_SMALL_INT(q));
+    }
+    return prod;
+}
+
+#endif
+
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_math_factorial_obj, mp_math_factorial);
+
+#endif
+
 STATIC const mp_rom_map_elem_t mp_module_math_globals_table[] = {
     { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_math) },
     { MP_ROM_QSTR(MP_QSTR_e), mp_const_float_e },
@@ -274,6 +338,9 @@ STATIC const mp_rom_map_elem_t mp_module_math_globals_table[] = {
     { MP_ROM_QSTR(MP_QSTR_trunc), MP_ROM_PTR(&mp_math_trunc_obj) },
     { MP_ROM_QSTR(MP_QSTR_radians), MP_ROM_PTR(&mp_math_radians_obj) },
     { MP_ROM_QSTR(MP_QSTR_degrees), MP_ROM_PTR(&mp_math_degrees_obj) },
+    #if MICROPY_PY_MATH_FACTORIAL
+    { MP_ROM_QSTR(MP_QSTR_factorial), MP_ROM_PTR(&mp_math_factorial_obj) },
+    #endif
     #if MICROPY_PY_MATH_SPECIAL_FUNCTIONS
     { MP_ROM_QSTR(MP_QSTR_erf), MP_ROM_PTR(&mp_math_erf_obj) },
     { MP_ROM_QSTR(MP_QSTR_erfc), MP_ROM_PTR(&mp_math_erfc_obj) },

+ 4 - 0
py/modmicropython.c

@@ -35,6 +35,7 @@
 // Various builtins specific to MicroPython runtime,
 // living in micropython module
 
+#if MICROPY_ENABLE_COMPILER
 STATIC mp_obj_t mp_micropython_opt_level(size_t n_args, const mp_obj_t *args) {
     if (n_args == 0) {
         return MP_OBJ_NEW_SMALL_INT(MP_STATE_VM(mp_optimise_value));
@@ -44,6 +45,7 @@ STATIC mp_obj_t mp_micropython_opt_level(size_t n_args, const mp_obj_t *args) {
     }
 }
 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_micropython_opt_level_obj, 0, 1, mp_micropython_opt_level);
+#endif
 
 #if MICROPY_PY_MICROPYTHON_MEM_INFO
 
@@ -158,7 +160,9 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(mp_micropython_schedule_obj, mp_micropython_sch
 STATIC const mp_rom_map_elem_t mp_module_micropython_globals_table[] = {
     { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_micropython) },
     { MP_ROM_QSTR(MP_QSTR_const), MP_ROM_PTR(&mp_identity_obj) },
+    #if MICROPY_ENABLE_COMPILER
     { MP_ROM_QSTR(MP_QSTR_opt_level), MP_ROM_PTR(&mp_micropython_opt_level_obj) },
+    #endif
 #if MICROPY_PY_MICROPYTHON_MEM_INFO
 #if MICROPY_MEM_STATS
     { MP_ROM_QSTR(MP_QSTR_mem_total), MP_ROM_PTR(&mp_micropython_mem_total_obj) },

+ 5 - 4
py/modsys.c

@@ -37,8 +37,6 @@
 
 #if MICROPY_PY_SYS
 
-#include "genhdr/mpversion.h"
-
 // defined per port; type of these is irrelevant, just need pointer
 extern struct _mp_dummy_t mp_sys_stdin_obj;
 extern struct _mp_dummy_t mp_sys_stdout_obj;
@@ -106,7 +104,8 @@ STATIC mp_obj_t mp_sys_print_exception(size_t n_args, const mp_obj_t *args) {
     #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES
     void *stream_obj = &mp_sys_stdout_obj;
     if (n_args > 1) {
-        stream_obj = MP_OBJ_TO_PTR(args[1]); // XXX may fail
+        mp_get_stream_raise(args[1], MP_STREAM_OP_WRITE);
+        stream_obj = MP_OBJ_TO_PTR(args[1]);
     }
 
     mp_print_t print = {stream_obj, mp_stream_write_adaptor};
@@ -140,10 +139,12 @@ STATIC mp_obj_t mp_sys_exc_info(void) {
 MP_DEFINE_CONST_FUN_OBJ_0(mp_sys_exc_info_obj, mp_sys_exc_info);
 #endif
 
+#if MICROPY_PY_SYS_GETSIZEOF
 STATIC mp_obj_t mp_sys_getsizeof(mp_obj_t obj) {
     return mp_unary_op(MP_UNARY_OP_SIZEOF, obj);
 }
-MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_getsizeof_obj, mp_sys_getsizeof);
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_getsizeof_obj, mp_sys_getsizeof);
+#endif
 
 STATIC const mp_rom_map_elem_t mp_module_sys_globals_table[] = {
     { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_sys) },

+ 136 - 12
py/mpconfig.h

@@ -26,6 +26,23 @@
 #ifndef MICROPY_INCLUDED_PY_MPCONFIG_H
 #define MICROPY_INCLUDED_PY_MPCONFIG_H
 
+// Current version of MicroPython
+#define MICROPY_VERSION_MAJOR (1)
+#define MICROPY_VERSION_MINOR (9)
+#define MICROPY_VERSION_MICRO (4)
+
+// Combined version as a 32-bit number for convenience
+#define MICROPY_VERSION ( \
+    MICROPY_VERSION_MAJOR << 16 \
+    | MICROPY_VERSION_MINOR << 8 \
+    | MICROPY_VERSION_MICRO)
+
+// String version
+#define MICROPY_VERSION_STRING \
+    MP_STRINGIFY(MICROPY_VERSION_MAJOR) "." \
+    MP_STRINGIFY(MICROPY_VERSION_MINOR) "." \
+    MP_STRINGIFY(MICROPY_VERSION_MICRO)
+
 // This file contains default configuration settings for MicroPython.
 // You can override any of the options below using mpconfigport.h file
 // located in a directory of your port.
@@ -106,6 +123,15 @@
 #define MICROPY_ALLOC_GC_STACK_SIZE (64)
 #endif
 
+// The C-type to use for entries in the GC stack.  By default it allows the
+// heap to be as large as the address space, but the bit-width of this type can
+// be reduced to save memory when the heap is small enough.  The type must be
+// big enough to index all blocks in the heap, which is set by
+// heap-size-in-bytes / MICROPY_BYTES_PER_GC_BLOCK.
+#ifndef MICROPY_GC_STACK_ENTRY_TYPE
+#define MICROPY_GC_STACK_ENTRY_TYPE size_t
+#endif
+
 // Be conservative and always clear to zero newly (re)allocated memory in the GC.
 // This helps eliminate stray pointers that hold on to memory that's no longer
 // used.  It decreases performance due to unnecessary memory clearing.
@@ -366,6 +392,11 @@
 #define MICROPY_MEM_STATS (0)
 #endif
 
+// The mp_print_t printer used for debugging output
+#ifndef MICROPY_DEBUG_PRINTER
+#define MICROPY_DEBUG_PRINTER (&mp_plat_print)
+#endif
+
 // Whether to build functions that print debugging info:
 //   mp_bytecode_print
 //   mp_parse_node_print
@@ -378,6 +409,11 @@
 #define MICROPY_DEBUG_VERBOSE (0)
 #endif
 
+// Whether to enable a simple VM stack overflow check
+#ifndef MICROPY_DEBUG_VM_STACK_OVERFLOW
+#define MICROPY_DEBUG_VM_STACK_OVERFLOW (0)
+#endif
+
 /*****************************************************************************/
 /* Optimisations                                                             */
 
@@ -402,6 +438,12 @@
 #define MICROPY_OPT_MPZ_BITWISE (0)
 #endif
 
+
+// Whether math.factorial is large, fast and recursive (1) or small and slow (0).
+#ifndef MICROPY_OPT_MATH_FACTORIAL
+#define MICROPY_OPT_MATH_FACTORIAL (0)
+#endif
+
 /*****************************************************************************/
 /* Python internal features                                                  */
 
@@ -622,6 +664,11 @@ typedef double mp_float_t;
 #define MICROPY_MODULE_BUILTIN_INIT (0)
 #endif
 
+// Whether to support module-level __getattr__ (see PEP 562)
+#ifndef MICROPY_MODULE_GETATTR
+#define MICROPY_MODULE_GETATTR (0)
+#endif
+
 // Whether module weak links are supported
 #ifndef MICROPY_MODULE_WEAK_LINKS
 #define MICROPY_MODULE_WEAK_LINKS (0)
@@ -681,6 +728,16 @@ typedef double mp_float_t;
 #define MICROPY_VFS (0)
 #endif
 
+// Support for VFS POSIX component, to mount a POSIX filesystem within VFS
+#ifndef MICROPY_VFS
+#define MICROPY_VFS_POSIX (0)
+#endif
+
+// Support for VFS FAT component, to mount a FAT filesystem within VFS
+#ifndef MICROPY_VFS
+#define MICROPY_VFS_FAT (0)
+#endif
+
 /*****************************************************************************/
 /* Fine control over Python builtins, classes, modules, etc                  */
 
@@ -696,14 +753,16 @@ typedef double mp_float_t;
 #define MICROPY_PY_FUNCTION_ATTRS (0)
 #endif
 
-// Whether to support descriptors (__get__ and __set__)
-// This costs some code size and makes all load attrs and store attrs slow
+// Whether to support the descriptors __get__, __set__, __delete__
+// This costs some code size and makes load/store/delete of instance
+// attributes slower for the classes that use this feature
 #ifndef MICROPY_PY_DESCRIPTORS
 #define MICROPY_PY_DESCRIPTORS (0)
 #endif
 
 // Whether to support class __delattr__ and __setattr__ methods
-// This costs some code size and makes all del attrs and store attrs slow
+// This costs some code size and makes store/delete of instance
+// attributes slower for the classes that use this feature
 #ifndef MICROPY_PY_DELATTR_SETATTR
 #define MICROPY_PY_DELATTR_SETATTR (0)
 #endif
@@ -742,6 +801,16 @@ typedef double mp_float_t;
 #define MICROPY_PY_BUILTINS_STR_CENTER (0)
 #endif
 
+// Whether str.count() method provided
+#ifndef MICROPY_PY_BUILTINS_STR_COUNT
+#define MICROPY_PY_BUILTINS_STR_COUNT (1)
+#endif
+
+// Whether str % (...) formatting operator provided
+#ifndef MICROPY_PY_BUILTINS_STR_OP_MODULO
+#define MICROPY_PY_BUILTINS_STR_OP_MODULO (1)
+#endif
+
 // Whether str.partition()/str.rpartition() method provided
 #ifndef MICROPY_PY_BUILTINS_STR_PARTITION
 #define MICROPY_PY_BUILTINS_STR_PARTITION (0)
@@ -757,6 +826,11 @@ typedef double mp_float_t;
 #define MICROPY_PY_BUILTINS_BYTEARRAY (1)
 #endif
 
+// Whether to support dict.fromkeys() class method
+#ifndef MICROPY_PY_BUILTINS_DICT_FROMKEYS
+#define MICROPY_PY_BUILTINS_DICT_FROMKEYS (1)
+#endif
+
 // Whether to support memoryview object
 #ifndef MICROPY_PY_BUILTINS_MEMORYVIEW
 #define MICROPY_PY_BUILTINS_MEMORYVIEW (0)
@@ -802,6 +876,11 @@ typedef double mp_float_t;
 #define MICROPY_PY_BUILTINS_RANGE_BINOP (0)
 #endif
 
+// Whether to support rounding of integers (incl bignum); eg round(123,-1)=120
+#ifndef MICROPY_PY_BUILTINS_ROUND_INT
+#define MICROPY_PY_BUILTINS_ROUND_INT (0)
+#endif
+
 // Whether to support timeout exceptions (like socket.timeout)
 #ifndef MICROPY_PY_BUILTINS_TIMEOUTERROR
 #define MICROPY_PY_BUILTINS_TIMEOUTERROR (0)
@@ -961,6 +1040,11 @@ typedef double mp_float_t;
 #define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (0)
 #endif
 
+// Whether to provide math.factorial function
+#ifndef MICROPY_PY_MATH_FACTORIAL
+#define MICROPY_PY_MATH_FACTORIAL (0)
+#endif
+
 // Whether to provide "cmath" module
 #ifndef MICROPY_PY_CMATH
 #define MICROPY_PY_CMATH (0)
@@ -981,6 +1065,11 @@ typedef double mp_float_t;
 #define MICROPY_PY_IO (1)
 #endif
 
+// Whether to provide "io.IOBase" class to support user streams
+#ifndef MICROPY_PY_IO_IOBASE
+#define MICROPY_PY_IO_IOBASE (0)
+#endif
+
 // Whether to provide "uio.resource_stream()" function with
 // the semantics of CPython's pkg_resources.resource_stream()
 // (allows to access binary resources in frozen source packages).
@@ -1108,6 +1197,12 @@ typedef double mp_float_t;
 #define MICROPY_PY_UCTYPES (0)
 #endif
 
+// Whether to provide SHORT, INT, LONG, etc. types in addition to
+// exact-bitness types like INT16, INT32, etc.
+#ifndef MICROPY_PY_UCTYPES_NATIVE_C_TYPES
+#define MICROPY_PY_UCTYPES_NATIVE_C_TYPES (1)
+#endif
+
 #ifndef MICROPY_PY_UZLIB
 #define MICROPY_PY_UZLIB (0)
 #endif
@@ -1120,6 +1215,18 @@ typedef double mp_float_t;
 #define MICROPY_PY_URE (0)
 #endif
 
+#ifndef MICROPY_PY_URE_MATCH_GROUPS
+#define MICROPY_PY_URE_MATCH_GROUPS (0)
+#endif
+
+#ifndef MICROPY_PY_URE_MATCH_SPAN_START_END
+#define MICROPY_PY_URE_MATCH_SPAN_START_END (0)
+#endif
+
+#ifndef MICROPY_PY_URE_SUB
+#define MICROPY_PY_URE_SUB (0)
+#endif
+
 #ifndef MICROPY_PY_UHEAPQ
 #define MICROPY_PY_UHEAPQ (0)
 #endif
@@ -1133,6 +1240,26 @@ typedef double mp_float_t;
 #define MICROPY_PY_UHASHLIB (0)
 #endif
 
+#ifndef MICROPY_PY_UHASHLIB_MD5
+#define MICROPY_PY_UHASHLIB_MD5 (0)
+#endif
+
+#ifndef MICROPY_PY_UHASHLIB_SHA1
+#define MICROPY_PY_UHASHLIB_SHA1  (0)
+#endif
+
+#ifndef MICROPY_PY_UHASHLIB_SHA256
+#define MICROPY_PY_UHASHLIB_SHA256 (1)
+#endif
+
+#ifndef MICROPY_PY_UCRYPTOLIB
+#define MICROPY_PY_UCRYPTOLIB (0)
+#endif
+
+#ifndef MICROPY_PY_UCRYPTOLIB_CONSTS
+#define MICROPY_PY_UCRYPTOLIB_CONSTS (0)
+#endif
+
 #ifndef MICROPY_PY_UBINASCII
 #define MICROPY_PY_UBINASCII (0)
 #endif
@@ -1256,29 +1383,26 @@ typedef double mp_float_t;
 #elif defined(MP_ENDIANNESS_BIG)
 #define MP_ENDIANNESS_LITTLE (!MP_ENDIANNESS_BIG)
 #else
-  // Endiannes not defined by port so try to autodetect it.
+  // Endianness not defined by port so try to autodetect it.
   #if defined(__BYTE_ORDER__)
     #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
       #define MP_ENDIANNESS_LITTLE (1)
-    #else
+    #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
       #define MP_ENDIANNESS_LITTLE (0)
     #endif
-  #elif defined(__LITTLE_ENDIAN__) || defined(__LITTLE_ENDIAN) || defined (_LITTLE_ENDIAN)
-    #define MP_ENDIANNESS_LITTLE (1)
-  #elif defined(__BIG_ENDIAN__) || defined(__BIG_ENDIAN) || defined (_BIG_ENDIAN)
-    #define MP_ENDIANNESS_LITTLE (0)
   #else
     #include <endian.h>
       #if defined(__BYTE_ORDER)
         #if __BYTE_ORDER == __LITTLE_ENDIAN
           #define MP_ENDIANNESS_LITTLE (1)
-        #else
+        #elif __BYTE_ORDER == __BIG_ENDIAN
           #define MP_ENDIANNESS_LITTLE (0)
         #endif
-      #else
-        #error endianness not defined and cannot detect it
       #endif
   #endif
+  #ifndef MP_ENDIANNESS_LITTLE
+    #error endianness not defined and cannot detect it
+  #endif
   #define MP_ENDIANNESS_BIG (!MP_ENDIANNESS_LITTLE)
 #endif
 

+ 1 - 1
py/mperrno.h

@@ -122,7 +122,7 @@
 #define MP_EPIPE            EPIPE
 #define MP_EDOM             EDOM
 #define MP_ERANGE           ERANGE
-#define MP_EWOULDBLOCK      EAGAIN
+#define MP_EWOULDBLOCK      EWOULDBLOCK
 #define MP_EOPNOTSUPP       EOPNOTSUPP
 #define MP_EAFNOSUPPORT     EAFNOSUPPORT
 #define MP_EADDRINUSE       EADDRINUSE

+ 24 - 15
py/mpstate.h

@@ -77,7 +77,7 @@ typedef struct _mp_state_mem_t {
     byte *gc_pool_end;
 
     int gc_stack_overflow;
-    size_t gc_stack[MICROPY_ALLOC_GC_STACK_SIZE];
+    MICROPY_GC_STACK_ENTRY_TYPE gc_stack[MICROPY_ALLOC_GC_STACK_SIZE];
     uint16_t gc_lock_depth;
 
     // This variable controls auto garbage collection.  If set to 0 then the
@@ -105,10 +105,11 @@ typedef struct _mp_state_mem_t {
 // This structure hold runtime and VM information.  It includes a section
 // which contains root pointers that must be scanned by the GC.
 typedef struct _mp_state_vm_t {
-    ////////////////////////////////////////////////////////////
-    // START ROOT POINTER SECTION
-    // everything that needs GC scanning must go here
-    // this must start at the start of this structure
+    //
+    // CONTINUE ROOT POINTER SECTION
+    // This must start at the start of this structure and follows
+    // the state in the mp_state_thread_t structure, continuing
+    // the root pointer section from there.
     //
 
     qstr_pool_t *last_pool;
@@ -139,8 +140,6 @@ typedef struct _mp_state_vm_t {
     volatile mp_obj_t mp_pending_exception;
 
     #if MICROPY_ENABLE_SCHEDULER
-    volatile int16_t sched_state;
-    uint16_t sched_sp;
     mp_sched_item_t sched_stack[MICROPY_SCHEDULER_DEPTH];
     #endif
 
@@ -172,7 +171,6 @@ typedef struct _mp_state_vm_t {
 
     #if MICROPY_PY_OS_DUPTERM
     mp_obj_t dupterm_objs[MICROPY_PY_OS_DUPTERM];
-    mp_obj_t dupterm_arr_obj;
     #endif
 
     #if MICROPY_PY_LWIP_SLIP
@@ -199,13 +197,20 @@ typedef struct _mp_state_vm_t {
     mp_thread_mutex_t qstr_mutex;
     #endif
 
+    #if MICROPY_ENABLE_COMPILER
     mp_uint_t mp_optimise_value;
+    #endif
 
     // size of the emergency exception buf, if it's dynamically allocated
     #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF && MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE == 0
     mp_int_t mp_emergency_exception_buf_size;
     #endif
 
+    #if MICROPY_ENABLE_SCHEDULER
+    volatile int16_t sched_state;
+    uint16_t sched_sp;
+    #endif
+
     #if MICROPY_PY_THREAD_GIL
     // This is a global mutex used to make the VM/runtime thread-safe.
     mp_thread_mutex_t gil_mutex;
@@ -215,11 +220,6 @@ typedef struct _mp_state_vm_t {
 // This structure holds state that is specific to a given thread.
 // Everything in this structure is scanned for root pointers.
 typedef struct _mp_state_thread_t {
-    mp_obj_dict_t *dict_locals;
-    mp_obj_dict_t *dict_globals;
-
-    nlr_buf_t *nlr_top; // ROOT POINTER
-
     // Stack top at the start of program
     char *stack_top;
 
@@ -232,12 +232,21 @@ typedef struct _mp_state_thread_t {
     uint8_t *pystack_end;
     uint8_t *pystack_cur;
     #endif
+
+    ////////////////////////////////////////////////////////////
+    // START ROOT POINTER SECTION
+    // Everything that needs GC scanning must start here, and
+    // is followed by state in the mp_state_vm_t structure.
+    //
+
+    mp_obj_dict_t *dict_locals;
+    mp_obj_dict_t *dict_globals;
+
+    nlr_buf_t *nlr_top;
 } mp_state_thread_t;
 
 // This structure combines the above 3 structures.
 // The order of the entries are important for root pointer scanning in the GC to work.
-// Note: if this structure changes then revisit all nlr asm code since they
-// have the offset of nlr_top hard-coded.
 typedef struct _mp_state_ctx_t {
     mp_state_thread_t thread;
     mp_state_vm_t vm;

+ 1 - 1
py/mpz.c

@@ -1532,7 +1532,7 @@ mpz_t *mpz_mod(const mpz_t *lhs, const mpz_t *rhs) {
 
 // must return actual int value if it fits in mp_int_t
 mp_int_t mpz_hash(const mpz_t *z) {
-    mp_int_t val = 0;
+    mp_uint_t val = 0;
     mpz_dig_t *d = z->dig + z->len;
 
     while (d-- > z->dig) {

+ 59 - 3
py/nativeglue.c

@@ -83,6 +83,20 @@ mp_obj_t mp_convert_native_to_obj(mp_uint_t val, mp_uint_t type) {
 
 #if MICROPY_EMIT_NATIVE
 
+mp_obj_dict_t *mp_native_swap_globals(mp_obj_dict_t *new_globals) {
+    if (new_globals == NULL) {
+        // Globals were the originally the same so don't restore them
+        return NULL;
+    }
+    mp_obj_dict_t *old_globals = mp_globals_get();
+    if (old_globals == new_globals) {
+        // Don't set globals if they are the same, and return NULL to indicate this
+        return NULL;
+    }
+    mp_globals_set(new_globals);
+    return old_globals;
+}
+
 // wrapper that accepts n_args and n_kw in one argument
 // (native emitter can only pass at most 3 arguments to a function)
 mp_obj_t mp_native_call_function_n_kw(mp_obj_t fun_in, size_t n_args_kw, const mp_obj_t *args) {
@@ -92,7 +106,7 @@ mp_obj_t mp_native_call_function_n_kw(mp_obj_t fun_in, size_t n_args_kw, const m
 // wrapper that makes raise obj and raises it
 // END_FINALLY opcode requires that we don't raise if o==None
 void mp_native_raise(mp_obj_t o) {
-    if (o != mp_const_none) {
+    if (o != MP_OBJ_NULL && o != mp_const_none) {
         nlr_raise(mp_make_raise_obj(o));
     }
 }
@@ -123,10 +137,50 @@ STATIC mp_obj_t mp_native_iternext(mp_obj_iter_buf_t *iter) {
     return mp_iternext(obj);
 }
 
+STATIC bool mp_native_yield_from(mp_obj_t gen, mp_obj_t send_value, mp_obj_t *ret_value) {
+    mp_vm_return_kind_t ret_kind;
+    nlr_buf_t nlr_buf;
+    mp_obj_t throw_value = *ret_value;
+    if (nlr_push(&nlr_buf) == 0) {
+        if (throw_value != MP_OBJ_NULL) {
+            send_value = MP_OBJ_NULL;
+        }
+        ret_kind = mp_resume(gen, send_value, throw_value, ret_value);
+        nlr_pop();
+    } else {
+        ret_kind = MP_VM_RETURN_EXCEPTION;
+        *ret_value = nlr_buf.ret_val;
+    }
+
+    if (ret_kind == MP_VM_RETURN_YIELD) {
+        return true;
+    } else if (ret_kind == MP_VM_RETURN_NORMAL) {
+        if (*ret_value == MP_OBJ_STOP_ITERATION) {
+            *ret_value = mp_const_none;
+        }
+    } else {
+        assert(ret_kind == MP_VM_RETURN_EXCEPTION);
+        if (!mp_obj_exception_match(*ret_value, MP_OBJ_FROM_PTR(&mp_type_StopIteration))) {
+            nlr_raise(*ret_value);
+        }
+        *ret_value = mp_obj_exception_get_value(*ret_value);
+    }
+
+    if (throw_value != MP_OBJ_NULL && mp_obj_exception_match(throw_value, MP_OBJ_FROM_PTR(&mp_type_GeneratorExit))) {
+        nlr_raise(mp_make_raise_obj(throw_value));
+    }
+
+    return false;
+}
+
 // these must correspond to the respective enum in runtime0.h
-void *const mp_fun_table[MP_F_NUMBER_OF] = {
+const void *const mp_fun_table[MP_F_NUMBER_OF] = {
+    &mp_const_none_obj,
+    &mp_const_false_obj,
+    &mp_const_true_obj,
     mp_convert_obj_to_native,
     mp_convert_native_to_obj,
+    mp_native_swap_globals,
     mp_load_name,
     mp_load_global,
     mp_load_build_class,
@@ -146,8 +200,8 @@ void *const mp_fun_table[MP_F_NUMBER_OF] = {
     mp_obj_new_dict,
     mp_obj_dict_store,
 #if MICROPY_PY_BUILTINS_SET
-    mp_obj_new_set,
     mp_obj_set_store,
+    mp_obj_new_set,
 #endif
     mp_make_function_from_raw_code,
     mp_native_call_function_n_kw,
@@ -170,9 +224,11 @@ void *const mp_fun_table[MP_F_NUMBER_OF] = {
     mp_delete_global,
     mp_obj_new_cell,
     mp_make_closure_from_raw_code,
+    mp_arg_check_num_sig,
     mp_setup_code_state,
     mp_small_int_floor_divide,
     mp_small_int_modulo,
+    mp_native_yield_from,
 };
 
 /*

+ 2 - 1
py/nlrthumb.c

@@ -76,8 +76,9 @@ __attribute__((naked)) unsigned int nlr_push(nlr_buf_t *nlr) {
 #endif
     );
 
-    #if defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8))
+    #if !defined(__clang__) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8))
     // Older versions of gcc give an error when naked functions don't return a value
+    // Additionally exclude Clang as it also defines __GNUC__ but doesn't need this statement
     return 0;
     #endif
 }

+ 21 - 5
py/nlrx86.c

@@ -39,15 +39,29 @@ unsigned int nlr_push_tail(nlr_buf_t *nlr) asm("nlr_push_tail");
 __attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr);
 #endif
 
+#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ >= 8
+// Since gcc 8.0 the naked attribute is supported
+#define USE_NAKED (1)
+#define UNDO_PRELUDE (0)
+#elif defined(__ZEPHYR__) || defined(__ANDROID__)
+// Zephyr and Android use a different calling convention by default
+#define USE_NAKED (0)
+#define UNDO_PRELUDE (0)
+#else
+#define USE_NAKED (0)
+#define UNDO_PRELUDE (1)
+#endif
+
+#if USE_NAKED
+__attribute__((naked))
+#endif
 unsigned int nlr_push(nlr_buf_t *nlr) {
+    #if !USE_NAKED
     (void)nlr;
+    #endif
 
     __asm volatile (
-    // Check for Zephyr, which uses a different calling convention
-    // by default.
-    // TODE: Better support for various x86 calling conventions
-    // (unfortunately, __attribute__((naked)) is not supported on x86).
-    #if !(defined(__ZEPHYR__) || defined(__ANDROID__))
+    #if UNDO_PRELUDE
     "pop    %ebp                \n" // undo function's prelude
     #endif
     "mov    4(%esp), %edx       \n" // load nlr_buf
@@ -61,7 +75,9 @@ unsigned int nlr_push(nlr_buf_t *nlr) {
     "jmp    nlr_push_tail       \n" // do the rest in C
     );
 
+    #if !USE_NAKED
     return 0; // needed to silence compiler warning
+    #endif
 }
 
 NORETURN void nlr_jump(void *val) {

+ 9 - 13
py/obj.c

@@ -235,12 +235,8 @@ mp_int_t mp_obj_get_int(mp_const_obj_t arg) {
     } else if (MP_OBJ_IS_TYPE(arg, &mp_type_int)) {
         return mp_obj_int_get_checked(arg);
     } else {
-        if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) {
-            mp_raise_TypeError("can't convert to int");
-        } else {
-            nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
-                "can't convert %s to int", mp_obj_get_type_str(arg)));
-        }
+        mp_obj_t res = mp_unary_op(MP_UNARY_OP_INT, (mp_obj_t)arg);
+        return mp_obj_int_get_checked(res);
     }
 }
 
@@ -353,7 +349,7 @@ void mp_obj_get_array(mp_obj_t o, size_t *len, mp_obj_t **items) {
             mp_raise_TypeError("expected tuple/list");
         } else {
             nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
-                "object '%s' is not a tuple or list", mp_obj_get_type_str(o)));
+                "object '%s' isn't a tuple or list", mp_obj_get_type_str(o)));
         }
     }
 }
@@ -475,24 +471,24 @@ mp_obj_t mp_obj_subscr(mp_obj_t base, mp_obj_t index, mp_obj_t value) {
     }
     if (value == MP_OBJ_NULL) {
         if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) {
-            mp_raise_TypeError("object does not support item deletion");
+            mp_raise_TypeError("object doesn't support item deletion");
         } else {
             nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
-                "'%s' object does not support item deletion", mp_obj_get_type_str(base)));
+                "'%s' object doesn't support item deletion", mp_obj_get_type_str(base)));
         }
     } else if (value == MP_OBJ_SENTINEL) {
         if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) {
-            mp_raise_TypeError("object is not subscriptable");
+            mp_raise_TypeError("object isn't subscriptable");
         } else {
             nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
-                "'%s' object is not subscriptable", mp_obj_get_type_str(base)));
+                "'%s' object isn't subscriptable", mp_obj_get_type_str(base)));
         }
     } else {
         if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) {
-            mp_raise_TypeError("object does not support item assignment");
+            mp_raise_TypeError("object doesn't support item assignment");
         } else {
             nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
-                "'%s' object does not support item assignment", mp_obj_get_type_str(base)));
+                "'%s' object doesn't support item assignment", mp_obj_get_type_str(base)));
         }
     }
 }

+ 31 - 24
py/obj.h

@@ -138,6 +138,7 @@ static inline bool MP_OBJ_IS_SMALL_INT(mp_const_obj_t o)
 #define MP_OBJ_SMALL_INT_VALUE(o) (((mp_int_t)(o)) >> 1)
 #define MP_OBJ_NEW_SMALL_INT(small_int) ((mp_obj_t)((((mp_uint_t)(small_int)) << 1) | 1))
 
+#if MICROPY_PY_BUILTINS_FLOAT
 #define mp_const_float_e MP_ROM_PTR((mp_obj_t)(((0x402df854 & ~3) | 2) + 0x80800000))
 #define mp_const_float_pi MP_ROM_PTR((mp_obj_t)(((0x40490fdb & ~3) | 2) + 0x80800000))
 
@@ -157,6 +158,7 @@ static inline mp_obj_t mp_obj_new_float(mp_float_t f) {
     } num = {.f = f};
     return (mp_obj_t)(((num.u & ~0x3) | 2) + 0x80800000);
 }
+#endif
 
 static inline bool MP_OBJ_IS_QSTR(mp_const_obj_t o)
     { return (((mp_uint_t)(o)) & 0xff800007) == 0x00000006; }
@@ -169,17 +171,22 @@ static inline bool MP_OBJ_IS_OBJ(mp_const_obj_t o)
 #elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D
 
 static inline bool MP_OBJ_IS_SMALL_INT(mp_const_obj_t o)
-    { return ((((mp_int_t)(o)) & 0xffff000000000000) == 0x0001000000000000); }
+    { return ((((uint64_t)(o)) & 0xffff000000000000) == 0x0001000000000000); }
 #define MP_OBJ_SMALL_INT_VALUE(o) (((mp_int_t)((o) << 16)) >> 17)
 #define MP_OBJ_NEW_SMALL_INT(small_int) (((((uint64_t)(small_int)) & 0x7fffffffffff) << 1) | 0x0001000000000001)
 
 static inline bool MP_OBJ_IS_QSTR(mp_const_obj_t o)
-    { return ((((mp_int_t)(o)) & 0xffff000000000000) == 0x0002000000000000); }
+    { return ((((uint64_t)(o)) & 0xffff000000000000) == 0x0002000000000000); }
 #define MP_OBJ_QSTR_VALUE(o) ((((uint32_t)(o)) >> 1) & 0xffffffff)
 #define MP_OBJ_NEW_QSTR(qst) ((mp_obj_t)((((mp_uint_t)(qst)) << 1) | 0x0002000000000001))
 
 #if MICROPY_PY_BUILTINS_FLOAT
-#define mp_const_float_e {((mp_obj_t)((uint64_t)0x4005bf0a8b125769 + 0x8004000000000000))}
+
+#if MICROPY_FLOAT_IMPL != MICROPY_FLOAT_IMPL_DOUBLE
+#error MICROPY_OBJ_REPR_D requires MICROPY_FLOAT_IMPL_DOUBLE
+#endif
+
+#define mp_const_float_e {((mp_obj_t)((uint64_t)0x4005bf0a8b145769 + 0x8004000000000000))}
 #define mp_const_float_pi {((mp_obj_t)((uint64_t)0x400921fb54442d18 + 0x8004000000000000))}
 
 static inline bool mp_obj_is_float(mp_const_obj_t o) {
@@ -270,6 +277,9 @@ typedef struct _mp_rom_obj_t { mp_const_obj_t o; } mp_rom_obj_t;
 #define MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(obj_name) extern const mp_obj_fun_builtin_var_t obj_name
 #define MP_DECLARE_CONST_FUN_OBJ_KW(obj_name) extern const mp_obj_fun_builtin_var_t obj_name
 
+#define MP_OBJ_FUN_ARGS_MAX (0xffff) // to set maximum value in n_args_max below
+#define MP_OBJ_FUN_MAKE_SIG(n_args_min, n_args_max, takes_kw) (((n_args_min) << 17) | ((n_args_max) << 1) | ((takes_kw) ? 1 : 0))
+
 #define MP_DEFINE_CONST_FUN_OBJ_0(obj_name, fun_name) \
     const mp_obj_fun_builtin_fixed_t obj_name = \
         {{&mp_type_fun_builtin_0}, .fun._0 = fun_name}
@@ -284,13 +294,13 @@ typedef struct _mp_rom_obj_t { mp_const_obj_t o; } mp_rom_obj_t;
         {{&mp_type_fun_builtin_3}, .fun._3 = fun_name}
 #define MP_DEFINE_CONST_FUN_OBJ_VAR(obj_name, n_args_min, fun_name) \
     const mp_obj_fun_builtin_var_t obj_name = \
-        {{&mp_type_fun_builtin_var}, false, n_args_min, MP_OBJ_FUN_ARGS_MAX, .fun.var = fun_name}
+        {{&mp_type_fun_builtin_var}, MP_OBJ_FUN_MAKE_SIG(n_args_min, MP_OBJ_FUN_ARGS_MAX, false), .fun.var = fun_name}
 #define MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(obj_name, n_args_min, n_args_max, fun_name) \
     const mp_obj_fun_builtin_var_t obj_name = \
-        {{&mp_type_fun_builtin_var}, false, n_args_min, n_args_max, .fun.var = fun_name}
+        {{&mp_type_fun_builtin_var}, MP_OBJ_FUN_MAKE_SIG(n_args_min, n_args_max, false), .fun.var = fun_name}
 #define MP_DEFINE_CONST_FUN_OBJ_KW(obj_name, n_args_min, fun_name) \
     const mp_obj_fun_builtin_var_t obj_name = \
-        {{&mp_type_fun_builtin_var}, true, n_args_min, MP_OBJ_FUN_ARGS_MAX, .fun.kw = fun_name}
+        {{&mp_type_fun_builtin_var}, MP_OBJ_FUN_MAKE_SIG(n_args_min, MP_OBJ_FUN_ARGS_MAX, true), .fun.kw = fun_name}
 
 // These macros are used to define constant map/dict objects
 // You can put "static" in front of the definition to make it local
@@ -451,22 +461,15 @@ typedef struct _mp_buffer_p_t {
 bool mp_get_buffer(mp_obj_t obj, mp_buffer_info_t *bufinfo, mp_uint_t flags);
 void mp_get_buffer_raise(mp_obj_t obj, mp_buffer_info_t *bufinfo, mp_uint_t flags);
 
-// Stream protocol
-typedef struct _mp_stream_p_t {
-    // On error, functions should return MP_STREAM_ERROR and fill in *errcode (values
-    // are implementation-dependent, but will be exposed to user, e.g. via exception).
-    mp_uint_t (*read)(mp_obj_t obj, void *buf, mp_uint_t size, int *errcode);
-    mp_uint_t (*write)(mp_obj_t obj, const void *buf, mp_uint_t size, int *errcode);
-    mp_uint_t (*ioctl)(mp_obj_t obj, mp_uint_t request, uintptr_t arg, int *errcode);
-    mp_uint_t is_text : 1; // default is bytes, set this for text stream
-} mp_stream_p_t;
-
 struct _mp_obj_type_t {
     // A type is an object so must start with this entry, which points to mp_type_type.
     mp_obj_base_t base;
 
-    // The name of this type.
-    qstr name;
+    // Flags associated with this type.
+    uint16_t flags;
+
+    // The name of this type, a qstr.
+    uint16_t name;
 
     // Corresponds to __repr__ and __str__ special methods.
     mp_print_fun_t print;
@@ -554,6 +557,8 @@ extern const mp_obj_type_t mp_type_slice;
 extern const mp_obj_type_t mp_type_zip;
 extern const mp_obj_type_t mp_type_array;
 extern const mp_obj_type_t mp_type_super;
+extern const mp_obj_type_t mp_type_gen_wrap;
+extern const mp_obj_type_t mp_type_native_gen_wrap;
 extern const mp_obj_type_t mp_type_gen_instance;
 extern const mp_obj_type_t mp_type_fun_builtin_0;
 extern const mp_obj_type_t mp_type_fun_builtin_1;
@@ -720,6 +725,7 @@ qstr mp_obj_str_get_qstr(mp_obj_t self_in); // use this if you will anyway conve
 const char *mp_obj_str_get_str(mp_obj_t self_in); // use this only if you need the string to be null terminated
 const char *mp_obj_str_get_data(mp_obj_t self_in, size_t *len);
 mp_obj_t mp_obj_str_intern(mp_obj_t str);
+mp_obj_t mp_obj_str_intern_checked(mp_obj_t obj);
 void mp_str_print_quoted(const mp_print_t *print, const byte *str_data, size_t str_len, bool is_bytes);
 
 #if MICROPY_PY_BUILTINS_FLOAT
@@ -761,7 +767,9 @@ size_t mp_obj_dict_len(mp_obj_t self_in);
 mp_obj_t mp_obj_dict_get(mp_obj_t self_in, mp_obj_t index);
 mp_obj_t mp_obj_dict_store(mp_obj_t self_in, mp_obj_t key, mp_obj_t value);
 mp_obj_t mp_obj_dict_delete(mp_obj_t self_in, mp_obj_t key);
-mp_map_t *mp_obj_dict_get_map(mp_obj_t self_in);
+static inline mp_map_t *mp_obj_dict_get_map(mp_obj_t dict) {
+    return &((mp_obj_dict_t*)MP_OBJ_TO_PTR(dict))->map;
+}
 
 // set
 void mp_obj_set_store(mp_obj_t self_in, mp_obj_t item);
@@ -781,12 +789,9 @@ typedef struct _mp_obj_fun_builtin_fixed_t {
     } fun;
 } mp_obj_fun_builtin_fixed_t;
 
-#define MP_OBJ_FUN_ARGS_MAX (0xffff) // to set maximum value in n_args_max below
 typedef struct _mp_obj_fun_builtin_var_t {
     mp_obj_base_t base;
-    bool is_kw : 1;
-    mp_uint_t n_args_min : 15; // inclusive
-    mp_uint_t n_args_max : 16; // inclusive
+    uint32_t sig; // see MP_OBJ_FUN_MAKE_SIG
     union {
         mp_fun_var_t var;
         mp_fun_kw_t kw;
@@ -805,7 +810,9 @@ typedef struct _mp_obj_module_t {
     mp_obj_base_t base;
     mp_obj_dict_t *globals;
 } mp_obj_module_t;
-mp_obj_dict_t *mp_obj_module_get_globals(mp_obj_t self_in);
+static inline mp_obj_dict_t *mp_obj_module_get_globals(mp_obj_t module) {
+    return ((mp_obj_module_t*)MP_OBJ_TO_PTR(module))->globals;
+}
 // check if given module object is a package
 bool mp_obj_is_package(mp_obj_t module);
 

+ 20 - 15
py/objarray.c

@@ -50,11 +50,14 @@
 // Note that we don't handle the case where the original buffer might change
 // size due to a resize of the original parent object.
 
-// make (& TYPECODE_MASK) a null operation if memorview not enabled
 #if MICROPY_PY_BUILTINS_MEMORYVIEW
 #define TYPECODE_MASK (0x7f)
+#define memview_offset free
 #else
+// make (& TYPECODE_MASK) a null operation if memorview not enabled
 #define TYPECODE_MASK (~(size_t)0)
+// memview_offset should not be accessed if memoryview is not enabled,
+// so not defined to catch errors
 #endif
 
 STATIC mp_obj_t array_iterator_new(mp_obj_t array_in, mp_obj_iter_buf_t *iter_buf);
@@ -175,7 +178,8 @@ STATIC mp_obj_t array_make_new(const mp_obj_type_t *type_in, size_t n_args, size
 #if MICROPY_PY_BUILTINS_BYTEARRAY
 STATIC mp_obj_t bytearray_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
     (void)type_in;
-    mp_arg_check_num(n_args, n_kw, 0, 1, false);
+    // Can take 2nd/3rd arg if constructs from str
+    mp_arg_check_num(n_args, n_kw, 0, 3, false);
 
     if (n_args == 0) {
         // no args: construct an empty bytearray
@@ -199,7 +203,7 @@ mp_obj_t mp_obj_new_memoryview(byte typecode, size_t nitems, void *items) {
     mp_obj_array_t *self = m_new_obj(mp_obj_array_t);
     self->base.type = &mp_type_memoryview;
     self->typecode = typecode;
-    self->free = 0;
+    self->memview_offset = 0;
     self->len = nitems;
     self->items = items;
     return MP_OBJ_FROM_PTR(self);
@@ -222,7 +226,7 @@ STATIC mp_obj_t memoryview_make_new(const mp_obj_type_t *type_in, size_t n_args,
 
     // test if the object can be written to
     if (mp_get_buffer(args[0], &bufinfo, MP_BUFFER_RW)) {
-        self->typecode |= 0x80; // used to indicate writable buffer
+        self->typecode |= MP_OBJ_ARRAY_TYPECODE_FLAG_RW; // indicate writable buffer
     }
 
     return MP_OBJ_FROM_PTR(self);
@@ -270,10 +274,10 @@ STATIC mp_obj_t array_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs
         }
 
         case MP_BINARY_OP_CONTAINS: {
+            #if MICROPY_PY_BUILTINS_BYTEARRAY
+            // Can search string only in bytearray
             mp_buffer_info_t lhs_bufinfo;
             mp_buffer_info_t rhs_bufinfo;
-
-            // Can search string only in bytearray
             if (mp_get_buffer(rhs_in, &rhs_bufinfo, MP_BUFFER_READ)) {
                 if (!MP_OBJ_IS_TYPE(lhs_in, &mp_type_bytearray)) {
                     return mp_const_false;
@@ -282,6 +286,7 @@ STATIC mp_obj_t array_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs
                 return mp_obj_new_bool(
                     find_subbytes(lhs_bufinfo.buf, lhs_bufinfo.len, rhs_bufinfo.buf, rhs_bufinfo.len, 1) != NULL);
             }
+            #endif
 
             // Otherwise, can only look for a scalar numeric value in an array
             if (MP_OBJ_IS_INT(rhs_in) || mp_obj_is_float(rhs_in)) {
@@ -394,7 +399,7 @@ STATIC mp_obj_t array_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value
                     src_items = src_slice->items;
                     #if MICROPY_PY_BUILTINS_MEMORYVIEW
                     if (MP_OBJ_IS_TYPE(value, &mp_type_memoryview)) {
-                        src_items = (uint8_t*)src_items + (src_slice->free * item_sz);
+                        src_items = (uint8_t*)src_items + (src_slice->memview_offset * item_sz);
                     }
                     #endif
                 } else if (MP_OBJ_IS_TYPE(value, &mp_type_bytes)) {
@@ -414,14 +419,14 @@ STATIC mp_obj_t array_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value
                 uint8_t* dest_items = o->items;
                 #if MICROPY_PY_BUILTINS_MEMORYVIEW
                 if (o->base.type == &mp_type_memoryview) {
-                    if ((o->typecode & 0x80) == 0) {
+                    if (!(o->typecode & MP_OBJ_ARRAY_TYPECODE_FLAG_RW)) {
                         // store to read-only memoryview not allowed
                         return MP_OBJ_NULL;
                     }
                     if (len_adj != 0) {
                         goto compat_error;
                     }
-                    dest_items += o->free * item_sz;
+                    dest_items += o->memview_offset * item_sz;
                 }
                 #endif
                 if (len_adj > 0) {
@@ -457,7 +462,7 @@ STATIC mp_obj_t array_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value
             } else if (o->base.type == &mp_type_memoryview) {
                 res = m_new_obj(mp_obj_array_t);
                 *res = *o;
-                res->free += slice.start;
+                res->memview_offset += slice.start;
                 res->len = slice.stop - slice.start;
             #endif
             } else {
@@ -470,8 +475,8 @@ STATIC mp_obj_t array_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value
             size_t index = mp_get_index(o->base.type, o->len, index_in, false);
             #if MICROPY_PY_BUILTINS_MEMORYVIEW
             if (o->base.type == &mp_type_memoryview) {
-                index += o->free;
-                if (value != MP_OBJ_SENTINEL && (o->typecode & 0x80) == 0) {
+                index += o->memview_offset;
+                if (value != MP_OBJ_SENTINEL && !(o->typecode & MP_OBJ_ARRAY_TYPECODE_FLAG_RW)) {
                     // store to read-only memoryview
                     return MP_OBJ_NULL;
                 }
@@ -497,11 +502,11 @@ STATIC mp_int_t array_get_buffer(mp_obj_t o_in, mp_buffer_info_t *bufinfo, mp_ui
     bufinfo->typecode = o->typecode & TYPECODE_MASK;
     #if MICROPY_PY_BUILTINS_MEMORYVIEW
     if (o->base.type == &mp_type_memoryview) {
-        if ((o->typecode & 0x80) == 0 && (flags & MP_BUFFER_WRITE)) {
+        if (!(o->typecode & MP_OBJ_ARRAY_TYPECODE_FLAG_RW) && (flags & MP_BUFFER_WRITE)) {
             // read-only memoryview
             return 1;
         }
-        bufinfo->buf = (uint8_t*)bufinfo->buf + (size_t)o->free * sz;
+        bufinfo->buf = (uint8_t*)bufinfo->buf + (size_t)o->memview_offset * sz;
     }
     #else
     (void)flags;
@@ -622,7 +627,7 @@ STATIC mp_obj_t array_iterator_new(mp_obj_t array_in, mp_obj_iter_buf_t *iter_bu
     o->cur = 0;
     #if MICROPY_PY_BUILTINS_MEMORYVIEW
     if (array->base.type == &mp_type_memoryview) {
-        o->offset = array->free;
+        o->offset = array->memview_offset;
     }
     #endif
     return MP_OBJ_FROM_PTR(o);

+ 10 - 0
py/objarray.h

@@ -29,11 +29,21 @@
 
 #include "py/obj.h"
 
+// Used only for memoryview types, set in "typecode" to indicate a writable memoryview
+#define MP_OBJ_ARRAY_TYPECODE_FLAG_RW (0x80)
+
+// This structure is used for all of bytearray, array.array, memoryview
+// objects.  Note that memoryview has different meaning for some fields,
+// see comment at the beginning of objarray.c.
 typedef struct _mp_obj_array_t {
     mp_obj_base_t base;
     size_t typecode : 8;
     // free is number of unused elements after len used elements
     // alloc size = len + free
+    // But for memoryview, 'free' is reused as offset (in elements) into the
+    // parent object. (Union is not used to not go into a complication of
+    // union-of-bitfields with different toolchains). See comments in
+    // objarray.c.
     size_t free : (8 * sizeof(size_t) - 8);
     size_t len; // in elements
     void *items;

+ 3 - 4
py/objboundmeth.c

@@ -89,10 +89,9 @@ STATIC void bound_meth_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
         // not load attribute
         return;
     }
-    if (attr == MP_QSTR___name__) {
-        mp_obj_bound_meth_t *o = MP_OBJ_TO_PTR(self_in);
-        dest[0] = MP_OBJ_NEW_QSTR(mp_obj_fun_get_name(o->meth));
-    }
+    // Delegate the load to the method object
+    mp_obj_bound_meth_t *self = MP_OBJ_TO_PTR(self_in);
+    mp_load_method_maybe(self->meth, attr, dest);
 }
 #endif
 

+ 2 - 2
py/objcomplex.c

@@ -195,13 +195,13 @@ mp_obj_t mp_obj_complex_binary_op(mp_binary_op_t op, mp_float_t lhs_real, mp_flo
         }
         case MP_BINARY_OP_FLOOR_DIVIDE:
         case MP_BINARY_OP_INPLACE_FLOOR_DIVIDE:
-            mp_raise_TypeError("can't do truncated division of a complex number");
+            mp_raise_TypeError("can't truncate-divide a complex number");
 
         case MP_BINARY_OP_TRUE_DIVIDE:
         case MP_BINARY_OP_INPLACE_TRUE_DIVIDE:
             if (rhs_imag == 0) {
                 if (rhs_real == 0) {
-                    mp_raise_msg(&mp_type_ZeroDivisionError, "complex division by zero");
+                    mp_raise_msg(&mp_type_ZeroDivisionError, "complex divide by zero");
                 }
                 lhs_real /= rhs_real;
                 lhs_imag /= rhs_real;

+ 2 - 1
py/objdeque.c

@@ -24,6 +24,7 @@
  * THE SOFTWARE.
  */
 
+#include <unistd.h> // for ssize_t
 #include <string.h>
 
 #include "py/mpconfig.h"
@@ -75,7 +76,7 @@ STATIC mp_obj_t deque_unary_op(mp_unary_op_t op, mp_obj_t self_in) {
         case MP_UNARY_OP_BOOL:
             return mp_obj_new_bool(self->i_get != self->i_put);
         case MP_UNARY_OP_LEN: {
-            mp_int_t len = self->i_put - self->i_get;
+            ssize_t len = self->i_put - self->i_get;
             if (len < 0) {
                 len += self->alloc;
             }

+ 5 - 7
py/objdict.c

@@ -160,7 +160,7 @@ STATIC mp_obj_t dict_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_
     }
 }
 
-// TODO: Make sure this is inlined in dict_subscr() below.
+// Note: Make sure this is inlined in load part of dict_subscr() below.
 mp_obj_t mp_obj_dict_get(mp_obj_t self_in, mp_obj_t index) {
     mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in);
     mp_map_elem_t *elem = mp_map_lookup(&self->map, index, MP_MAP_LOOKUP);
@@ -227,6 +227,7 @@ STATIC mp_obj_t dict_copy(mp_obj_t self_in) {
 }
 STATIC MP_DEFINE_CONST_FUN_OBJ_1(dict_copy_obj, dict_copy);
 
+#if MICROPY_PY_BUILTINS_DICT_FROMKEYS
 // this is a classmethod
 STATIC mp_obj_t dict_fromkeys(size_t n_args, const mp_obj_t *args) {
     mp_obj_t iter = mp_getiter(args[1], NULL);
@@ -256,6 +257,7 @@ STATIC mp_obj_t dict_fromkeys(size_t n_args, const mp_obj_t *args) {
 }
 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dict_fromkeys_fun_obj, 2, 3, dict_fromkeys);
 STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(dict_fromkeys_obj, MP_ROM_PTR(&dict_fromkeys_fun_obj));
+#endif
 
 STATIC mp_obj_t dict_get_helper(size_t n_args, const mp_obj_t *args, mp_map_lookup_kind_t lookup_kind) {
     mp_check_self(MP_OBJ_IS_DICT_TYPE(args[0]));
@@ -528,7 +530,9 @@ STATIC mp_obj_t dict_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) {
 STATIC const mp_rom_map_elem_t dict_locals_dict_table[] = {
     { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&dict_clear_obj) },
     { MP_ROM_QSTR(MP_QSTR_copy), MP_ROM_PTR(&dict_copy_obj) },
+    #if MICROPY_PY_BUILTINS_DICT_FROMKEYS
     { MP_ROM_QSTR(MP_QSTR_fromkeys), MP_ROM_PTR(&dict_fromkeys_obj) },
+    #endif
     { MP_ROM_QSTR(MP_QSTR_get), MP_ROM_PTR(&dict_get_obj) },
     { MP_ROM_QSTR(MP_QSTR_items), MP_ROM_PTR(&dict_items_obj) },
     { MP_ROM_QSTR(MP_QSTR_keys), MP_ROM_PTR(&dict_keys_obj) },
@@ -600,9 +604,3 @@ mp_obj_t mp_obj_dict_delete(mp_obj_t self_in, mp_obj_t key) {
     dict_get_helper(2, args, MP_MAP_LOOKUP_REMOVE_IF_FOUND);
     return self_in;
 }
-
-mp_map_t *mp_obj_dict_get_map(mp_obj_t self_in) {
-    mp_check_self(MP_OBJ_IS_DICT_TYPE(self_in));
-    mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in);
-    return &self->map;
-}

+ 53 - 14
py/objexcept.c

@@ -40,12 +40,23 @@
 // Number of items per traceback entry (file, line, block)
 #define TRACEBACK_ENTRY_LEN (3)
 
-// Number of traceback entries to reserve in the emergency exception buffer
-#define EMG_TRACEBACK_ALLOC (2 * TRACEBACK_ENTRY_LEN)
-
-// Optionally allocated buffer for storing the first argument of an exception
-// allocated when the heap is locked.
+// Optionally allocated buffer for storing some traceback, the tuple argument,
+// and possible string object and data, for when the heap is locked.
 #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
+
+// When used the layout of the emergency exception buffer is:
+//  - traceback entry (file, line, block)
+//  - traceback entry (file, line, block)
+//  - mp_obj_tuple_t object
+//  - n_args * mp_obj_t for tuple
+//  - mp_obj_str_t object
+//  - string data
+#define EMG_BUF_TRACEBACK_OFFSET    (0)
+#define EMG_BUF_TRACEBACK_SIZE      (2 * TRACEBACK_ENTRY_LEN * sizeof(size_t))
+#define EMG_BUF_TUPLE_OFFSET        (EMG_BUF_TRACEBACK_OFFSET + EMG_BUF_TRACEBACK_SIZE)
+#define EMG_BUF_TUPLE_SIZE(n_args)  (sizeof(mp_obj_tuple_t) + n_args * sizeof(mp_obj_t))
+#define EMG_BUF_STR_OFFSET          (EMG_BUF_TUPLE_OFFSET + EMG_BUF_TUPLE_SIZE(1))
+
 #   if MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE > 0
 #define mp_emergency_exception_buf_size MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE
 
@@ -153,9 +164,9 @@ mp_obj_t mp_obj_exception_make_new(const mp_obj_type_t *type, size_t n_args, siz
         // reserved room (after the traceback data) for a tuple with 1 element.
         // Otherwise we are free to use the whole buffer after the traceback data.
         if (o_tuple == NULL && mp_emergency_exception_buf_size >=
-            EMG_TRACEBACK_ALLOC * sizeof(size_t) + sizeof(mp_obj_tuple_t) + n_args * sizeof(mp_obj_t)) {
+            EMG_BUF_TUPLE_OFFSET + EMG_BUF_TUPLE_SIZE(n_args)) {
             o_tuple = (mp_obj_tuple_t*)
-                ((uint8_t*)MP_STATE_VM(mp_emergency_exception_buf) + EMG_TRACEBACK_ALLOC * sizeof(size_t));
+                ((uint8_t*)MP_STATE_VM(mp_emergency_exception_buf) + EMG_BUF_TUPLE_OFFSET);
         }
         #endif
 
@@ -313,7 +324,35 @@ mp_obj_t mp_obj_new_exception_args(const mp_obj_type_t *exc_type, size_t n_args,
 }
 
 mp_obj_t mp_obj_new_exception_msg(const mp_obj_type_t *exc_type, const char *msg) {
-    return mp_obj_new_exception_msg_varg(exc_type, msg);
+    // Check that the given type is an exception type
+    assert(exc_type->make_new == mp_obj_exception_make_new);
+
+    // Try to allocate memory for the message
+    mp_obj_str_t *o_str = m_new_obj_maybe(mp_obj_str_t);
+
+    #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
+    // If memory allocation failed and there is an emergency buffer then try to use
+    // that buffer to store the string object, reserving room at the start for the
+    // traceback and 1-tuple.
+    if (o_str == NULL
+        && mp_emergency_exception_buf_size >= EMG_BUF_STR_OFFSET + sizeof(mp_obj_str_t)) {
+        o_str = (mp_obj_str_t*)((uint8_t*)MP_STATE_VM(mp_emergency_exception_buf)
+            + EMG_BUF_STR_OFFSET);
+    }
+    #endif
+
+    if (o_str == NULL) {
+        // No memory for the string object so create the exception with no args
+        return mp_obj_exception_make_new(exc_type, 0, 0, NULL);
+    }
+
+    // Create the string object and call mp_obj_exception_make_new to create the exception
+    o_str->base.type = &mp_type_str;
+    o_str->hash = qstr_compute_hash(o_str->data, o_str->len);
+    o_str->len = strlen(msg);
+    o_str->data = (const byte*)msg;
+    mp_obj_t arg = MP_OBJ_FROM_PTR(o_str);
+    return mp_obj_exception_make_new(exc_type, 1, 0, &arg);
 }
 
 // The following struct and function implement a simple printer that conservatively
@@ -366,11 +405,10 @@ mp_obj_t mp_obj_new_exception_msg_varg(const mp_obj_type_t *exc_type, const char
     // that buffer to store the string object and its data (at least 16 bytes for
     // the string data), reserving room at the start for the traceback and 1-tuple.
     if ((o_str == NULL || o_str_buf == NULL)
-        && mp_emergency_exception_buf_size >= EMG_TRACEBACK_ALLOC * sizeof(size_t)
-            + sizeof(mp_obj_tuple_t) + sizeof(mp_obj_t) + sizeof(mp_obj_str_t) + 16) {
+        && mp_emergency_exception_buf_size >= EMG_BUF_STR_OFFSET + sizeof(mp_obj_str_t) + 16) {
         used_emg_buf = true;
         o_str = (mp_obj_str_t*)((uint8_t*)MP_STATE_VM(mp_emergency_exception_buf)
-            + EMG_TRACEBACK_ALLOC * sizeof(size_t) + sizeof(mp_obj_tuple_t) + sizeof(mp_obj_t));
+            + EMG_BUF_STR_OFFSET);
         o_str_buf = (byte*)&o_str[1];
         o_str_alloc = (uint8_t*)MP_STATE_VM(mp_emergency_exception_buf)
             + mp_emergency_exception_buf_size - o_str_buf;
@@ -464,11 +502,12 @@ void mp_obj_exception_add_traceback(mp_obj_t self_in, qstr file, size_t line, qs
         self->traceback_data = m_new_maybe(size_t, TRACEBACK_ENTRY_LEN);
         if (self->traceback_data == NULL) {
             #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
-            if (mp_emergency_exception_buf_size >= EMG_TRACEBACK_ALLOC * sizeof(size_t)) {
+            if (mp_emergency_exception_buf_size >= EMG_BUF_TRACEBACK_OFFSET + EMG_BUF_TRACEBACK_SIZE) {
                 // There is room in the emergency buffer for traceback data
-                size_t *tb = (size_t*)MP_STATE_VM(mp_emergency_exception_buf);
+                size_t *tb = (size_t*)((uint8_t*)MP_STATE_VM(mp_emergency_exception_buf)
+                    + EMG_BUF_TRACEBACK_OFFSET);
                 self->traceback_data = tb;
-                self->traceback_alloc = EMG_TRACEBACK_ALLOC;
+                self->traceback_alloc = EMG_BUF_TRACEBACK_SIZE / sizeof(size_t);
             } else {
                 // Can't allocate and no room in emergency buffer
                 return;

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