#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 // Reserve one byte for the terminating NUL written on Enter. When the // line is full, drop the character but still fall through to advance the // serial-ring read pointer below; a 'continue' here would spin forever. if (cmd_line_len < CMD_BUF_SIZE - 1) { 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 || sbuf[l] == 8) { // Backspace DEL or BS/^H 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] = NUL; // 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_cmd_prompt(); cursor = 0; cmd_line_len = 0; history_editptr = 0xffff; } l++; l &= SBUF_MASK; } }