@@ -516,10 +516,20 @@ MP_REGISTER_ROOT_POINTER(vstr_t * repl_line);
516516
517517#else // MICROPY_REPL_EVENT_DRIVEN
518518
519+ #if !MICROPY_HAL_HAS_STDIO_MODE_SWITCH
520+ // If the port doesn't need any stdio mode switching calls then provide trivial ones.
521+ static inline void mp_hal_stdio_mode_raw (void ) {
522+ }
523+ static inline void mp_hal_stdio_mode_orig (void ) {
524+ }
525+ #endif
526+
519527int pyexec_raw_repl (void ) {
520528 vstr_t line ;
521529 vstr_init (& line , 32 );
522530
531+ mp_hal_stdio_mode_raw ();
532+
523533raw_repl_reset :
524534 mp_hal_stdout_tx_str ("raw REPL; CTRL-B to exit\r\n" );
525535
@@ -533,6 +543,7 @@ int pyexec_raw_repl(void) {
533543 if (vstr_len (& line ) == 2 && vstr_str (& line )[0 ] == CHAR_CTRL_E ) {
534544 int ret = do_reader_stdin (vstr_str (& line )[1 ]);
535545 if (ret & PYEXEC_FORCED_EXIT ) {
546+ mp_hal_stdio_mode_orig ();
536547 return ret ;
537548 }
538549 vstr_reset (& line );
@@ -545,6 +556,7 @@ int pyexec_raw_repl(void) {
545556 mp_hal_stdout_tx_str ("\r\n" );
546557 vstr_clear (& line );
547558 pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL ;
559+ mp_hal_stdio_mode_orig ();
548560 return 0 ;
549561 } else if (c == CHAR_CTRL_C ) {
550562 // clear line
@@ -565,10 +577,19 @@ int pyexec_raw_repl(void) {
565577 // exit for a soft reset
566578 mp_hal_stdout_tx_str ("\r\n" );
567579 vstr_clear (& line );
580+ mp_hal_stdio_mode_orig ();
568581 return PYEXEC_FORCED_EXIT ;
569582 }
570583
584+ // Execute code with terminal mode switching for keyboard interrupt support.
585+ // Switches to original mode (ISIG enabled) so Ctrl-C generates SIGINT during execution,
586+ // then restores raw mode (ISIG disabled) for REPL input to support paste mode with arbitrary
587+ // data including byte 0x03 without triggering interrupts.
588+ mp_hal_stdio_mode_orig ();
571589 int ret = parse_compile_execute (& line , MP_PARSE_FILE_INPUT , EXEC_FLAG_PRINT_EOF | EXEC_FLAG_SOURCE_IS_VSTR );
590+ if (!(ret & PYEXEC_FORCED_EXIT )) {
591+ mp_hal_stdio_mode_raw ();
592+ }
572593 if (ret & PYEXEC_FORCED_EXIT ) {
573594 return ret ;
574595 }
@@ -579,6 +600,8 @@ int pyexec_friendly_repl(void) {
579600 vstr_t line ;
580601 vstr_init (& line , 32 );
581602
603+ mp_hal_stdio_mode_raw ();
604+
582605friendly_repl_reset :
583606 mp_hal_stdout_tx_str (MICROPY_BANNER_NAME_AND_VERSION );
584607 mp_hal_stdout_tx_str ("; " MICROPY_BANNER_MACHINE );
@@ -634,6 +657,7 @@ int pyexec_friendly_repl(void) {
634657 mp_hal_stdout_tx_str ("\r\n" );
635658 vstr_clear (& line );
636659 pyexec_mode_kind = PYEXEC_MODE_RAW_REPL ;
660+ mp_hal_stdio_mode_orig ();
637661 return 0 ;
638662 } else if (ret == CHAR_CTRL_B ) {
639663 // reset friendly REPL
@@ -647,6 +671,7 @@ int pyexec_friendly_repl(void) {
647671 // exit for a soft reset
648672 mp_hal_stdout_tx_str ("\r\n" );
649673 vstr_clear (& line );
674+ mp_hal_stdio_mode_orig ();
650675 return PYEXEC_FORCED_EXIT ;
651676 } else if (ret == CHAR_CTRL_E ) {
652677 // paste mode
@@ -691,7 +716,15 @@ int pyexec_friendly_repl(void) {
691716 }
692717 }
693718
719+ // Execute code with terminal mode switching for keyboard interrupt support.
720+ // Switches to original mode (ISIG enabled) so Ctrl-C generates SIGINT during execution,
721+ // then restores raw mode (ISIG disabled) for REPL input to support paste mode with arbitrary
722+ // data including byte 0x03 without triggering interrupts.
723+ mp_hal_stdio_mode_orig ();
694724 ret = parse_compile_execute (& line , parse_input_kind , EXEC_FLAG_ALLOW_DEBUGGING | EXEC_FLAG_IS_REPL | EXEC_FLAG_SOURCE_IS_VSTR );
725+ if (!(ret & PYEXEC_FORCED_EXIT )) {
726+ mp_hal_stdio_mode_raw ();
727+ }
695728 if (ret & PYEXEC_FORCED_EXIT ) {
696729 return ret ;
697730 }
0 commit comments