diff --git a/esp8266/main.c b/esp8266/main.c
index 5087c7d6a33920973b66052c7138baff31e91100..1e988ef33ae6ea0ff0ee3c60ad89dc76478d0525 100644
--- a/esp8266/main.c
+++ b/esp8266/main.c
@@ -58,6 +58,10 @@ STATIC void mp_reset(void) {
     MP_STATE_PORT(mp_kbd_exception) = mp_obj_new_exception(&mp_type_KeyboardInterrupt);
     MP_STATE_PORT(term_obj) = MP_OBJ_NULL;
     MP_STATE_PORT(dupterm_arr_obj) = MP_OBJ_NULL;
+    #if MICROPY_EMIT_XTENSA
+    extern void esp_native_code_init(void);
+    esp_native_code_init();
+    #endif
     pin_init0();
     readline_init0();
     dupterm_task_init();
diff --git a/esp8266/modesp.c b/esp8266/modesp.c
index 207422b67d16a763153d0dae92982066c66170f5..302bc01edc5a267b08ae1c8afd341b16f005cfdb 100644
--- a/esp8266/modesp.c
+++ b/esp8266/modesp.c
@@ -699,6 +699,99 @@ STATIC mp_obj_t esp_esf_free_bufs(mp_obj_t idx_in) {
 }
 STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_esf_free_bufs_obj, esp_esf_free_bufs);
 
+#if MICROPY_EMIT_XTENSA
+
+// We provide here a way of committing executable data to a region from
+// which it can be executed by the CPU.  There are 2 such writable regions:
+//  - iram1, which may have some space left at the end of it
+//  - memory-mapped flash rom
+//
+// By default the iram1 region (the space at the end of it) is used.  The
+// user can select iram1 or a section of flash by calling the
+// esp.set_native_code_location() function; see below.  If flash is selected
+// then it is erased as needed.
+
+#define IRAM1_END (0x40108000)
+#define FLASH_START (0x40200000)
+#define FLASH_END (0x40300000)
+#define FLASH_SEC_SIZE (4096)
+
+#define ESP_NATIVE_CODE_IRAM1 (0)
+#define ESP_NATIVE_CODE_FLASH (1)
+
+extern uint32_t _lit4_end;
+STATIC uint32_t esp_native_code_location;
+STATIC uint32_t esp_native_code_start;
+STATIC uint32_t esp_native_code_end;
+STATIC uint32_t esp_native_code_cur;
+STATIC uint32_t esp_native_code_erased;
+
+void esp_native_code_init(void) {
+    esp_native_code_location = ESP_NATIVE_CODE_IRAM1;
+    esp_native_code_start = (uint32_t)&_lit4_end;
+    esp_native_code_end = IRAM1_END;
+    esp_native_code_cur = esp_native_code_start;
+    esp_native_code_erased = 0;
+}
+
+void *esp_native_code_commit(void *buf, size_t len) {
+    //printf("COMMIT(buf=%p, len=%u, start=%08x, cur=%08x, end=%08x, erased=%08x)\n", buf, len, esp_native_code_start, esp_native_code_cur, esp_native_code_end, esp_native_code_erased);
+
+    len = (len + 3) & ~3;
+    if (esp_native_code_cur + len > esp_native_code_end) {
+        nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_MemoryError,
+            "memory allocation failed, allocating %u bytes for native code", (uint)len));
+    }
+
+    void *dest;
+    if (esp_native_code_location == ESP_NATIVE_CODE_IRAM1) {
+        dest = (void*)esp_native_code_cur;
+        memcpy(dest, buf, len);
+    } else {
+        SpiFlashOpResult res;
+        while (esp_native_code_erased < esp_native_code_cur + len) {
+            res = spi_flash_erase_sector(esp_native_code_erased / FLASH_SEC_SIZE);
+            if (res != SPI_FLASH_RESULT_OK) {
+                break;
+            }
+            esp_native_code_erased += FLASH_SEC_SIZE;
+        }
+        if (res == SPI_FLASH_RESULT_OK) {
+            res = spi_flash_write(esp_native_code_cur, buf, len);
+        }
+        if (res != SPI_FLASH_RESULT_OK) {
+            mp_raise_OSError(res == SPI_FLASH_RESULT_TIMEOUT ? MP_ETIMEDOUT : MP_EIO);
+        }
+        dest = (void*)(FLASH_START + esp_native_code_cur);
+    }
+
+    esp_native_code_cur += len;
+
+    return dest;
+}
+
+STATIC mp_obj_t esp_set_native_code_location(mp_obj_t start_in, mp_obj_t len_in) {
+    if (start_in == mp_const_none && len_in == mp_const_none) {
+        // use end of iram1 region
+        esp_native_code_init();
+    } else {
+        // use flash; input params are byte offsets from start of flash
+        esp_native_code_location = ESP_NATIVE_CODE_FLASH;
+        esp_native_code_start = mp_obj_get_int(start_in);
+        esp_native_code_end = esp_native_code_start + mp_obj_get_int(len_in);
+        esp_native_code_cur = esp_native_code_start;
+        esp_native_code_erased = esp_native_code_start;
+        // memory-mapped flash is limited in extents to 1MByte
+        if (esp_native_code_end > FLASH_END - FLASH_START) {
+            mp_raise_ValueError("flash location must be below 1MByte");
+        }
+    }
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(esp_set_native_code_location_obj, esp_set_native_code_location);
+
+#endif
+
 STATIC const mp_map_elem_t esp_module_globals_table[] = {
     { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_esp) },
 
@@ -729,6 +822,9 @@ STATIC const mp_map_elem_t esp_module_globals_table[] = {
     { MP_OBJ_NEW_QSTR(MP_QSTR_malloc), (mp_obj_t)&esp_malloc_obj },
     { MP_OBJ_NEW_QSTR(MP_QSTR_free), (mp_obj_t)&esp_free_obj },
     { MP_OBJ_NEW_QSTR(MP_QSTR_esf_free_bufs), (mp_obj_t)&esp_esf_free_bufs_obj },
+    #if MICROPY_EMIT_XTENSA
+    { MP_OBJ_NEW_QSTR(MP_QSTR_set_native_code_location), (mp_obj_t)&esp_set_native_code_location_obj },
+    #endif
 
 #if MODESP_INCLUDE_CONSTANTS
     { MP_OBJ_NEW_QSTR(MP_QSTR_SLEEP_NONE),
diff --git a/esp8266/mpconfigport.h b/esp8266/mpconfigport.h
index 03d664e570c8d10df695bfd9841bcabde1b9c057..40550024b6debc1ce913ffdfc53ad10940ff666f 100644
--- a/esp8266/mpconfigport.h
+++ b/esp8266/mpconfigport.h
@@ -10,9 +10,7 @@
 #define MICROPY_ALLOC_PARSE_RESULT_INC  (8)
 #define MICROPY_ALLOC_PARSE_CHUNK_INIT  (64)
 #define MICROPY_PERSISTENT_CODE_LOAD (1)
-#define MICROPY_EMIT_X64            (0)
-#define MICROPY_EMIT_THUMB          (0)
-#define MICROPY_EMIT_INLINE_THUMB   (0)
+#define MICROPY_EMIT_XTENSA         (1)
 #define MICROPY_MEM_STATS           (0)
 #define MICROPY_DEBUG_PRINTERS      (1)
 #define MICROPY_DEBUG_PRINTER_DEST  mp_debug_print
@@ -131,6 +129,8 @@ typedef uint32_t sys_prot_t; // for modlwip
 #include <sys/types.h>
 
 #define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len)
+void *esp_native_code_commit(void*, size_t);
+#define MP_PLAT_COMMIT_EXEC(buf, len) esp_native_code_commit(buf, len)
 
 #define mp_type_fileio fatfs_type_fileio
 #define mp_type_textio fatfs_type_textio
diff --git a/esp8266/mpconfigport_512k.h b/esp8266/mpconfigport_512k.h
index ffdc95dbd4af3278a5e939c34b08a94176399c5a..1bb4524054e30a34d76db8ca3b643a214058e8b0 100644
--- a/esp8266/mpconfigport_512k.h
+++ b/esp8266/mpconfigport_512k.h
@@ -1,5 +1,8 @@
 #include <mpconfigport.h>
 
+#undef MICROPY_EMIT_XTENSA
+#define MICROPY_EMIT_XTENSA             (0)
+
 #undef MICROPY_FSUSERMOUNT
 #define MICROPY_FSUSERMOUNT             (0)
 #undef MICROPY_VFS_FAT