diff --git a/Makefile b/Makefile index fbe636d..db568ce 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ all: create_build_dir $(VERSION_HEADER) $(SUBDIRS) $(BUILDDIR)rtlplayground.bin create_build_dir: mkdir -p $(BUILDDIR) -SRCS = rtlplayground.c rtl837x_flash.c rtl837x_leds.c rtl837x_phy.c rtl837x_port.c cmd_parser.c html_data.c rtl837x_igmp.c rtl837x_stp.c rtl837x_pins.c dhcp.c machine.c +SRCS = rtlplayground.c rtl837x_flash.c rtl837x_leds.c rtl837x_phy.c rtl837x_port.c cmd_parser.c html_data.c rtl837x_igmp.c rtl837x_stp.c rtl837x_pins.c dhcp.c machine.c cmd_editor.c OBJS = ${SRCS:%.c=$(BUILDDIR)%.rel} OBJS += uip/$(BUILDDIR)/timer.rel uip/$(BUILDDIR)/uip-fw.rel uip/$(BUILDDIR)/uip-neighbor.rel uip/$(BUILDDIR)/uip-split.rel uip/$(BUILDDIR)/uip.rel uip/$(BUILDDIR)/uip_arp.rel uip/$(BUILDDIR)/uiplib.rel httpd/$(BUILDDIR)/httpd.rel httpd/$(BUILDDIR)/page_impl.rel diff --git a/cmd_editor.c b/cmd_editor.c new file mode 100644 index 0000000..43d0c0c --- /dev/null +++ b/cmd_editor.c @@ -0,0 +1,208 @@ +#include "cmd_parser.h" +#include "machine.h" + +#pragma codeseg BANK2 +#pragma constseg BANK2 + +// Position in the serial buffer +__xdata uint8_t l; +// Properties of currently edited command line in cmd_buffer[CMD_BUF_SIZE] +__xdata uint8_t cursor; +__xdata uint8_t cmd_line_len; +__xdata uint8_t current_cmdline[CMD_BUF_SIZE]; +__xdata uint16_t history_editptr; + + +extern __xdata uint8_t cmd_history[CMD_HISTORY_SIZE]; +extern __xdata uint16_t cmd_history_ptr; + +void cmd_editor_init(void) __banked +{ + l = sbuf_ptr; // We have printed out entered characters until l + cursor = 0; + cmd_line_len = 0; + cmd_available = 0; + history_editptr = 0xffff; +} + +/* + * Allows editing the current command line held in cmd_buffer[CMD_BUF_SIZE] by + * identifying new characters typed or up to 4-byte escape sequences in the + * serial buffer ring sbuf[SBUF_SIZE]. + * Upon detecting new characters or escape sequences, the cmd_buffer and the + * representation of the command line in the terminal are updated. + * To debug, the easist is to interpose a tty-interceptor between the physical + * serial device and a logical one created by interceptty: + * sudo interceptty -s 'ispeed 115200 ospeed 115200' /dev/ttyUSB0 /dev/tmpS + * picocom -b 115200 /dev/tmpS + */ +void cmd_edit(void) __banked +{ + while (l != sbuf_ptr) { + if (sbuf[l] >= ' ' && sbuf[l] < 127) { // A printable character, copy to command line + if (cmd_line_len >= CMD_BUF_SIZE) + continue; + write_char(sbuf[l]); + // Shift buffer to right + for (uint8_t i = cmd_line_len; i > cursor; i--) + cmd_buffer[i] = cmd_buffer[i-1]; + // Insert char in comand buffer + cmd_buffer[cursor++] = sbuf[l]; + cmd_line_len++; + // Print rest of line + for (uint8_t i = cursor; i < cmd_line_len; i++) + write_char(cmd_buffer[i]); + // Move backwards + for (uint8_t i = cursor; i < cmd_line_len; i++) + write_char('\010'); // BS works like cursor-left + } else if (sbuf[l] == '\033') { // ESC-Sequence + // Wait until we have at least 3 characters including the ESC character in the serial buffer + if (((sbuf_ptr + SBUF_SIZE - l) & SBUF_MASK) < 3) + continue; + if (((sbuf_ptr > l ? sbuf_ptr - l : SBUF_SIZE + sbuf_ptr - l) >= 4) + && sbuf[l] == '\033' && sbuf[(l + 1) & SBUF_MASK] == '[' && sbuf[(l + 2) & SBUF_MASK] == '3' && sbuf[(l + 3) & SBUF_MASK] == '~') { // DEL + if (cursor < cmd_line_len) { + write_char('\033'); write_char('['); write_char('1'); write_char('P'); // Delete to end of line + cmd_line_len--; + for (uint8_t i = cursor; i < cmd_line_len; i++) { + cmd_buffer[i] = cmd_buffer[i+1]; + write_char(cmd_buffer[i]); + } + for (uint8_t i = cursor; i < cmd_line_len; i++) + write_char('\010'); + } + l += 4; + l &= SBUF_MASK; + continue; + } else if (sbuf[l] == '\033' && sbuf[(l + 1) & SBUF_MASK] == '[' && sbuf[(l + 2) & SBUF_MASK] == 'D') { // + if (cursor) { + write_char('\010'); // BS works like cursor-left + cursor--; + } + l += 3; + l &= SBUF_MASK; + continue; + } else if (sbuf[l] == '\033' && sbuf[(l + 1) & SBUF_MASK] == '[' && sbuf[(l + 2) & SBUF_MASK] == 'C') { // + if (cursor < cmd_line_len) { + write_char('\033'); write_char('['); write_char('C'); + cursor++; + } + l += 3; + l &= SBUF_MASK; + continue; + } else if (sbuf[l] == '\033' && sbuf[(l + 1) & SBUF_MASK] == '[' && sbuf[(l + 2) & SBUF_MASK] == 'A') { // + for (uint8_t i = 0; i < cmd_line_len; i++) + current_cmdline[i] = cmd_buffer[i]; + current_cmdline[cmd_line_len] = 0; + __xdata uint16_t p; + if (history_editptr == 0xffff) + p = (cmd_history_ptr - 2) & CMD_HISTORY_MASK; + else + p = history_editptr; + // Move cursor to beginning of line + write_char('\033'); write_char('['); itoa(cursor + 2); write_char('D'); + cursor = 0; + while (cmd_history[p] && cmd_history[p] != '\n') { + cursor++; + p--; + p &= CMD_HISTORY_MASK; + } + history_editptr = (p - 1) & CMD_HISTORY_MASK; + p = (p + 1) & CMD_HISTORY_MASK; + if (cursor) { + print_string("\033[2K> "); // Clear entire line: ^[[2K and print new prompt + for (uint8_t i = 0; i < cursor; i++) { + cmd_buffer[i] = cmd_history[p]; + write_char(cmd_buffer[i]); + p = (p+1) & CMD_HISTORY_MASK; + } + cmd_line_len = cursor; + } else { + print_string("\033[2C"); // Move 2 right to start of editing space + } + l += 3; + l &= SBUF_MASK; + continue; + } else if (sbuf[l] == '\033' && sbuf[(l + 1) & SBUF_MASK] == '[' && sbuf[(l + 2) & SBUF_MASK] == 'B') { // + if (history_editptr != 0xffff) { + __xdata uint16_t p = (history_editptr + 2) & CMD_HISTORY_MASK; + // Move cursor to beginning of line + write_char('\033'); write_char('['); itoa(cursor + 2); write_char('D'); + print_string("\033[2K> "); // Clear entire line: ^[[2K and print new prompt + uint8_t i = 0; + while (cmd_history[p] && cmd_history[p] != '\n') { + p = (p + 1) & CMD_HISTORY_MASK; + } + p = (p + 1) & CMD_HISTORY_MASK; + while (cmd_history[p] && cmd_history[p] != '\n') { + cmd_buffer[i] = cmd_history[p]; + write_char(cmd_buffer[i++]); + p = (p + 1) & CMD_HISTORY_MASK; + } + history_editptr = (p - 1) & CMD_HISTORY_MASK; + if (!i) { + if (current_cmdline[i]) { + while (current_cmdline[i]) { + cmd_buffer[i] = current_cmdline[i]; + write_char(cmd_buffer[i++]); + } + } else { + write_char('\033'); write_char('['); write_char('C'); + } + history_editptr = 0xffff; + // Move cursor right + } + cmd_line_len = i; + cursor = 0; + // Move cursor again to beginning of line + write_char('\033'); write_char('['); itoa(i); write_char('D'); + } else { + // If we are at the last entry of the history, just move the cursor to the end of the line + if (cursor < cmd_line_len) { + write_char('\033'); write_char('['); itoa(cmd_line_len - cursor); write_char('C'); + } + cursor = cmd_line_len; + } + l += 3; + l &= SBUF_MASK; + continue; + } else { // An unknown or not yet complete Escape sequence: wait + continue; + } + } else if (sbuf[l] == 127) { // Backspace + if (cursor > 0) { + write_char('\010'); + for (uint8_t i = cursor; i < cmd_line_len; i++) + write_char(cmd_buffer[i]); + write_char(' '); // Overwrite end of line + // Move backwards n steps: + for (uint8_t i = cursor; i <= cmd_line_len; i++) + write_char('\010'); + cursor--; + for (uint8_t i = cursor; i <= cmd_line_len; i++) + cmd_buffer[i] = cmd_buffer[i+1]; + cmd_line_len--; + } + } + // If the command buffer is currently in use, we cannot copy to it + if (cmd_available) + break; + // Check whether return was pressed: + if (sbuf[l] == '\n' || sbuf[l] == '\r') { + write_char('\n'); + cmd_buffer[cmd_line_len] = '\0'; +// write_char('>'); print_string_x(cmd_buffer); write_char('<'); + // If there is a command we print the prompt after execution + // otherwise immediately because there is nothing to execute + if (cmd_line_len) + cmd_available = 1; + else + print_string("\n> "); + cursor = 0; + cmd_line_len = 0; + history_editptr = 0xffff; + } + l++; + l &= SBUF_MASK; + } +} diff --git a/cmd_editor.h b/cmd_editor.h new file mode 100644 index 0000000..cfb70a0 --- /dev/null +++ b/cmd_editor.h @@ -0,0 +1,9 @@ +#ifndef _CMD_EDITOR_H_ +#define _CMD_EDITOR_H_ + +#include + +void cmd_editor_init(void) __banked; +void cmd_edit(void) __banked; + +#endif diff --git a/cmd_parser.c b/cmd_parser.c index 51dd9bf..0b8f97a 100644 --- a/cmd_parser.c +++ b/cmd_parser.c @@ -51,17 +51,16 @@ __xdata uint8_t hexvalue[4] = { 0 }; // Buffer for writing to flash 0x1fd000, copy to 0x1fe000 -__xdata uint8_t cmd_buffer[SBUF_SIZE]; +__xdata uint8_t cmd_buffer[CMD_BUF_SIZE]; __xdata uint8_t cmd_available; -__xdata uint8_t l; __xdata uint8_t line_ptr; __xdata char is_white; __xdata char save_cmd; __xdata uint8_t ip[4]; -#define N_WORDS SBUF_SIZE +#define N_WORDS CMD_BUF_SIZE __xdata signed char cmd_words_b[N_WORDS]; __xdata uint8_t cmd_history[CMD_HISTORY_SIZE]; @@ -93,7 +92,7 @@ uint8_t cmd_compare(uint8_t start, uint8_t * __code cmd) signed char j = 0; for (i = cmd_words_b[start]; i != cmd_words_b[start + 1] && cmd_buffer[i] != ' '; i++) { - i &= SBUF_SIZE - 1; + i &= CMD_BUF_SIZE - 1; // print_byte(i); write_char(':'); print_byte(j); write_char('#'); print_string("\n"); // write_char('>'); write_char(cmd[j]); write_char('-'); write_char(cmd_buffer[i]); print_string("\n"); if (!cmd[j] && !isletter(cmd_buffer[i])) @@ -710,7 +709,7 @@ uint8_t cmd_tokenize(void) __banked is_white = 1; uint8_t word = 0; cmd_words_b[0] = -1; - while (cmd_buffer[line_ptr] && line_ptr < SBUF_SIZE - 1) { + while (cmd_buffer[line_ptr] && line_ptr < CMD_BUF_SIZE - 1) { if (is_white && cmd_buffer[line_ptr] != ' ') { is_white = 0; cmd_words_b[word++] = line_ptr; @@ -723,7 +722,7 @@ uint8_t cmd_tokenize(void) __banked return 1; } } - if (line_ptr == SBUF_SIZE - 1) + if (line_ptr == CMD_BUF_SIZE - 1) return 1; cmd_words_b[word++] = line_ptr; cmd_words_b[word++] = -1; @@ -1001,6 +1000,16 @@ void cmd_parser(void) __banked } } + +void clear_command_history(void) __banked +{ + for (cmd_history_ptr = 0; cmd_history_ptr < CMD_HISTORY_SIZE; cmd_history_ptr++) + cmd_history[cmd_history_ptr] = 0; + cmd_history_ptr = 0; + return; +} + + #define FLASH_READ_BURST_SIZE 0x100 #define PASSWORD "1234" void execute_config(void) __banked @@ -1020,7 +1029,7 @@ void execute_config(void) __banked __xdata uint8_t cfg_idx = 0; uint8_t c = 0; do { - for (uint8_t cmd_idx = 0; cmd_idx < (SBUF_SIZE - 1); cmd_idx++) { + for (uint8_t cmd_idx = 0; cmd_idx < (CMD_BUF_SIZE - 1); cmd_idx++) { c = flash_buf[cfg_idx++]; if (c == 0 || c == '\n') { cmd_buffer[cmd_idx] = '\0'; @@ -1041,13 +1050,6 @@ void execute_config(void) __banked config_done: // Start saving commands to cmd_history + clear_command_history(); save_cmd = 1; } - -void clear_command_history(void) __banked -{ - for (cmd_history_ptr = 0; cmd_history_ptr < CMD_HISTORY_SIZE; cmd_history_ptr++) - cmd_history[cmd_history_ptr] = 0; - cmd_history_ptr = 0; - return; -} \ No newline at end of file diff --git a/cmd_parser.h b/cmd_parser.h index 816d00b..dff8cf8 100644 --- a/cmd_parser.h +++ b/cmd_parser.h @@ -5,7 +5,7 @@ #include "rtl837x_common.h" -extern __xdata uint8_t cmd_buffer[SBUF_SIZE]; +extern __xdata uint8_t cmd_buffer[CMD_BUF_SIZE]; extern __xdata uint8_t cmd_available; uint8_t cmd_tokenize(void) __banked; diff --git a/machine.c b/machine.c index ae8ff9c..2b36644 100644 --- a/machine.c +++ b/machine.c @@ -75,7 +75,7 @@ void machine_custom_init(void) { } #elif defined MACHINE_KP_9000_9XH_X_EU __code const struct machine machine = { - .machine_name = "keepLink KP-9000-6XH-X-EU", + .machine_name = "keepLink KP-9000-9XH-X-EU", .isRTL8373 = 1, .min_port = 0, .max_port = 8, diff --git a/rtl837x_common.h b/rtl837x_common.h index f6c9dd7..f33a1d2 100644 --- a/rtl837x_common.h +++ b/rtl837x_common.h @@ -19,9 +19,19 @@ #define LOOKUP_MISS_DROP_9 0x00015555 #define LOOKUP_MISS_FLOOD 0x00000000 -// The serial buffer. Defines the command line size -// Must be 2^x and <= 128 -#define SBUF_SIZE 128 +/* Buffer for serial input, SBUF_SIZE must be power of 2 < 256 + * Writing to this buffer is under the sole control of the serial ISR + * Note that key-presses such as can create multiple + * keys (3 to 4) being sent via the serial line, so this must be + * sufficiently large */ +#define SBUF_SIZE 16 +#define SBUF_MASK (SBUF_SIZE - 1) + +extern __xdata volatile uint8_t sbuf_ptr; +extern __xdata uint8_t sbuf[SBUF_SIZE]; + +// Define the command buffer size, Must be 2^x and <= 128 +#define CMD_BUF_SIZE 128 // Size of the TCP Output buffer #define TCP_OUTBUF_SIZE 2500 @@ -90,6 +100,7 @@ extern __xdata struct uip_eth_addr uip_ethaddr; // Headers for calls in the common code area (HOME/BANK0) void print_string(__code char *p); +void print_string_x(__xdata char *p); void print_long(__xdata uint32_t a); void print_short(uint16_t a); void print_byte(uint8_t a); @@ -121,7 +132,6 @@ uint16_t strlen(register __code const char *s); uint16_t strlen_x(register __xdata const char *s); uint16_t strtox(register __xdata uint8_t *dst, register __code const char *s); void tcpip_output(void); -void print_string_x(__xdata char *p); uint8_t read_flash(uint8_t bank, __code uint8_t *addr); void get_random_32(void); void read_reg_timer(uint32_t * tmr); diff --git a/rtlplayground.c b/rtlplayground.c index 55b6d52..fc0eeed 100644 --- a/rtlplayground.c +++ b/rtlplayground.c @@ -16,6 +16,7 @@ #include "rtl837x_leds.h" #include "dhcp.h" #include "cmd_parser.h" +#include "cmd_editor.h" #include "uip/uipopt.h" #include "uip/uip.h" #include "uip/uip_arp.h" @@ -86,10 +87,13 @@ extern __xdata struct dhcp_state dhcp_state; #define STP_TICK_DIVIDER 3 - -// Buffer for serial input, SBUF_SIZE must be power of 2 < 256 +/* Buffer for serial input, SBUF_SIZE must be power of 2 < 256 + * Writing to this buffer is under the sole control of the serial ISR + * Note that key-presses such as can create multiple + * keys being sent via the serial line */ __xdata volatile uint8_t sbuf_ptr; __xdata uint8_t sbuf[SBUF_SIZE]; + __xdata uint8_t sfr_data[4]; extern __xdata uint8_t gpio_last_value[8]; @@ -2102,39 +2106,9 @@ void bootloader(void) set_sys_led_state(SYS_LED_ON); - // Wait for commands on serial connection - // sbuf_ptr is moved forward by serial interrupt, l is the position until we have already - // printed out the entered characters - __xdata uint8_t l = sbuf_ptr; // We have printed out entered characters until l - __xdata uint8_t line_start = sbuf_ptr; // This is where the current line starts - cmd_available = 0; + cmd_editor_init(); while (1) { - while (l != sbuf_ptr) { - // If the command buffer is currently in use, we cannot copy to it - if (cmd_available) - break; - write_char(sbuf[l]); - // Check whether there is a full line: - if (sbuf[l] == '\n' || sbuf[l] == '\r') { - write_char('\n'); - register uint8_t i = 0; - while (line_start != l) { - cmd_buffer[i++] = sbuf[line_start++]; - line_start &= (SBUF_SIZE - 1); - } - line_start++; - line_start &= (SBUF_SIZE - 1); - cmd_buffer[i] = '\0'; - // If there is a command we print the prompt after execution - // otherwise immediately because there is nothing to execute - if (i) - cmd_available = 1; - else - print_string("\n> "); - } - l++; - l &= (SBUF_SIZE - 1); - } + cmd_edit(); idle(); // Enter Idle mode until interrupt occurs } }