diff options
author | XiaJin-RiVAI <jin.xia@rivai.ai> | 2022-09-26 14:32:45 +0800 |
---|---|---|
committer | XiaJin-RiVAI <jin.xia@rivai.ai> | 2022-09-26 14:32:45 +0800 |
commit | 6b9c49193ae41398fbd0558af9d91e47436941a8 (patch) | |
tree | 9afddc99a50c09ce722943c1f50c24f6e5fa7e1a /riscv/interactive.cc | |
parent | 7ab4caa4a01c825f0be67137f079e8882c9998dc (diff) | |
download | spike-6b9c49193ae41398fbd0558af9d91e47436941a8.zip spike-6b9c49193ae41398fbd0558af9d91e47436941a8.tar.gz spike-6b9c49193ae41398fbd0558af9d91e47436941a8.tar.bz2 |
Update interactive.cc
support arrow/home/end keys in debug mode
support the arrow/home/end keys to move the cursor and trace back the history command in the debug mode
Diffstat (limited to 'riscv/interactive.cc')
-rw-r--r-- | riscv/interactive.cc | 205 |
1 files changed, 189 insertions, 16 deletions
diff --git a/riscv/interactive.cc b/riscv/interactive.cc index c6a9f80..df83580 100644 --- a/riscv/interactive.cc +++ b/riscv/interactive.cc @@ -21,12 +21,40 @@ #include <math.h> #define MAX_CMD_STR 40 // maximum possible size of a command line +#define BITS_PER_CHAR 8 #define STR_(X) #X // these definitions allow to use a macro as a string #define STR(X) STR_(X) DECLARE_TRAP(-1, interactive) +static std::vector<std::string> history_commands; + +// if input an arrow/home key, there will be a 3/4-key input sequence, +// so we use an uint32_t to buffer it +typedef uint32_t keybuffer_t; + +enum KEYCODE +{ + KEYCODE_HEADER0 = 0x1b, + KEYCODE_HEADER1 = 0x1b5b, + KEYCODE_LEFT = 0x1b5b44, + KEYCODE_RIGHT = 0x1b5b43, + KEYCODE_UP = 0x1b5b41, + KEYCODE_DOWN = 0x1b5b42, + KEYCODE_HOME0 = 0x1b5b48, + KEYCODE_HOME1_0 = 0x1b5b31, + KEYCODE_HOME1_1 = 0x1b5b317e, + KEYCODE_END0 = 0x1b5b46, + KEYCODE_END1_0 = 0x1b5b34, + KEYCODE_END1_1 = 0x1b5b347e, + KEYCODE_BACKSPACE0 = 0x8, + KEYCODE_BACKSPACE1_0 = 0x1b5b33, + KEYCODE_BACKSPACE1_1 = 0x1b5b337e, + KEYCODE_BACKSPACE2 = 0x7f, + KEYCODE_ENTER = '\n', +}; + processor_t *sim_t::get_core(const std::string& i) { char *ptr; @@ -36,30 +64,177 @@ processor_t *sim_t::get_core(const std::string& i) return get_core(p); } +static void clear_str(bool noncanonical, int fd, std::string target_str) +{ + if (noncanonical) + { + std::string clear_motion; + clear_motion += '\r'; + for (unsigned i = 0; i < target_str.size(); i++) + { + clear_motion += ' '; + } + clear_motion += '\r'; + if (write(fd, clear_motion.c_str(), clear_motion.size() + 1)); // shut up gcc + } +} + +static void send_key(bool noncanonical, int fd, keybuffer_t key_code, const int len) +{ + if (noncanonical) + { + std::string key_motion; + for (int i = len - 1; i >= 0; i--) + { + key_motion += (char) ((key_code >> (i * BITS_PER_CHAR)) & 0xff); + } + if (write(fd, key_motion.c_str(), len) != len); // shut up gcc + } +} + static std::string readline(int fd) { struct termios tios; + // try to make sure the terminal is noncanonical and nonecho + if (tcgetattr(fd, &tios) == 0) + { + tios.c_lflag &= (~ICANON); + tios.c_lflag &= (~ECHO); + tcsetattr(fd, TCSANOW, &tios); + } bool noncanonical = tcgetattr(fd, &tios) == 0 && (tios.c_lflag & ICANON) == 0; - std::string s; + std::string s_head = std::string("(spike) "); + std::string s = s_head; + keybuffer_t key_buffer = 0; + // index for up/down arrow + size_t history_index = 0; + // position for left/right arrow + size_t cursor_pos = s.size(); + const size_t initial_s_len = cursor_pos; + std::cerr << s << std::flush; for (char ch; read(fd, &ch, 1) == 1; ) { - if (ch == '\x7f') + uint32_t keycode = key_buffer << BITS_PER_CHAR | ch; + switch (keycode) { - if (s.empty()) - continue; - s.erase(s.end()-1); - - if (noncanonical && write(fd, "\b \b", 3) != 3) {} + // the partial keycode, add to the key_buffer + case KEYCODE_HEADER0: + case KEYCODE_HEADER1: + case KEYCODE_HOME1_0: + case KEYCODE_END1_0: + case KEYCODE_BACKSPACE1_0: + key_buffer = keycode; + break; + // for backspace key + case KEYCODE_BACKSPACE0: + case KEYCODE_BACKSPACE1_1: + case KEYCODE_BACKSPACE2: + if (cursor_pos <= initial_s_len) + continue; + clear_str(noncanonical, fd, s); + cursor_pos--; + s.erase(cursor_pos, 1); + if (noncanonical && write(fd, s.c_str(), s.size() + 1) != 1); // shut up gcc + // move cursor by left arrow key + for (unsigned i = 0; i < s.size() - cursor_pos; i++) { + send_key(noncanonical, fd, KEYCODE_LEFT, 3); + } + key_buffer = 0; + break; + case KEYCODE_HOME0: + case KEYCODE_HOME1_1: + // move cursor by left arrow key + for (unsigned i = 0; i < cursor_pos - initial_s_len; i++) { + send_key(noncanonical, fd, KEYCODE_LEFT, 3); + } + cursor_pos = initial_s_len; + key_buffer = 0; + break; + case KEYCODE_END0: + case KEYCODE_END1_1: + // move cursor by right arrow key + for (unsigned i = 0; i < s.size() - cursor_pos; i++) { + send_key(noncanonical, fd, KEYCODE_RIGHT, 3); + } + cursor_pos = s.size(); + key_buffer = 0; + break; + case KEYCODE_UP: + // up arrow + if (history_commands.size() > 0) { + clear_str(noncanonical, fd, s); + history_index = std::min(history_commands.size(), history_index + 1); + s = history_commands[history_commands.size() - history_index]; + if (noncanonical && write(fd, s.c_str(), s.size() + 1)) + ; // shut up gcc + cursor_pos = s.size(); + } + key_buffer = 0; + break; + case KEYCODE_DOWN: + // down arrow + if (history_commands.size() > 0) { + clear_str(noncanonical, fd, s); + history_index = std::max(0, (int)history_index - 1); + if (history_index == 0) { + s = s_head; + } else { + s = history_commands[history_commands.size() - history_index]; + } + if (noncanonical && write(fd, s.c_str(), s.size() + 1)) + ; // shut up gcc + cursor_pos = s.size(); + } + key_buffer = 0; + break; + case KEYCODE_LEFT: + if (s.size() > initial_s_len) { + cursor_pos = cursor_pos - 1; + if ((int)cursor_pos < (int)initial_s_len) { + cursor_pos = initial_s_len; + } else { + send_key(noncanonical, fd, KEYCODE_LEFT, 3); + } + } + key_buffer = 0; + break; + case KEYCODE_RIGHT: + if (s.size() > initial_s_len) { + cursor_pos = cursor_pos + 1; + if (cursor_pos > s.size()) { + cursor_pos = s.size(); + } else { + send_key(noncanonical, fd, KEYCODE_RIGHT, 3); + } + } + key_buffer = 0; + break; + case KEYCODE_ENTER: + if (noncanonical && write(fd, &ch, 1) != 1); // shut up gcc + if (s.size() > initial_s_len && (history_commands.size() == 0 || s != history_commands[history_commands.size() - 1])) { + history_commands.push_back(s); + } + return s.substr(initial_s_len); + default: + DEFAULT_KEY: + // unknown buffered key, do nothing + if (key_buffer != 0) { + key_buffer = 0; + break; + } + clear_str(noncanonical, fd, s); + s.insert(cursor_pos, 1, ch); + cursor_pos++; + if (noncanonical && write(fd, s.c_str(), s.size() + 1) != 1); // shut up gcc + // send left arrow key to move cursor + for (unsigned i = 0; i < s.size() - cursor_pos; i++) { + send_key(noncanonical, fd, KEYCODE_LEFT, 3); + } + break; } - else if (noncanonical && write(fd, &ch, 1) != 1) {} - - if (ch == '\n') - break; - if (ch != '\x7f') - s += ch; } - return s; + return s.substr(initial_s_len); } #ifdef HAVE_BOOST_ASIO @@ -90,7 +265,6 @@ std::string sim_t::rin(boost::asio::streambuf *bout_ptr) { // output goes to socket sout_.rdbuf(bout_ptr); } else { // if we are not listening on a socket, get commands from terminal - std::cerr << ": " << std::flush; s = readline(2); // 2 is stderr, but when doing reads it reverts to stdin // output goes to stderr sout_.rdbuf(std::cerr.rdbuf()); @@ -159,7 +333,6 @@ void sim_t::interactive() #ifdef HAVE_BOOST_ASIO s = rin(&bout); // get command string from socket or terminal #else - std::cerr << ": " << std::flush; s = readline(2); // 2 is stderr, but when doing reads it reverts to stdin #endif } |