bios: add terminal history
authorFranck Jullien <franck.jullien@gmail.com>
Tue, 28 Apr 2020 21:03:18 +0000 (23:03 +0200)
committerFranck Jullien <franck.jullien@gmail.com>
Fri, 1 May 2020 10:12:07 +0000 (12:12 +0200)
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
litex/soc/software/bios/main.c
litex/soc/software/bios/readline.c [new file with mode: 0644]
litex/soc/software/bios/readline.h [new file with mode: 0644]
litex/soc/software/bios/readline_simple.c [new file with mode: 0644]

index cc3b973aa5d2c73bc5ad5302b7776fa19b10ae17..5ede7cd7174f5a60673837f35796489f8be54e03 100755 (executable)
@@ -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
index b6501edbbb4210827ff5d76fa079d1fb96310056..368031f6a362ebcee6fb5349d99c4fedec63a4db 100644 (file)
@@ -10,6 +10,7 @@
 // This file is Copyright (c) 2018 Jean-François Nguyen <jf@lambdaconcept.fr>
 // This file is Copyright (c) 2018 Sergiusz Bazanski <q3k@q3k.org>
 // This file is Copyright (c) 2016 Tim 'mithro' Ansell <mithro@mithis.com>
+// This file is Copyright (c) 2020 Franck Jullien <franck.jullien@gmail.com>
 
 // 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 (file)
index 0000000..998ebc0
--- /dev/null
@@ -0,0 +1,331 @@
+// This file is Copyright (c) 2020 Franck Jullien <franck.jullien@gmail.com>
+//
+//     Largely inspired/copied from U-boot and Barebox projects wich are:
+//         Wolfgang Denk, DENX Software Engineering, <wd@denx.de>
+//         Sascha Hauer, Pengutronix, <s.hauer@pengutronix.de>
+//     cmdline-editing related codes from vivi
+//         Author: Janghoon Lyu <nandy@mizi.com>
+
+// SPDX-License-Identifier: BSD-Source-Code
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <console.h>
+#include <uart.h>
+
+#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 (file)
index 0000000..f1284c5
--- /dev/null
@@ -0,0 +1,83 @@
+#ifndef __READLINE_H__
+#define __READLINE_H__
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#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 (file)
index 0000000..bcf0621
--- /dev/null
@@ -0,0 +1,59 @@
+// This file is Copyright (c) 2013-2014 Sebastien Bourdeauducq <sb@m-labs.hk>
+// This file is Copyright (c) 2014-2019 Florent Kermarrec <florent@enjoy-digital.fr>
+
+// SPDX-License-Identifier: BSD-Source-Code
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <console.h>
+#include <uart.h>
+
+#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;
+}
+