Merge pull request #93 from logicog/cmdbuf

Add support for command line editing
This commit is contained in:
feelfree69
2026-02-25 15:55:41 +01:00
committed by GitHub
8 changed files with 259 additions and 56 deletions
+1 -1
View File
@@ -27,7 +27,7 @@ all: create_build_dir $(VERSION_HEADER) $(SUBDIRS) $(BUILDDIR)rtlplayground.bin
create_build_dir: create_build_dir:
mkdir -p $(BUILDDIR) 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 = ${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 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
+208
View File
@@ -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') { // <CURSOR-LEFT>
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') { // <CURSOR-RIGHT>
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') { // <CURSOR-UP>
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') { // <CURSOR-DOWN>
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;
}
}
+9
View File
@@ -0,0 +1,9 @@
#ifndef _CMD_EDITOR_H_
#define _CMD_EDITOR_H_
#include <stdint.h>
void cmd_editor_init(void) __banked;
void cmd_edit(void) __banked;
#endif
+17 -15
View File
@@ -51,17 +51,16 @@ __xdata uint8_t hexvalue[4] = { 0 };
// Buffer for writing to flash 0x1fd000, copy to 0x1fe000 // 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 cmd_available;
__xdata uint8_t l;
__xdata uint8_t line_ptr; __xdata uint8_t line_ptr;
__xdata char is_white; __xdata char is_white;
__xdata char save_cmd; __xdata char save_cmd;
__xdata uint8_t ip[4]; __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 signed char cmd_words_b[N_WORDS];
__xdata uint8_t cmd_history[CMD_HISTORY_SIZE]; __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; signed char j = 0;
for (i = cmd_words_b[start]; i != cmd_words_b[start + 1] && cmd_buffer[i] != ' '; i++) { 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"); // 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"); // write_char('>'); write_char(cmd[j]); write_char('-'); write_char(cmd_buffer[i]); print_string("\n");
if (!cmd[j] && !isletter(cmd_buffer[i])) if (!cmd[j] && !isletter(cmd_buffer[i]))
@@ -710,7 +709,7 @@ uint8_t cmd_tokenize(void) __banked
is_white = 1; is_white = 1;
uint8_t word = 0; uint8_t word = 0;
cmd_words_b[0] = -1; 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] != ' ') { if (is_white && cmd_buffer[line_ptr] != ' ') {
is_white = 0; is_white = 0;
cmd_words_b[word++] = line_ptr; cmd_words_b[word++] = line_ptr;
@@ -723,7 +722,7 @@ uint8_t cmd_tokenize(void) __banked
return 1; return 1;
} }
} }
if (line_ptr == SBUF_SIZE - 1) if (line_ptr == CMD_BUF_SIZE - 1)
return 1; return 1;
cmd_words_b[word++] = line_ptr; cmd_words_b[word++] = line_ptr;
cmd_words_b[word++] = -1; 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 FLASH_READ_BURST_SIZE 0x100
#define PASSWORD "1234" #define PASSWORD "1234"
void execute_config(void) __banked void execute_config(void) __banked
@@ -1020,7 +1029,7 @@ void execute_config(void) __banked
__xdata uint8_t cfg_idx = 0; __xdata uint8_t cfg_idx = 0;
uint8_t c = 0; uint8_t c = 0;
do { 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++]; c = flash_buf[cfg_idx++];
if (c == 0 || c == '\n') { if (c == 0 || c == '\n') {
cmd_buffer[cmd_idx] = '\0'; cmd_buffer[cmd_idx] = '\0';
@@ -1041,13 +1050,6 @@ void execute_config(void) __banked
config_done: config_done:
// Start saving commands to cmd_history // Start saving commands to cmd_history
clear_command_history();
save_cmd = 1; 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;
}
+1 -1
View File
@@ -5,7 +5,7 @@
#include "rtl837x_common.h" #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; extern __xdata uint8_t cmd_available;
uint8_t cmd_tokenize(void) __banked; uint8_t cmd_tokenize(void) __banked;
+1 -1
View File
@@ -75,7 +75,7 @@ void machine_custom_init(void) { }
#elif defined MACHINE_KP_9000_9XH_X_EU #elif defined MACHINE_KP_9000_9XH_X_EU
__code const struct machine machine = { __code const struct machine machine = {
.machine_name = "keepLink KP-9000-6XH-X-EU", .machine_name = "keepLink KP-9000-9XH-X-EU",
.isRTL8373 = 1, .isRTL8373 = 1,
.min_port = 0, .min_port = 0,
.max_port = 8, .max_port = 8,
+14 -4
View File
@@ -19,9 +19,19 @@
#define LOOKUP_MISS_DROP_9 0x00015555 #define LOOKUP_MISS_DROP_9 0x00015555
#define LOOKUP_MISS_FLOOD 0x00000000 #define LOOKUP_MISS_FLOOD 0x00000000
// The serial buffer. Defines the command line size /* Buffer for serial input, SBUF_SIZE must be power of 2 < 256
// Must be 2^x and <= 128 * Writing to this buffer is under the sole control of the serial ISR
#define SBUF_SIZE 128 * Note that key-presses such as <cursor-left> 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 // Size of the TCP Output buffer
#define TCP_OUTBUF_SIZE 2500 #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) // Headers for calls in the common code area (HOME/BANK0)
void print_string(__code char *p); void print_string(__code char *p);
void print_string_x(__xdata char *p);
void print_long(__xdata uint32_t a); void print_long(__xdata uint32_t a);
void print_short(uint16_t a); void print_short(uint16_t a);
void print_byte(uint8_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 strlen_x(register __xdata const char *s);
uint16_t strtox(register __xdata uint8_t *dst, register __code const char *s); uint16_t strtox(register __xdata uint8_t *dst, register __code const char *s);
void tcpip_output(void); void tcpip_output(void);
void print_string_x(__xdata char *p);
uint8_t read_flash(uint8_t bank, __code uint8_t *addr); uint8_t read_flash(uint8_t bank, __code uint8_t *addr);
void get_random_32(void); void get_random_32(void);
void read_reg_timer(uint32_t * tmr); void read_reg_timer(uint32_t * tmr);
+8 -34
View File
@@ -16,6 +16,7 @@
#include "rtl837x_leds.h" #include "rtl837x_leds.h"
#include "dhcp.h" #include "dhcp.h"
#include "cmd_parser.h" #include "cmd_parser.h"
#include "cmd_editor.h"
#include "uip/uipopt.h" #include "uip/uipopt.h"
#include "uip/uip.h" #include "uip/uip.h"
#include "uip/uip_arp.h" #include "uip/uip_arp.h"
@@ -86,10 +87,13 @@ extern __xdata struct dhcp_state dhcp_state;
#define STP_TICK_DIVIDER 3 #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 <cursor-left> can create multiple
* keys being sent via the serial line */
__xdata volatile uint8_t sbuf_ptr; __xdata volatile uint8_t sbuf_ptr;
__xdata uint8_t sbuf[SBUF_SIZE]; __xdata uint8_t sbuf[SBUF_SIZE];
__xdata uint8_t sfr_data[4]; __xdata uint8_t sfr_data[4];
extern __xdata uint8_t gpio_last_value[8]; extern __xdata uint8_t gpio_last_value[8];
@@ -2102,39 +2106,9 @@ void bootloader(void)
set_sys_led_state(SYS_LED_ON); set_sys_led_state(SYS_LED_ON);
// Wait for commands on serial connection cmd_editor_init();
// 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;
while (1) { while (1) {
while (l != sbuf_ptr) { cmd_edit();
// 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);
}
idle(); // Enter Idle mode until interrupt occurs idle(); // Enter Idle mode until interrupt occurs
} }
} }