diff --git a/system/keyboard.c b/system/keyboard.c index 2ad896b..7079558 100644 --- a/system/keyboard.c +++ b/system/keyboard.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -// Copyright (C) 2020-2022 Martin Whitaker. +// Copyright (C) 2020-2025 Martin Whitaker. #include @@ -13,6 +13,13 @@ #include "keyboard.h" #include "config.h" +//------------------------------------------------------------------------------ +// Private Constants +//------------------------------------------------------------------------------ + +// The number of frame periods to wait during escape sequence parsing. +#define TTY_SEQ_WAIT_TIME 2 + //------------------------------------------------------------------------------ // Private Variables //------------------------------------------------------------------------------ @@ -216,6 +223,133 @@ static const char usb_hid_keymap[] = { keyboard_types_t keyboard_types = KT_NONE; +//------------------------------------------------------------------------------ +// Private Functions +//------------------------------------------------------------------------------ + +// This function is called when an ESC O prefix has been detected. +static char get_vt220_sequence1(void) +{ + switch (tty_get_char(TTY_SEQ_WAIT_TIME)) { + case 'P': + return '1'; // VT100/VT220 PF1 (F1 in terminal emulators) + case 'Q': + return '2'; // VT100/VT220 PF2 (F2 in terminal emulators) + case 'R': + return '3'; // VT100/VT220 PF3 (F3 in terminal emulators) + case 'S': + return '4'; // VT100/VT220 PF4 (F4 in terminal emulators) + case 'T': + return '5'; // F5 in some terminal emulators + case 'U': + return '6'; // F6 in some terminal emulators + case 'V': + return '7'; // F7 in some terminal emulators + case 'W': + return '8'; // F8 in some terminal emulators + case 'X': + return '9'; // F9 in some terminal emulators + case 'Y': + return '0'; // F10 in some terminal emulators + default: + return '\0'; // unrecognised key + break; + } +} + +// This function is called when an ESC [ prefix has been detected. +static char get_vt220_sequence2(void) +{ + char ch1; + char ch2; + + ch1 = tty_get_char(TTY_SEQ_WAIT_TIME); + switch (ch1) { + case 'A': + return 'u'; // VT100/VT220 cursor up + case 'B': + return 'd'; // VT100/VT220 cursor down + case 'C': + return 'r'; // VT100/VT220 cursor right + case 'D': + return 'l'; // VT100/VT220 cursor left + default: + break; + } + if ((ch1 < '1') || (ch1 > '6')) { + return '\0'; // unrecognised sequence - give up + } + ch2 = tty_get_char(TTY_SEQ_WAIT_TIME); + if (ch2 == '~') { + return '\0'; // VT100/VT220 editing key - ignore it + } + switch (ch1) { + case '1': + switch (ch2) { + case '1': + ch1 = '1'; break; // F1 in terminal emulators + case '2': + ch1 = '2'; break; // F2 in terminal emulators + case '3': + ch1 = '3'; break; // F3 in terminal emulators + case '4': + ch1 = '4'; break; // F4 in terminal emulators + case '5': + ch1 = '5'; break; // F5 in terminal emulators + case '7': + ch1 = '6'; break; // VT220 F6 + case '8': + ch1 = '7'; break; // VT220 F7 + case '9': + ch1 = '8'; break; // VT220 F8 + default: + ch1 = '\0'; break; // unrecognised key + } + break; + case '2': + switch (ch2) { + case '0': + ch1 = '9'; break; // VT220 F9 + case '1': + ch1 = '0'; break; // VT220 F10 + default: + ch1 = '\0'; break; // unrecognised key + } + break; + default: + ch1 = '\0'; // unrecognised key + break; + } + ch2 = tty_get_char(TTY_SEQ_WAIT_TIME); + if (ch2 == '~') { + return ch1; // valid key sequence - return decoded key + } + return '\0'; // invalid key sequence - ignore it +} + +// This function is called when an ESC has been detected. +static char get_tty_special_key(void) +{ + switch (tty_get_char(TTY_SEQ_WAIT_TIME)) { + case 'A': + return 'u'; // VT52 cursor up + case 'B': + return 'd'; // VT52 cursor down + case 'C': + return 'r'; // VT52 cursor right + case 'D': + return 'l'; // VT52 cursor left + case 'O': + return get_vt220_sequence1(); + case '[': + return get_vt220_sequence2(); + case '\0': + return ESC; // timeout - return ESC character + default: + return '\0'; // unrecognised sequence - give up + } +} + //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ @@ -239,6 +373,20 @@ void keyboard_init(void) char get_key(void) { + if (enable_tty) { + char c = tty_get_char(0); + switch (c) { + case '\0': + break; + case '\r': + return '\n'; + case ESC: + return get_tty_special_key(); + default: + return c; + } + } + if (keyboard_types & KT_USB) { uint8_t c = get_usb_keycode(); if (c > 0 && c < sizeof(usb_hid_keymap)) { @@ -274,13 +422,5 @@ char get_key(void) } } - if (enable_tty) { - uint8_t c = tty_get_key(); - if (c != 0xFF) { - if (c == 0x0D) c = '\n'; // Enter - return c; - } - } - return '\0'; } diff --git a/system/keyboard.h b/system/keyboard.h index 068cc85..10eabe7 100644 --- a/system/keyboard.h +++ b/system/keyboard.h @@ -40,7 +40,8 @@ void keyboard_init(void); /** * Checks if a key has been pressed and returns the primary ASCII character * corresponding to that key if so, otherwise returns the null character. - * F1 to F10 are mapped to the corresponding decimal digit (F10 -> 0). All + * F1 to F10 are mapped to the corresponding decimal digit (F10 -> 0). The + * Up, Down, Left, Right cursor keys are mapped to 'u', 'd', 'l', 'r'. All * other keys that don't have a corresponding ASCII character are ignored. * Characters are only returned for key presses, not key releases. A US * keyboard layout is assumed (because we can't easily do anything else). diff --git a/system/serial.c b/system/serial.c index 4f01db7..7682dff 100644 --- a/system/serial.c +++ b/system/serial.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -// Copyright (C) 2004-2023 Sam Demeulemeester +// Copyright (C) 2004-2025 Sam Demeulemeester #include #include @@ -65,7 +65,7 @@ static void serial_wait_for_xmit(struct serial_port *port) } while ((lsr & BOTH_EMPTY) != BOTH_EMPTY); } -void serial_echo_print(const char *p) +static void serial_echo_print(const char *p) { struct serial_port *port = &console_serial; @@ -81,7 +81,7 @@ void serial_echo_print(const char *p) } } -void tty_goto(int y, int x) +static void tty_goto(int y, int x) { static char s[3]; @@ -117,6 +117,9 @@ void tty_init(void) console_serial.parity = SERIAL_DEFAULT_PARITY; console_serial.bits = SERIAL_DEFAULT_BITS; + // Precalculate the frame time (in microseconds). + console_serial.frame_time = (1000000 * (1 + console_serial.bits + console_serial.parity + 1)) / console_serial.baudrate; + // UART MMIO Address is usually above TOLUD and never < 1MB if (console_serial.base_addr > 0xFFFF) { console_serial.is_mmio = true; @@ -242,13 +245,17 @@ void tty_send_region(int start_row, int start_col, int end_row, int end_col) } } -char tty_get_key(void) +char tty_get_char(int max_wait_frames) { - int uart_status = serial_read_reg(&console_serial, UART_LSR); + int wait_time = max_wait_frames * console_serial.frame_time; + do { + int uart_status = serial_read_reg(&console_serial, UART_LSR); + if (uart_status & UART_LSR_DR) { + return serial_read_reg(&console_serial, UART_RX); + } + usleep(10); + wait_time -= 10; + } while (wait_time > 0); - if (uart_status & UART_LSR_DR) { - return serial_read_reg(&console_serial, UART_RX); - } else { - return 0xFF; - } + return '\0'; } diff --git a/system/serial.h b/system/serial.h index f9239aa..9142bb8 100644 --- a/system/serial.h +++ b/system/serial.h @@ -7,7 +7,7 @@ * display via Serial/UART. * *//* - * Copyright (C) 2004-2023 Sam Demeulemeester. + * Copyright (C) 2004-2025 Sam Demeulemeester. */ #define SERIAL_DEFAULT_BITS 8 @@ -26,6 +26,7 @@ struct serial_port { int parity; int bits; int baudrate; + int frame_time; int reg_width; int refclk; uintptr_t base_addr; @@ -179,6 +180,6 @@ void tty_print(int y, int x, const char *p); void tty_send_region(int start_row, int start_col, int end_row, int end_col); -char tty_get_key(void); +char tty_get_char(int max_wait_frames); #endif /* _SERIAL_REG_H */