aboutsummaryrefslogtreecommitdiff
path: root/riscv/interactive.cc
diff options
context:
space:
mode:
authorAndrew Waterman <andrew@sifive.com>2022-10-03 15:53:01 -0700
committerGitHub <noreply@github.com>2022-10-03 15:53:01 -0700
commit26bb163c0c7f1d2f27dec18a446ce8419df13b46 (patch)
tree3bc182e93e933798227314ae2de70f84c943b60d /riscv/interactive.cc
parent37003b120b1ea704918022216c717e13c391be6c (diff)
parent6b9c49193ae41398fbd0558af9d91e47436941a8 (diff)
downloadspike-26bb163c0c7f1d2f27dec18a446ce8419df13b46.zip
spike-26bb163c0c7f1d2f27dec18a446ce8419df13b46.tar.gz
spike-26bb163c0c7f1d2f27dec18a446ce8419df13b46.tar.bz2
Merge pull request #1099 from XiaJin-RiVAI/master
support arrow/home/end keys in debug mode
Diffstat (limited to 'riscv/interactive.cc')
-rw-r--r--riscv/interactive.cc205
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
}