diff --git a/lib/mp-readline/readline.c b/lib/mp-readline/readline.c
index d0449e97ea9295322dc6cd87aec4c302de1a7497..31b60e2682a522fc526eb5fdfbab34864afbd6c2 100644
--- a/lib/mp-readline/readline.c
+++ b/lib/mp-readline/readline.c
@@ -105,7 +105,7 @@ int readline_process_char(int c) {
     bool redraw_from_cursor = false;
     int redraw_step_forward = 0;
     if (rl.escape_seq == ESEQ_NONE) {
-        if (CHAR_CTRL_A <= c && c <= CHAR_CTRL_D && vstr_len(rl.line) == rl.orig_line_len) {
+        if (CHAR_CTRL_A <= c && c <= CHAR_CTRL_E && vstr_len(rl.line) == rl.orig_line_len) {
             // control character with empty line
             return c;
         } else if (c == CHAR_CTRL_A) {
diff --git a/stmhal/pyexec.c b/stmhal/pyexec.c
index c692933dc005a871960f71b71c5c1bf4aba3d6ea..a809940a8a292e4e1a7c4f3cbc15d30be572b066 100644
--- a/stmhal/pyexec.c
+++ b/stmhal/pyexec.c
@@ -389,6 +389,7 @@ friendly_repl_reset:
 
         vstr_reset(&line);
         int ret = readline(&line, ">>> ");
+        mp_parse_input_kind_t parse_input_kind = MP_PARSE_SINGLE_INPUT;
 
         if (ret == CHAR_CTRL_A) {
             // change to raw REPL
@@ -409,20 +410,46 @@ friendly_repl_reset:
             mp_hal_stdout_tx_str("\r\n");
             vstr_clear(&line);
             return PYEXEC_FORCED_EXIT;
+        } else if (ret == CHAR_CTRL_E) {
+            // paste mode
+            mp_hal_stdout_tx_str("\r\npaste mode; CTRL-C to cancel, CTRL-D to finish\r\n=== ");
+            vstr_reset(&line);
+            for (;;) {
+                char c = mp_hal_stdin_rx_chr();
+                if (c == CHAR_CTRL_C) {
+                    // cancel everything
+                    mp_hal_stdout_tx_str("\r\n");
+                    goto input_restart;
+                } else if (c == CHAR_CTRL_D) {
+                    // end of input
+                    mp_hal_stdout_tx_str("\r\n");
+                    break;
+                } else {
+                    // add char to buffer and echo
+                    vstr_add_byte(&line, c);
+                    if (c == '\r') {
+                        mp_hal_stdout_tx_str("\r\n=== ");
+                    } else {
+                        mp_hal_stdout_tx_strn(&c, 1);
+                    }
+                }
+            }
+            parse_input_kind = MP_PARSE_FILE_INPUT;
         } else if (vstr_len(&line) == 0) {
             continue;
-        }
-
-        while (mp_repl_continue_with_input(vstr_null_terminated_str(&line))) {
-            vstr_add_byte(&line, '\n');
-            ret = readline(&line, "... ");
-            if (ret == CHAR_CTRL_C) {
-                // cancel everything
-                mp_hal_stdout_tx_str("\r\n");
-                goto input_restart;
-            } else if (ret == CHAR_CTRL_D) {
-                // stop entering compound statement
-                break;
+        } else {
+            // got a line with non-zero length, see if it needs continuing
+            while (mp_repl_continue_with_input(vstr_null_terminated_str(&line))) {
+                vstr_add_byte(&line, '\n');
+                ret = readline(&line, "... ");
+                if (ret == CHAR_CTRL_C) {
+                    // cancel everything
+                    mp_hal_stdout_tx_str("\r\n");
+                    goto input_restart;
+                } else if (ret == CHAR_CTRL_D) {
+                    // stop entering compound statement
+                    break;
+                }
             }
         }
 
@@ -430,7 +457,7 @@ friendly_repl_reset:
         if (lex == NULL) {
             printf("MemoryError\n");
         } else {
-            ret = parse_compile_execute(lex, MP_PARSE_SINGLE_INPUT, EXEC_FLAG_ALLOW_DEBUGGING | EXEC_FLAG_IS_REPL);
+            ret = parse_compile_execute(lex, parse_input_kind, EXEC_FLAG_ALLOW_DEBUGGING | EXEC_FLAG_IS_REPL);
             if (ret & PYEXEC_FORCED_EXIT) {
                 return ret;
             }
diff --git a/unix/main.c b/unix/main.c
index 7a2f7e4ff1a578261be7c9199df502796fbdf752..a3f4cfe6cf982e2449221b7a18a8fdd921f6de6f 100644
--- a/unix/main.c
+++ b/unix/main.c
@@ -162,10 +162,12 @@ STATIC int do_repl(void) {
     vstr_t line;
     vstr_init(&line, 16);
     for (;;) {
+        mp_hal_stdio_mode_raw();
+
     input_restart:
         vstr_reset(&line);
-        mp_hal_stdio_mode_raw();
         int ret = readline(&line, ">>> ");
+        mp_parse_input_kind_t parse_input_kind = MP_PARSE_SINGLE_INPUT;
 
         if (ret == CHAR_CTRL_D) {
             // EOF
@@ -173,31 +175,56 @@ STATIC int do_repl(void) {
             mp_hal_stdio_mode_orig();
             vstr_clear(&line);
             return 0;
+        } else if (ret == CHAR_CTRL_E) {
+            // paste mode
+            mp_hal_stdout_tx_str("\npaste mode; CTRL-C to cancel, CTRL-D to finish\n=== ");
+            vstr_reset(&line);
+            for (;;) {
+                char c = mp_hal_stdin_rx_chr();
+                if (c == CHAR_CTRL_C) {
+                    // cancel everything
+                    mp_hal_stdout_tx_str("\n");
+                    goto input_restart;
+                } else if (c == CHAR_CTRL_D) {
+                    // end of input
+                    mp_hal_stdout_tx_str("\n");
+                    break;
+                } else {
+                    // add char to buffer and echo
+                    vstr_add_byte(&line, c);
+                    if (c == '\r') {
+                        mp_hal_stdout_tx_str("\n=== ");
+                    } else {
+                        mp_hal_stdout_tx_strn(&c, 1);
+                    }
+                }
+            }
+            parse_input_kind = MP_PARSE_FILE_INPUT;
         } else if (line.len == 0) {
             if (ret != 0) {
                 printf("\n");
             }
-            mp_hal_stdio_mode_orig();
-            continue;
-        }
-
-        while (mp_repl_continue_with_input(vstr_null_terminated_str(&line))) {
-            vstr_add_byte(&line, '\n');
-            ret = readline(&line, "... ");
-            if (ret == CHAR_CTRL_C) {
-                // cancel everything
-                printf("\n");
-                mp_hal_stdio_mode_orig();
-                goto input_restart;
-            } else if (ret == CHAR_CTRL_D) {
-                // stop entering compound statement
-                break;
+            goto input_restart;
+        } else {
+            // got a line with non-zero length, see if it needs continuing
+            while (mp_repl_continue_with_input(vstr_null_terminated_str(&line))) {
+                vstr_add_byte(&line, '\n');
+                ret = readline(&line, "... ");
+                if (ret == CHAR_CTRL_C) {
+                    // cancel everything
+                    printf("\n");
+                    goto input_restart;
+                } else if (ret == CHAR_CTRL_D) {
+                    // stop entering compound statement
+                    break;
+                }
             }
         }
+
         mp_hal_stdio_mode_orig();
 
         mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, line.buf, line.len, false);
-        ret = execute_from_lexer(lex, MP_PARSE_SINGLE_INPUT, true);
+        ret = execute_from_lexer(lex, parse_input_kind, true);
         if (ret & FORCED_EXIT) {
             return ret;
         }