From bc5a1986e237931554f7362e3bde44ba56fed5a7 Mon Sep 17 00:00:00 2001 From: Franck Jullien Date: Tue, 28 Apr 2020 23:03:18 +0200 Subject: [PATCH] bios: add terminal history Terminal history and characters parsing is done in readline.c. Passing TERM_NO_HIST disable terminal history. Passing TERM_MINI use a simple terminal implementation in order to save more space. --- litex/soc/software/bios/Makefile | 15 +- litex/soc/software/bios/main.c | 61 +--- litex/soc/software/bios/readline.c | 331 ++++++++++++++++++++++ litex/soc/software/bios/readline.h | 83 ++++++ litex/soc/software/bios/readline_simple.c | 59 ++++ 5 files changed, 500 insertions(+), 49 deletions(-) create mode 100644 litex/soc/software/bios/readline.c create mode 100644 litex/soc/software/bios/readline.h create mode 100644 litex/soc/software/bios/readline_simple.c diff --git a/litex/soc/software/bios/Makefile b/litex/soc/software/bios/Makefile index cc3b973a..5ede7cd7 100755 --- a/litex/soc/software/bios/Makefile +++ b/litex/soc/software/bios/Makefile @@ -2,7 +2,7 @@ include ../include/generated/variables.mak include $(SOC_DIRECTORY)/software/common.mak ifeq ($(CPU),blackparrot) -BP_LIBS = -L$(BP_EXTERNAL_DIR)/lib/gcc/riscv64-unknown-elf/8.3.0 +BP_LIBS = -L$(BP_EXTERNAL_DIR)/lib/gcc/riscv64-unknown-elf/8.3.0 BP_FLAGS = -lgcc endif # Permit TFTP_SERVER_PORT override from shell environment / command line @@ -12,6 +12,17 @@ endif OBJECTS=isr.o sdram.o sdcard.o main.o boot-helper.o boot.o +ifdef TERM_NO_HIST +CFLAGS += -DTERM_NO_HIST +endif + +ifdef TERM_MINI +CFLAGS += -DTERM_MINI +OBJECTS += readline_simple.o +else +OBJECTS += readline.o +endif + all: bios.bin $(PYTHON) -m litex.soc.software.memusage bios.elf $(CURDIR)/../include/generated/regions.ld $(TRIPLE) @@ -39,7 +50,7 @@ bios.elf: $(BIOS_DIRECTORY)/linker.ld $(OBJECTS) $(BP_LIBS) \ -lnet -lbase-nofloat -lcompiler_rt \ $(BP_FLAGS) - + ifneq ($(OS),Windows_NT) chmod -x $@ endif diff --git a/litex/soc/software/bios/main.c b/litex/soc/software/bios/main.c index b6501edb..368031f6 100644 --- a/litex/soc/software/bios/main.c +++ b/litex/soc/software/bios/main.c @@ -10,6 +10,7 @@ // This file is Copyright (c) 2018 Jean-François Nguyen // This file is Copyright (c) 2018 Sergiusz Bazanski // This file is Copyright (c) 2016 Tim 'mithro' Ansell +// This file is Copyright (c) 2020 Franck Jullien // License: BSD @@ -42,6 +43,7 @@ #include "sdram.h" #include "sdcard.h" #include "boot.h" +#include "readline.h" /* General address space functions */ @@ -548,48 +550,6 @@ static void crcbios(void) } } -static void readstr(char *s, int size) -{ - static char skip = 0; - char c[2]; - int ptr; - - c[1] = 0; - ptr = 0; - while(1) { - c[0] = readchar(); - if (c[0] == skip) - continue; - skip = 0; - switch(c[0]) { - case 0x7f: - case 0x08: - if(ptr > 0) { - ptr--; - putsnonl("\x08 \x08"); - } - break; - case 0x07: - break; - case '\r': - skip = '\n'; - s[ptr] = 0x00; - putsnonl("\n"); - return; - case '\n': - skip = '\r'; - s[ptr] = 0x00; - putsnonl("\n"); - return; - default: - putsnonl(c); - s[ptr] = c[0]; - ptr++; - break; - } - } -} - static void boot_sequence(void) { if(serialboot()) { @@ -614,7 +574,7 @@ static void boot_sequence(void) int main(int i, char **c) { - char buffer[64]; + char buffer[CMD_LINE_BUFFER_SIZE]; int sdr_ok; #ifdef CONFIG_CPU_HAS_INTERRUPT irq_setmask(0); @@ -696,10 +656,17 @@ int main(int i, char **c) } printf("--============= \e[1mConsole\e[0m ================--\n"); - while(1) { - putsnonl("\e[92;1mlitex\e[0m> "); - readstr(buffer, 64); - do_command(buffer); +#if !defined(TERM_MINI) && !defined(TERM_NO_HIST) + hist_init(); +#endif + printf("\n%s", PROMPT); + while(1) { + readline(buffer, CMD_LINE_BUFFER_SIZE); + if (buffer[0] != 0) { + printf("\n"); + do_command(buffer); + } + printf("\n%s", PROMPT); } return 0; } diff --git a/litex/soc/software/bios/readline.c b/litex/soc/software/bios/readline.c new file mode 100644 index 00000000..998ebc0e --- /dev/null +++ b/litex/soc/software/bios/readline.c @@ -0,0 +1,331 @@ +// This file is Copyright (c) 2020 Franck Jullien +// +// Largely inspired/copied from U-boot and Barebox projects wich are: +// Wolfgang Denk, DENX Software Engineering, +// Sascha Hauer, Pengutronix, +// cmdline-editing related codes from vivi +// Author: Janghoon Lyu + +// SPDX-License-Identifier: BSD-Source-Code + +#include +#include +#include +#include + +#include +#include + +#include "readline.h" + +#ifndef TERM_NO_HIST +static int hist_max = 0; +static int hist_add_idx = 0; +static int hist_cur = 0; +static int hist_num = 0; +static char hist_lines[HIST_MAX][CMD_LINE_BUFFER_SIZE]; +#endif + +#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) + +static const struct esc_cmds esccmds[] = { + {"OA", KEY_UP}, // cursor key Up + {"OB", KEY_DOWN}, // cursor key Down + {"OC", KEY_RIGHT}, // Cursor Key Right + {"OD", KEY_LEFT}, // cursor key Left + {"OH", KEY_HOME}, // Cursor Key Home + {"OF", KEY_END}, // Cursor Key End + {"[A", KEY_UP}, // cursor key Up + {"[B", KEY_DOWN}, // cursor key Down + {"[C", KEY_RIGHT}, // Cursor Key Right + {"[D", KEY_LEFT}, // cursor key Left + {"[H", KEY_HOME}, // Cursor Key Home + {"[F", KEY_END}, // Cursor Key End + {"[1~", KEY_HOME}, // Cursor Key Home + {"[2~", KEY_INSERT}, // Cursor Key Insert + {"[3~", KEY_DEL}, // Cursor Key Delete + {"[4~", KEY_END}, // Cursor Key End + {"[5~", KEY_PAGEUP}, // Cursor Key Page Up + {"[6~", KEY_PAGEDOWN},// Cursor Key Page Down +}; + +static int read_key(void) +{ + char c; + char esc[5]; + c = readchar(); + + if (c == 27) { + int i = 0; + esc[i++] = readchar(); + esc[i++] = readchar(); + if (isdigit(esc[1])) { + while(1) { + esc[i] = readchar(); + if (esc[i++] == '~') + break; + if (i == ARRAY_SIZE(esc)) + return -1; + } + } + esc[i] = 0; + for (i = 0; i < ARRAY_SIZE(esccmds); i++){ + if (!strcmp(esc, esccmds[i].seq)) + return esccmds[i].val; + } + return -1; + } + return c; +} + +#ifndef TERM_NO_HIST +static void cread_add_to_hist(char *line) +{ + strcpy(&hist_lines[hist_add_idx][0], line); + + if (++hist_add_idx >= HIST_MAX) + hist_add_idx = 0; + + if (hist_add_idx > hist_max) + hist_max = hist_add_idx; + + hist_num++; +} + +static char* hist_prev(void) +{ + char *ret; + int old_cur; + + if (hist_cur < 0) + return NULL; + + old_cur = hist_cur; + if (--hist_cur < 0) + hist_cur = hist_max; + + if (hist_cur == hist_add_idx) { + hist_cur = old_cur; + ret = NULL; + } else { + ret = &hist_lines[hist_cur][0]; + } + + return ret; +} + +static char* hist_next(void) +{ + char *ret; + + if (hist_cur < 0) + return NULL; + + if (hist_cur == hist_add_idx) + return NULL; + + if (++hist_cur > hist_max) + hist_cur = 0; + + if (hist_cur == hist_add_idx) + ret = ""; + else + ret = &hist_lines[hist_cur][0]; + + return ret; +} + +void hist_init(void) +{ + int i; + + hist_max = 0; + hist_add_idx = 0; + hist_cur = -1; + hist_num = 0; + + for (i = 0; i < HIST_MAX; i++) + hist_lines[i][0] = '\0'; +} +#endif + +static void cread_add_char(char ichar, int insert, unsigned long *num, + unsigned long *eol_num, char *buf, unsigned long len) +{ + unsigned long wlen; + + if (insert || *num == *eol_num) { + if (*eol_num > len - 1) { + getcmd_cbeep(); + return; + } + (*eol_num)++; + } + + if (insert) { + wlen = *eol_num - *num; + if (wlen > 1) { + memmove(&buf[*num+1], &buf[*num], wlen-1); + } + + buf[*num] = ichar; + putnstr(buf + *num, wlen); + (*num)++; + while (--wlen) { + getcmd_putch(CTL_BACKSPACE); + } + } else { + /* echo the character */ + wlen = 1; + buf[*num] = ichar; + putnstr(buf + *num, wlen); + (*num)++; + } +} + +int readline(char *buf, int len) +{ + unsigned long num = 0; + unsigned long eol_num = 0; + unsigned long wlen; + int insert = 1; + char ichar; + + while (1) { + + ichar = read_key(); + + if ((ichar == '\n') || (ichar == '\r')) + break; + + switch (ichar) { + case '\t': + break; + + case KEY_HOME: + BEGINNING_OF_LINE(); + break; + case CTL_CH('c'): /* ^C - break */ + *buf = 0; /* discard input */ + return -1; + break; + case KEY_RIGHT: + if (num < eol_num) { + getcmd_putch(buf[num]); + num++; + } + break; + case KEY_LEFT: + if (num) { + getcmd_putch(CTL_BACKSPACE); + num--; + } + break; + case CTL_CH('d'): + if (num < eol_num) { + wlen = eol_num - num - 1; + if (wlen) { + memmove(&buf[num], &buf[num+1], wlen); + putnstr(buf + num, (int)wlen); + } + + getcmd_putch(' '); + do { + getcmd_putch(CTL_BACKSPACE); + } while (wlen--); + eol_num--; + } + break; + case KEY_ERASE_TO_EOL: + ERASE_TO_EOL(); + break; + case KEY_REFRESH_TO_EOL: + case KEY_END: + REFRESH_TO_EOL(); + break; + case KEY_INSERT: + insert = !insert; + break; + case KEY_ERASE_LINE: + BEGINNING_OF_LINE(); + ERASE_TO_EOL(); + break; + case DEL: + case KEY_DEL7: + case 8: + if (num) { + wlen = eol_num - num; + num--; + memmove(buf + num, buf + num + 1, wlen); + getcmd_putch(CTL_BACKSPACE); + putnstr(buf + num, (int)wlen); + getcmd_putch(' '); + do { + getcmd_putch(CTL_BACKSPACE); + } while (wlen--); + eol_num--; + } + break; + case KEY_DEL: + if (num < eol_num) { + wlen = eol_num - num; + memmove(buf + num, buf + num + 1, wlen); + putnstr(buf + num, (int)(wlen - 1)); + getcmd_putch(' '); + do { + getcmd_putch(CTL_BACKSPACE); + } while (--wlen); + eol_num--; + } + break; + case KEY_UP: + case KEY_DOWN: + { +#ifndef TERM_NO_HIST + char * hline; + if (ichar == KEY_UP) + hline = hist_prev(); + else + hline = hist_next(); + + if (!hline) { + getcmd_cbeep(); + break; + } + + /* nuke the current line */ + /* first, go home */ + BEGINNING_OF_LINE(); + + /* erase to end of line */ + ERASE_TO_EOL(); + + /* copy new line into place and display */ + strcpy(buf, hline); + eol_num = strlen(buf); + REFRESH_TO_EOL(); +#endif + break; + } + + default: + if (isascii(ichar) && isprint(ichar)) + cread_add_char (ichar, insert, &num, &eol_num, buf, len); + break; + } + } + + len = eol_num; + buf[eol_num] = '\0'; + +#ifndef TERM_NO_HIST + if (buf[0] && buf[0] != CREAD_HIST_CHAR) + cread_add_to_hist(buf); + hist_cur = hist_add_idx; +#endif + + num = 0; + eol_num = 0; + + return len; +} diff --git a/litex/soc/software/bios/readline.h b/litex/soc/software/bios/readline.h new file mode 100644 index 00000000..f1284c5d --- /dev/null +++ b/litex/soc/software/bios/readline.h @@ -0,0 +1,83 @@ +#ifndef __READLINE_H__ +#define __READLINE_H__ + +#include +#include + +#define CMD_LINE_BUFFER_SIZE 64 + +#define PROMPT "\e[92;1mlitex\e[0m> " + +#define ESC 27 + +struct esc_cmds { + const char *seq; + char val; +}; + +#define CTL_CH(c) ((c) - 'a' + 1) + +/* Misc. non-Ascii keys */ +#define KEY_UP CTL_CH('p') /* cursor key Up */ +#define KEY_DOWN CTL_CH('n') /* cursor key Down */ +#define KEY_RIGHT CTL_CH('f') /* Cursor Key Right */ +#define KEY_LEFT CTL_CH('b') /* cursor key Left */ +#define KEY_HOME CTL_CH('a') /* Cursor Key Home */ +#define KEY_ERASE_TO_EOL CTL_CH('k') +#define KEY_REFRESH_TO_EOL CTL_CH('e') +#define KEY_ERASE_LINE CTL_CH('x') +#define KEY_INSERT CTL_CH('o') +#define KEY_CLEAR_SCREEN CTL_CH('l') +#define KEY_DEL7 127 +#define KEY_END 133 /* Cursor Key End */ +#define KEY_PAGEUP 135 /* Cursor Key Page Up */ +#define KEY_PAGEDOWN 136 /* Cursor Key Page Down */ +#define KEY_DEL 137 /* Cursor Key Del */ + +#define MAX_CMDBUF_SIZE 256 + +#define CTL_BACKSPACE ('\b') +#define DEL 255 +#define DEL7 127 +#define CREAD_HIST_CHAR ('!') + +#define HIST_MAX 10 + +#define putnstr(str,n) do { \ + printf ("%.*s", n, str); \ + } while (0) + +#define getcmd_putch(ch) putchar(ch) +#define getcmd_cbeep() getcmd_putch('\a') +#define ANSI_CLEAR_SCREEN "\e[2J\e[;H" + +#define BEGINNING_OF_LINE() { \ + while (num) { \ + getcmd_putch(CTL_BACKSPACE); \ + num--; \ + } \ +} + +#define ERASE_TO_EOL() { \ + if (num < eol_num) { \ + int t; \ + for (t = num; t < eol_num; t++) \ + getcmd_putch(' '); \ + while (t-- > num) \ + getcmd_putch(CTL_BACKSPACE); \ + eol_num = num; \ + } \ +} + +#define REFRESH_TO_EOL() { \ + if (num < eol_num) { \ + wlen = eol_num - num; \ + putnstr(buf + num, (int)wlen); \ + num = eol_num; \ + } \ +} + +int readline(char *buf, int len); +void hist_init(void); + +#endif /* READLINE_H_ */ diff --git a/litex/soc/software/bios/readline_simple.c b/litex/soc/software/bios/readline_simple.c new file mode 100644 index 00000000..bcf06213 --- /dev/null +++ b/litex/soc/software/bios/readline_simple.c @@ -0,0 +1,59 @@ +// This file is Copyright (c) 2013-2014 Sebastien Bourdeauducq +// This file is Copyright (c) 2014-2019 Florent Kermarrec + +// SPDX-License-Identifier: BSD-Source-Code + +#include +#include +#include +#include + +#include +#include + +#include "readline.h" + +int readline(char *s, int size) +{ + static char skip = 0; + char c[2]; + int ptr; + + c[1] = 0; + ptr = 0; + while(1) { + c[0] = readchar(); + if (c[0] == skip) + continue; + skip = 0; + switch(c[0]) { + case 0x7f: + case 0x08: + if(ptr > 0) { + ptr--; + putsnonl("\x08 \x08"); + } + break; + case 0x07: + break; + case '\r': + skip = '\n'; + s[ptr] = 0x00; + putsnonl("\n"); + return 0; + case '\n': + skip = '\r'; + s[ptr] = 0x00; + putsnonl("\n"); + return 0; + default: + putsnonl(c); + s[ptr] = c[0]; + ptr++; + break; + } + } + + return 0; +} + -- 2.30.2