diff --git a/esp8266/main.c b/esp8266/main.c
index b6024f3c574447625301e0d3ea9c91e2dc380982..a3878c0e75232b3e9759d639fe63d2e363e06843 100644
--- a/esp8266/main.c
+++ b/esp8266/main.c
@@ -39,8 +39,7 @@
 
 STATIC char heap[16384];
 
-void user_init(void) {
-soft_reset:
+STATIC void mp_reset(void) {
     mp_stack_set_limit(10240);
     mp_hal_init();
     gc_init(heap, heap + sizeof(heap));
@@ -48,29 +47,20 @@ soft_reset:
     mp_init();
     mp_obj_list_init(mp_sys_path, 0);
     mp_obj_list_init(mp_sys_argv, 0);
+}
 
-    printf("\n");
+void soft_reset(void) {
+    mp_hal_stdout_tx_str("PYB: soft reset\r\n");
+    mp_hal_udelay(10000); // allow UART to flush output
+    mp_reset();
+    pyexec_event_repl_init();
+}
 
-#if MICROPY_REPL_EVENT_DRIVEN
-    pyexec_friendly_repl_init();
+void user_init(void) {
+    mp_reset();
+    mp_hal_stdout_tx_str("\r\n");
+    pyexec_event_repl_init();
     uart_task_init();
-    return;
-    goto soft_reset;
-#else
-    for (;;) {
-        if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) {
-            if (pyexec_raw_repl() != 0) {
-                break;
-            }
-        } else {
-            if (pyexec_friendly_repl() != 0) {
-                break;
-            }
-        }
-    }
-
-    goto soft_reset;
-#endif
 }
 
 mp_lexer_t *mp_lexer_new_from_file(const char *filename) {
diff --git a/esp8266/uart.c b/esp8266/uart.c
index 6087668a7012c9efaee72d0778df5a10a4e57713..87bbb7c92eb1937be9b88ca1270879e3fd800ea7 100644
--- a/esp8266/uart.c
+++ b/esp8266/uart.c
@@ -200,10 +200,16 @@ void ICACHE_FLASH_ATTR uart_reattach() {
 
 // Task-based UART interface
 
-int pyexec_friendly_repl_process_char(int c);
+#include "py/obj.h"
+#include "stmhal/pyexec.h"
+
+void soft_reset(void);
 
 void uart_task_handler(os_event_t *evt) {
-    pyexec_friendly_repl_process_char(evt->par);
+    int ret = pyexec_event_repl_process_char(evt->par);
+    if (ret & PYEXEC_FORCED_EXIT) {
+        soft_reset();
+    }
 }
 
 void uart_task_init() {
diff --git a/minimal/main.c b/minimal/main.c
index 29b5af1af7b83ef9d1659b8bb33d2d4ce57ec204..f6041267a206f7c31e66b4148b1aa123570ffc2f 100644
--- a/minimal/main.c
+++ b/minimal/main.c
@@ -41,10 +41,10 @@ int main(int argc, char **argv) {
     #endif
     mp_init();
     #if MICROPY_REPL_EVENT_DRIVEN
-    pyexec_friendly_repl_init();
+    pyexec_event_repl_init();
     for (;;) {
-        int c = stdin_rx_chr();
-        if (pyexec_friendly_repl_process_char(c)) {
+        int c = mp_hal_stdin_rx_chr();
+        if (pyexec_event_repl_process_char(c)) {
             break;
         }
     }
diff --git a/stmhal/pyexec.c b/stmhal/pyexec.c
index 1a121ce3e5cbc62631672ef686ee319cb78584ad..aa7e35390566e89e73d6a400ae8d984bcdca2d60 100644
--- a/stmhal/pyexec.c
+++ b/stmhal/pyexec.c
@@ -118,83 +118,81 @@ STATIC int parse_compile_execute(mp_lexer_t *lex, mp_parse_input_kind_t input_ki
     return ret;
 }
 
-int pyexec_raw_repl(void) {
-    vstr_t line;
-    vstr_init(&line, 32);
-
-raw_repl_reset:
-    mp_hal_stdout_tx_str("raw REPL; CTRL-B to exit\r\n");
-
-    for (;;) {
-        vstr_reset(&line);
-        mp_hal_stdout_tx_str(">");
-        for (;;) {
-            int c = mp_hal_stdin_rx_chr();
-            if (c == CHAR_CTRL_A) {
-                // reset raw REPL
-                goto raw_repl_reset;
-            } else if (c == CHAR_CTRL_B) {
-                // change to friendly REPL
-                mp_hal_stdout_tx_str("\r\n");
-                vstr_clear(&line);
-                pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL;
-                return 0;
-            } else if (c == CHAR_CTRL_C) {
-                // clear line
-                vstr_reset(&line);
-            } else if (c == CHAR_CTRL_D) {
-                // input finished
-                break;
-            } else {
-                // let through any other raw 8-bit value
-                vstr_add_byte(&line, c);
-            }
-        }
-
-        // indicate reception of command
-        mp_hal_stdout_tx_str("OK");
-
-        if (line.len == 0) {
-            // exit for a soft reset
-            mp_hal_stdout_tx_str("\r\n");
-            vstr_clear(&line);
-            return PYEXEC_FORCED_EXIT;
-        }
-
-        mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, line.buf, line.len, 0);
-        if (lex == NULL) {
-            printf("\x04MemoryError\n\x04");
-        } else {
-            int ret = parse_compile_execute(lex, MP_PARSE_FILE_INPUT, EXEC_FLAG_PRINT_EOF);
-            if (ret & PYEXEC_FORCED_EXIT) {
-                return ret;
-            }
-        }
-    }
-}
-
 #if MICROPY_REPL_EVENT_DRIVEN
 
-typedef struct _friendly_repl_t {
+typedef struct _repl_t {
+    // XXX line holds a root pointer!
     vstr_t line;
     bool cont_line;
-} friendly_repl_t;
+} repl_t;
+
+repl_t repl;
 
-friendly_repl_t repl;
+STATIC int pyexec_raw_repl_process_char(int c);
+STATIC int pyexec_friendly_repl_process_char(int c);
 
-void pyexec_friendly_repl_init(void) {
+void pyexec_event_repl_init(void) {
     vstr_init(&repl.line, 32);
     repl.cont_line = false;
     readline_init(&repl.line, ">>> ");
+    if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) {
+        pyexec_raw_repl_process_char(CHAR_CTRL_A);
+    } else {
+        pyexec_friendly_repl_process_char(CHAR_CTRL_B);
+    }
 }
 
-void pyexec_friendly_repl_reset(void) {
+STATIC int pyexec_raw_repl_process_char(int c) {
+    if (c == CHAR_CTRL_A) {
+        // reset raw REPL
+        mp_hal_stdout_tx_str("raw REPL; CTRL-B to exit\r\n");
+        goto reset;
+    } else if (c == CHAR_CTRL_B) {
+        // change to friendly REPL
+        pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL;
+        repl.cont_line = false;
+        pyexec_friendly_repl_process_char(CHAR_CTRL_B);
+        return 0;
+    } else if (c == CHAR_CTRL_C) {
+        // clear line
+        vstr_reset(&repl.line);
+        return 0;
+    } else if (c == CHAR_CTRL_D) {
+        // input finished
+    } else {
+        // let through any other raw 8-bit value
+        vstr_add_byte(&repl.line, c);
+        return 0;
+    }
+
+    // indicate reception of command
+    mp_hal_stdout_tx_str("OK");
+
+    if (repl.line.len == 0) {
+        // exit for a soft reset
+        mp_hal_stdout_tx_str("\r\n");
+        vstr_clear(&repl.line);
+        return PYEXEC_FORCED_EXIT;
+    }
+
+    mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, repl.line.buf, repl.line.len, 0);
+    if (lex == NULL) {
+        mp_hal_stdout_tx_str("\x04MemoryError\r\n\x04");
+    } else {
+        int ret = parse_compile_execute(lex, MP_PARSE_FILE_INPUT, EXEC_FLAG_PRINT_EOF);
+        if (ret & PYEXEC_FORCED_EXIT) {
+            return ret;
+        }
+    }
+
+reset:
     vstr_reset(&repl.line);
-    repl.cont_line = false;
-    readline_init(&repl.line, ">>> ");
+    mp_hal_stdout_tx_str(">");
+
+    return 0;
 }
 
-int pyexec_friendly_repl_process_char(int c) {
+STATIC int pyexec_friendly_repl_process_char(int c) {
     int ret = readline_process_char(c);
 
     if (!repl.cont_line) {
@@ -203,12 +201,14 @@ int pyexec_friendly_repl_process_char(int c) {
             // change to raw REPL
             pyexec_mode_kind = PYEXEC_MODE_RAW_REPL;
             mp_hal_stdout_tx_str("\r\n");
-            vstr_clear(&repl.line);
-            return PYEXEC_SWITCH_MODE;
+            pyexec_raw_repl_process_char(CHAR_CTRL_A);
+            return 0;
         } else if (ret == CHAR_CTRL_B) {
             // reset friendly REPL
             mp_hal_stdout_tx_str("\r\n");
-            goto friendly_repl_reset;
+            mp_hal_stdout_tx_str("Micro Python " MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE "; " MICROPY_HW_BOARD_NAME " with " MICROPY_HW_MCU_NAME "\r\n");
+            mp_hal_stdout_tx_str("Type \"help()\" for more information.\r\n");
+            goto input_restart;
         } else if (ret == CHAR_CTRL_C) {
             // break
             mp_hal_stdout_tx_str("\r\n");
@@ -218,8 +218,6 @@ int pyexec_friendly_repl_process_char(int c) {
             mp_hal_stdout_tx_str("\r\n");
             vstr_clear(&repl.line);
             return PYEXEC_FORCED_EXIT;
-        } else if (vstr_len(&repl.line) == 0) {
-            //goto input_restart;
         }
 
         if (ret < 0) {
@@ -238,13 +236,13 @@ int pyexec_friendly_repl_process_char(int c) {
     } else {
 
         if (ret == CHAR_CTRL_C) {
-                // cancel everything
-                mp_hal_stdout_tx_str("\r\n");
-                repl.cont_line = false;
-                goto input_restart;
+           // cancel everything
+           mp_hal_stdout_tx_str("\r\n");
+           repl.cont_line = false;
+           goto input_restart;
         } else if (ret == CHAR_CTRL_D) {
-                // stop entering compound statement
-                goto exec;
+            // stop entering compound statement
+            goto exec;
         }
 
         if (ret < 0) {
@@ -268,14 +266,78 @@ exec: ;
             }
         }
 
-friendly_repl_reset: // TODO
 input_restart:
-        pyexec_friendly_repl_reset();
+        vstr_reset(&repl.line);
+        repl.cont_line = false;
+        readline_init(&repl.line, ">>> ");
         return 0;
     }
 }
 
-#else //MICROPY_REPL_EVENT_DRIVEN
+int pyexec_event_repl_process_char(int c) {
+    if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) {
+        return pyexec_raw_repl_process_char(c);
+    } else {
+        return pyexec_friendly_repl_process_char(c);
+    }
+}
+
+#else // MICROPY_REPL_EVENT_DRIVEN
+
+int pyexec_raw_repl(void) {
+    vstr_t line;
+    vstr_init(&line, 32);
+
+raw_repl_reset:
+    mp_hal_stdout_tx_str("raw REPL; CTRL-B to exit\r\n");
+
+    for (;;) {
+        vstr_reset(&line);
+        mp_hal_stdout_tx_str(">");
+        for (;;) {
+            int c = mp_hal_stdin_rx_chr();
+            if (c == CHAR_CTRL_A) {
+                // reset raw REPL
+                goto raw_repl_reset;
+            } else if (c == CHAR_CTRL_B) {
+                // change to friendly REPL
+                mp_hal_stdout_tx_str("\r\n");
+                vstr_clear(&line);
+                pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL;
+                return 0;
+            } else if (c == CHAR_CTRL_C) {
+                // clear line
+                vstr_reset(&line);
+            } else if (c == CHAR_CTRL_D) {
+                // input finished
+                break;
+            } else {
+                // let through any other raw 8-bit value
+                vstr_add_byte(&line, c);
+            }
+        }
+
+        // indicate reception of command
+        mp_hal_stdout_tx_str("OK");
+
+        if (line.len == 0) {
+            // exit for a soft reset
+            mp_hal_stdout_tx_str("\r\n");
+            vstr_clear(&line);
+            return PYEXEC_FORCED_EXIT;
+        }
+
+        mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, line.buf, line.len, 0);
+        if (lex == NULL) {
+            printf("\x04MemoryError\n\x04");
+        } else {
+            int ret = parse_compile_execute(lex, MP_PARSE_FILE_INPUT, EXEC_FLAG_PRINT_EOF);
+            if (ret & PYEXEC_FORCED_EXIT) {
+                return ret;
+            }
+        }
+    }
+}
 
 int pyexec_friendly_repl(void) {
     vstr_t line;
@@ -376,7 +438,7 @@ friendly_repl_reset:
     }
 }
 
-#endif //MICROPY_REPL_EVENT_DRIVEN
+#endif // MICROPY_REPL_EVENT_DRIVEN
 
 int pyexec_file(const char *filename) {
     mp_lexer_t *lex = mp_lexer_new_from_file(filename);
diff --git a/stmhal/pyexec.h b/stmhal/pyexec.h
index d01d505a78ffed31b0a06f0ba32ad12e150cb3c1..0728b91333cef2b53af5f57e2b77f8b4b704c447 100644
--- a/stmhal/pyexec.h
+++ b/stmhal/pyexec.h
@@ -37,7 +37,7 @@ extern pyexec_mode_kind_t pyexec_mode_kind;
 int pyexec_raw_repl(void);
 int pyexec_friendly_repl(void);
 int pyexec_file(const char *filename);
-void pyexec_friendly_repl_init(void);
-int pyexec_friendly_repl_process_char(int c);
+void pyexec_event_repl_init(void);
+int pyexec_event_repl_process_char(int c);
 
 MP_DECLARE_CONST_FUN_OBJ(pyb_set_repl_info_obj);