diff --git a/stmhal/Makefile b/stmhal/Makefile index bf5db71867..b4169e3d63 100644 --- a/stmhal/Makefile +++ b/stmhal/Makefile @@ -71,6 +71,8 @@ SRC_C = \ math.c \ malloc0.c \ gccollect.c \ + pybstdio.c \ + readline.c \ pyexec.c \ help.c \ input.c \ diff --git a/stmhal/input.c b/stmhal/input.c index f53018a422..d704c2cf76 100644 --- a/stmhal/input.c +++ b/stmhal/input.c @@ -3,10 +3,9 @@ #include "mpconfig.h" #include "qstr.h" #include "obj.h" +#include "readline.h" #include "usb.h" -extern int readline(vstr_t *line, const char *prompt); - STATIC mp_obj_t mp_builtin_input(uint n_args, const mp_obj_t *args) { if (n_args == 1) { mp_obj_print(args[0], PRINT_REPR); diff --git a/stmhal/main.c b/stmhal/main.c index 1da7a9a649..e40026efdd 100644 --- a/stmhal/main.c +++ b/stmhal/main.c @@ -16,10 +16,10 @@ #include "obj.h" #include "parsehelper.h" #include "compile.h" -#include "runtime0.h" #include "runtime.h" #include "gc.h" #include "gccollect.h" +#include "readline.h" #include "pyexec.h" #include "usart.h" #include "led.h" @@ -210,6 +210,8 @@ soft_reset: def_path[2] = MP_OBJ_NEW_QSTR(MP_QSTR_0_colon__slash_lib); sys_path = mp_obj_new_list(3, def_path); + readline_init(); + exti_init(); #if MICROPY_HW_HAS_SWITCH diff --git a/stmhal/modpyb.c b/stmhal/modpyb.c index 432dc859c1..45bd8b1e0f 100644 --- a/stmhal/modpyb.c +++ b/stmhal/modpyb.c @@ -11,6 +11,7 @@ #include "gc.h" #include "gccollect.h" #include "systick.h" +#include "pybstdio.h" #include "pyexec.h" #include "led.h" #include "gpio.h" @@ -202,8 +203,6 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_hid_send_report_obj, pyb_hid_send_report); MP_DEFINE_CONST_FUN_OBJ_2(pyb_I2C_obj, pyb_I2C); // TODO put this in i2c.c #endif -extern int stdin_rx_chr(void); - STATIC mp_obj_t pyb_input(void ) { return mp_obj_new_int(stdin_rx_chr()); } diff --git a/stmhal/pybstdio.c b/stmhal/pybstdio.c new file mode 100644 index 0000000000..95582d330e --- /dev/null +++ b/stmhal/pybstdio.c @@ -0,0 +1,54 @@ +#include + +#include "misc.h" +#include "mpconfig.h" +#include "qstr.h" +#include "misc.h" +#include "obj.h" +#include "pybstdio.h" +#include "storage.h" +#include "usb.h" +#include "usart.h" + +void stdout_tx_str(const char *str) { + if (pyb_usart_global_debug != PYB_USART_NONE) { + usart_tx_str(pyb_usart_global_debug, str); + } +#if defined(USE_HOST_MODE) && MICROPY_HW_HAS_LCD + lcd_print_str(str); +#endif + usb_vcp_send_str(str); +} + +void stdout_tx_strn(const char *str, uint len) { + if (pyb_usart_global_debug != PYB_USART_NONE) { + usart_tx_strn(pyb_usart_global_debug, str, len); + } +#if defined(USE_HOST_MODE) && MICROPY_HW_HAS_LCD + lcd_print_strn(str, len); +#endif + usb_vcp_send_strn(str, len); +} + +int stdin_rx_chr(void) { + for (;;) { +#if 0 +#ifdef USE_HOST_MODE + pyb_usb_host_process(); + int c = pyb_usb_host_get_keyboard(); + if (c != 0) { + return c; + } +#endif +#endif + if (usb_vcp_rx_num() != 0) { + return usb_vcp_rx_get(); + } else if (pyb_usart_global_debug != PYB_USART_NONE && usart_rx_any(pyb_usart_global_debug)) { + return usart_rx_char(pyb_usart_global_debug); + } + HAL_Delay(1); + if (storage_needs_flush()) { + storage_flush(); + } + } +} diff --git a/stmhal/pybstdio.h b/stmhal/pybstdio.h new file mode 100644 index 0000000000..7e10fc8658 --- /dev/null +++ b/stmhal/pybstdio.h @@ -0,0 +1,3 @@ +void stdout_tx_str(const char *str); +void stdout_tx_strn(const char *str, uint len); +int stdin_rx_chr(void); diff --git a/stmhal/pyexec.c b/stmhal/pyexec.c index f1f28abde0..7c19d45ca6 100644 --- a/stmhal/pyexec.c +++ b/stmhal/pyexec.c @@ -1,5 +1,4 @@ #include -#include #include #include @@ -19,201 +18,15 @@ #include "gc.h" #include "gccollect.h" #include "systick.h" +#include "pybstdio.h" +#include "readline.h" #include "pyexec.h" #include "storage.h" #include "usb.h" -#include "usart.h" pyexec_mode_kind_t pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL; STATIC bool repl_display_debugging_info = 0; -void stdout_tx_str(const char *str) { - if (pyb_usart_global_debug != PYB_USART_NONE) { - usart_tx_str(pyb_usart_global_debug, str); - } -#if defined(USE_HOST_MODE) && MICROPY_HW_HAS_LCD - lcd_print_str(str); -#endif - usb_vcp_send_str(str); -} - -void stdout_tx_strn(const char *str, uint len) { - if (pyb_usart_global_debug != PYB_USART_NONE) { - usart_tx_strn(pyb_usart_global_debug, str, len); - } -#if defined(USE_HOST_MODE) && MICROPY_HW_HAS_LCD - lcd_print_strn(str, len); -#endif - usb_vcp_send_strn(str, len); -} - -int stdin_rx_chr(void) { - for (;;) { -#if 0 -#ifdef USE_HOST_MODE - pyb_usb_host_process(); - int c = pyb_usb_host_get_keyboard(); - if (c != 0) { - return c; - } -#endif -#endif - if (usb_vcp_rx_num() != 0) { - return usb_vcp_rx_get(); - } else if (pyb_usart_global_debug != PYB_USART_NONE && usart_rx_any(pyb_usart_global_debug)) { - return usart_rx_char(pyb_usart_global_debug); - } - HAL_Delay(1); - if (storage_needs_flush()) { - storage_flush(); - } - } -} - -char *str_dup(const char *str) { - uint32_t len = strlen(str); - char *s2 = m_new(char, len + 1); - memcpy(s2, str, len); - s2[len] = 0; - return s2; -} - -#define READLINE_HIST_SIZE (8) - -static const char *readline_hist[READLINE_HIST_SIZE] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; - -int readline(vstr_t *line, const char *prompt) { - stdout_tx_str(prompt); - int orig_line_len = line->len; - int escape_seq = 0; - int hist_cur = -1; - int cursor_pos = orig_line_len; - for (;;) { - int c = stdin_rx_chr(); - int last_line_len = line->len; - int redraw_step_back = 0; - bool redraw_from_cursor = false; - int redraw_step_forward = 0; - if (escape_seq == 0) { - if (VCP_CHAR_CTRL_A <= c && c <= VCP_CHAR_CTRL_D && vstr_len(line) == orig_line_len) { - // control character with empty line - return c; - } else if (c == '\r') { - // newline - stdout_tx_str("\r\n"); - if (line->len > orig_line_len && (readline_hist[0] == NULL || strcmp(readline_hist[0], line->buf + orig_line_len) != 0)) { - // a line which is not empty and different from the last one - // so update the history - for (int i = READLINE_HIST_SIZE - 1; i > 0; i--) { - readline_hist[i] = readline_hist[i - 1]; - } - readline_hist[0] = str_dup(line->buf + orig_line_len); - } - return 0; - } else if (c == 27) { - // escape sequence - escape_seq = 1; - } else if (c == 127) { - // backspace - if (cursor_pos > orig_line_len) { - vstr_cut_out_bytes(line, cursor_pos - 1, 1); - // set redraw parameters - redraw_step_back = 1; - redraw_from_cursor = true; - } - } else if (32 <= c && c <= 126) { - // printable character - vstr_ins_char(line, cursor_pos, c); - // set redraw parameters - redraw_from_cursor = true; - redraw_step_forward = 1; - } - } else if (escape_seq == 1) { - if (c == '[') { - escape_seq = 2; - } else { - escape_seq = 0; - } - } else if (escape_seq == 2) { - escape_seq = 0; - if (c == 'A') { - // up arrow - if (hist_cur + 1 < READLINE_HIST_SIZE && readline_hist[hist_cur + 1] != NULL) { - // increase hist num - hist_cur += 1; - // set line to history - line->len = orig_line_len; - vstr_add_str(line, readline_hist[hist_cur]); - // set redraw parameters - redraw_step_back = cursor_pos - orig_line_len; - redraw_from_cursor = true; - redraw_step_forward = line->len - orig_line_len; - } - } else if (c == 'B') { - // down arrow - if (hist_cur >= 0) { - // decrease hist num - hist_cur -= 1; - // set line to history - vstr_cut_tail_bytes(line, line->len - orig_line_len); - if (hist_cur >= 0) { - vstr_add_str(line, readline_hist[hist_cur]); - } - // set redraw parameters - redraw_step_back = cursor_pos - orig_line_len; - redraw_from_cursor = true; - redraw_step_forward = line->len - orig_line_len; - } - } else if (c == 'C') { - // right arrow - if (cursor_pos < line->len) { - redraw_step_forward = 1; - } - } else if (c == 'D') { - // left arrow - if (cursor_pos > orig_line_len) { - redraw_step_back = 1; - } - } - } else { - escape_seq = 0; - } - - // redraw command prompt, efficiently - if (redraw_step_back > 0) { - for (int i = 0; i < redraw_step_back; i++) { - stdout_tx_str("\b"); - } - cursor_pos -= redraw_step_back; - } - if (redraw_from_cursor) { - if (line->len < last_line_len) { - // erase old chars - for (int i = cursor_pos; i < last_line_len; i++) { - stdout_tx_str(" "); - } - // step back - for (int i = cursor_pos; i < last_line_len; i++) { - stdout_tx_str("\b"); - } - } - // draw new chars - stdout_tx_strn(line->buf + cursor_pos, line->len - cursor_pos); - // move cursor forward if needed (already moved forward by length of line, so move it back) - for (int i = cursor_pos + redraw_step_forward; i < line->len; i++) { - stdout_tx_str("\b"); - } - cursor_pos += redraw_step_forward; - } else if (redraw_step_forward > 0) { - // draw over old chars to move cursor forwards - stdout_tx_strn(line->buf + cursor_pos, redraw_step_forward); - cursor_pos += redraw_step_forward; - } - - HAL_Delay(1); - } -} - // parses, compiles and executes the code in the lexer // frees the lexer before returning bool parse_compile_execute(mp_lexer_t *lex, mp_parse_input_kind_t input_kind, bool is_repl) { diff --git a/stmhal/readline.c b/stmhal/readline.c new file mode 100644 index 0000000000..ec13a269ac --- /dev/null +++ b/stmhal/readline.c @@ -0,0 +1,160 @@ +#include + +#include + +#include "misc.h" +#include "mpconfig.h" +#include "qstr.h" +#include "misc.h" +#include "obj.h" +#include "pybstdio.h" +#include "readline.h" +#include "usb.h" + +#define READLINE_HIST_SIZE (8) + +static const char *readline_hist[READLINE_HIST_SIZE] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; + +void readline_init(void) { + memset(readline_hist, 0, READLINE_HIST_SIZE * sizeof(const char*)); +} + +STATIC char *str_dup(const char *str) { + uint32_t len = strlen(str); + char *s2 = m_new(char, len + 1); + memcpy(s2, str, len); + s2[len] = 0; + return s2; +} + +int readline(vstr_t *line, const char *prompt) { + stdout_tx_str(prompt); + int orig_line_len = line->len; + int escape_seq = 0; + int hist_cur = -1; + int cursor_pos = orig_line_len; + for (;;) { + int c = stdin_rx_chr(); + int last_line_len = line->len; + int redraw_step_back = 0; + bool redraw_from_cursor = false; + int redraw_step_forward = 0; + if (escape_seq == 0) { + if (VCP_CHAR_CTRL_A <= c && c <= VCP_CHAR_CTRL_D && vstr_len(line) == orig_line_len) { + // control character with empty line + return c; + } else if (c == '\r') { + // newline + stdout_tx_str("\r\n"); + if (line->len > orig_line_len && (readline_hist[0] == NULL || strcmp(readline_hist[0], line->buf + orig_line_len) != 0)) { + // a line which is not empty and different from the last one + // so update the history + for (int i = READLINE_HIST_SIZE - 1; i > 0; i--) { + readline_hist[i] = readline_hist[i - 1]; + } + readline_hist[0] = str_dup(line->buf + orig_line_len); + } + return 0; + } else if (c == 27) { + // escape sequence + escape_seq = 1; + } else if (c == 127) { + // backspace + if (cursor_pos > orig_line_len) { + vstr_cut_out_bytes(line, cursor_pos - 1, 1); + // set redraw parameters + redraw_step_back = 1; + redraw_from_cursor = true; + } + } else if (32 <= c && c <= 126) { + // printable character + vstr_ins_char(line, cursor_pos, c); + // set redraw parameters + redraw_from_cursor = true; + redraw_step_forward = 1; + } + } else if (escape_seq == 1) { + if (c == '[') { + escape_seq = 2; + } else { + escape_seq = 0; + } + } else if (escape_seq == 2) { + escape_seq = 0; + if (c == 'A') { + // up arrow + if (hist_cur + 1 < READLINE_HIST_SIZE && readline_hist[hist_cur + 1] != NULL) { + // increase hist num + hist_cur += 1; + // set line to history + line->len = orig_line_len; + vstr_add_str(line, readline_hist[hist_cur]); + // set redraw parameters + redraw_step_back = cursor_pos - orig_line_len; + redraw_from_cursor = true; + redraw_step_forward = line->len - orig_line_len; + } + } else if (c == 'B') { + // down arrow + if (hist_cur >= 0) { + // decrease hist num + hist_cur -= 1; + // set line to history + vstr_cut_tail_bytes(line, line->len - orig_line_len); + if (hist_cur >= 0) { + vstr_add_str(line, readline_hist[hist_cur]); + } + // set redraw parameters + redraw_step_back = cursor_pos - orig_line_len; + redraw_from_cursor = true; + redraw_step_forward = line->len - orig_line_len; + } + } else if (c == 'C') { + // right arrow + if (cursor_pos < line->len) { + redraw_step_forward = 1; + } + } else if (c == 'D') { + // left arrow + if (cursor_pos > orig_line_len) { + redraw_step_back = 1; + } + } + } else { + escape_seq = 0; + } + + // redraw command prompt, efficiently + if (redraw_step_back > 0) { + for (int i = 0; i < redraw_step_back; i++) { + stdout_tx_str("\b"); + } + cursor_pos -= redraw_step_back; + } + if (redraw_from_cursor) { + if (line->len < last_line_len) { + // erase old chars + for (int i = cursor_pos; i < last_line_len; i++) { + stdout_tx_str(" "); + } + // step back + for (int i = cursor_pos; i < last_line_len; i++) { + stdout_tx_str("\b"); + } + } + // draw new chars + stdout_tx_strn(line->buf + cursor_pos, line->len - cursor_pos); + // move cursor forward if needed (already moved forward by length of line, so move it back) + for (int i = cursor_pos + redraw_step_forward; i < line->len; i++) { + stdout_tx_str("\b"); + } + cursor_pos += redraw_step_forward; + } else if (redraw_step_forward > 0) { + // draw over old chars to move cursor forwards + stdout_tx_strn(line->buf + cursor_pos, redraw_step_forward); + cursor_pos += redraw_step_forward; + } + + HAL_Delay(1); + } +} diff --git a/stmhal/readline.h b/stmhal/readline.h new file mode 100644 index 0000000000..6b1baee324 --- /dev/null +++ b/stmhal/readline.h @@ -0,0 +1,2 @@ +void readline_init(void); +int readline(vstr_t *line, const char *prompt);