@@ -537,10 +537,20 @@ MP_REGISTER_ROOT_POINTER(vstr_t * repl_line);
537537
538538#else // MICROPY_REPL_EVENT_DRIVEN
539539
540+ #if !MICROPY_HAL_HAS_STDIO_MODE_SWITCH
541+ // If the port doesn't need any stdio mode switching calls then provide trivial ones.
542+ static inline void mp_hal_stdio_mode_raw (void ) {
543+ }
544+ static inline void mp_hal_stdio_mode_orig (void ) {
545+ }
546+ #endif
547+
540548int pyexec_raw_repl (void ) {
541549 vstr_t line ;
542550 vstr_init (& line , 32 );
543551
552+ mp_hal_stdio_mode_raw ();
553+
544554raw_repl_reset :
545555 mp_hal_stdout_tx_str ("raw REPL; CTRL-B to exit\r\n" );
546556
@@ -554,6 +564,7 @@ int pyexec_raw_repl(void) {
554564 if (vstr_len (& line ) == 2 && vstr_str (& line )[0 ] == CHAR_CTRL_E ) {
555565 int ret = do_reader_stdin (vstr_str (& line )[1 ]);
556566 if (ret & PYEXEC_FORCED_EXIT ) {
567+ mp_hal_stdio_mode_orig ();
557568 return ret ;
558569 }
559570 vstr_reset (& line );
@@ -566,6 +577,7 @@ int pyexec_raw_repl(void) {
566577 mp_hal_stdout_tx_str ("\r\n" );
567578 vstr_clear (& line );
568579 pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL ;
580+ mp_hal_stdio_mode_orig ();
569581 return 0 ;
570582 } else if (c == CHAR_CTRL_C ) {
571583 // clear line
@@ -586,10 +598,19 @@ int pyexec_raw_repl(void) {
586598 // exit for a soft reset
587599 mp_hal_stdout_tx_str ("\r\n" );
588600 vstr_clear (& line );
601+ mp_hal_stdio_mode_orig ();
589602 return PYEXEC_FORCED_EXIT ;
590603 }
591604
605+ // Execute code with terminal mode switching for keyboard interrupt support.
606+ // Switches to original mode (ISIG enabled) so Ctrl-C generates SIGINT during execution,
607+ // then restores raw mode (ISIG disabled) for REPL input to support paste mode with arbitrary
608+ // data including byte 0x03 without triggering interrupts.
609+ mp_hal_stdio_mode_orig ();
592610 int ret = parse_compile_execute (& line , MP_PARSE_FILE_INPUT , EXEC_FLAG_PRINT_EOF | EXEC_FLAG_SOURCE_IS_VSTR );
611+ if (!(ret & PYEXEC_FORCED_EXIT )) {
612+ mp_hal_stdio_mode_raw ();
613+ }
593614 if (ret & PYEXEC_FORCED_EXIT ) {
594615 return ret ;
595616 }
@@ -600,6 +621,8 @@ int pyexec_friendly_repl(void) {
600621 vstr_t line ;
601622 vstr_init (& line , 32 );
602623
624+ mp_hal_stdio_mode_raw ();
625+
603626friendly_repl_reset :
604627 mp_hal_stdout_tx_str (MICROPY_BANNER_NAME_AND_VERSION );
605628 mp_hal_stdout_tx_str ("; " MICROPY_BANNER_MACHINE );
@@ -641,6 +664,7 @@ int pyexec_friendly_repl(void) {
641664 mp_hal_stdout_tx_str ("\r\n" );
642665 vstr_clear (& line );
643666 pyexec_mode_kind = PYEXEC_MODE_RAW_REPL ;
667+ mp_hal_stdio_mode_orig ();
644668 return 0 ;
645669 } else if (ret == CHAR_CTRL_B ) {
646670 // reset friendly REPL
@@ -654,6 +678,7 @@ int pyexec_friendly_repl(void) {
654678 // exit for a soft reset
655679 mp_hal_stdout_tx_str ("\r\n" );
656680 vstr_clear (& line );
681+ mp_hal_stdio_mode_orig ();
657682 return PYEXEC_FORCED_EXIT ;
658683 } else if (ret == CHAR_CTRL_E ) {
659684 // paste mode
@@ -698,7 +723,15 @@ int pyexec_friendly_repl(void) {
698723 }
699724 }
700725
726+ // Execute code with terminal mode switching for keyboard interrupt support.
727+ // Switches to original mode (ISIG enabled) so Ctrl-C generates SIGINT during execution,
728+ // then restores raw mode (ISIG disabled) for REPL input to support paste mode with arbitrary
729+ // data including byte 0x03 without triggering interrupts.
730+ mp_hal_stdio_mode_orig ();
701731 ret = parse_compile_execute (& line , parse_input_kind , EXEC_FLAG_ALLOW_DEBUGGING | EXEC_FLAG_IS_REPL | EXEC_FLAG_SOURCE_IS_VSTR );
732+ if (!(ret & PYEXEC_FORCED_EXIT )) {
733+ mp_hal_stdio_mode_raw ();
734+ }
702735 if (ret & PYEXEC_FORCED_EXIT ) {
703736 return ret ;
704737 }
0 commit comments