Files

212 lines
7.3 KiB
C

#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') { // <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 || 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;
}
}