libc/kbd: raw-клавиатура — вычерпывание FIFO + селективный wipe модификаторов

- kbd_raw_sync: цикл вычерпывания SIO FIFO (не 1 байт/прерывание) —
  фикс залипания клавиш; overrun-wipe сбрасывает только пострадавшие
  клавиши, не модификаторы (typematic их не перечитывает).
- Гайд docs/kbd-games.md; заметка о Rx-overrun в docs/TODO.md; справочник
  скан-кодов в docs/libc-reference.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-23 19:38:12 +03:00
parent 95c22be9bd
commit 3d586af031
5 changed files with 186 additions and 16 deletions
+11 -3
View File
@@ -48,9 +48,13 @@ uint8_t kbd_raw_down(uint16_t code);
/* Восстановление после Rx-overrun SIO: звать РАЗ В КАДР (до чтения
* kbd_raw_down). Аппаратный FIFO SIO 3 байта; при длинных DI-окнах пачка
* скан-кодов (напр. shift+стрелка release) переполняет его → потерян break
* → залипшая клавиша. Трамплин ловит overrun и взводит флаг; kbd_raw_sync
* по флагу сбрасывает ВСЁ held-состояние (реально зажатые перечитаются). */
* скан-кодов (напр. быстрый тап стрелки: make+break = 5 байт) переполняет
* его → потерян break → залипшая клавиша. Трамплин ловит overrun и
* взводит флаг; kbd_raw_sync по флагу сбрасывает held-состояние всех
* клавиш КРОМЕ модификаторов (Shift/Ctrl/Alt): PS/2 автоповторяет только
* последнюю нажатую клавишу, поэтому обычные зажатые перечитаются
* typematic'ом, а сброшенный модификатор восстановить нечем — он бы
* «отваливался» при каждом overrun (см. kbd_raw_sync.c). */
void kbd_raw_sync(void);
#define KBD_EXT 0x0100 /* база кода была расширенной (0xE0-префикс) */
@@ -66,5 +70,9 @@ void kbd_raw_sync(void);
#define KBD_ESC 0x76
#define KBD_LSHIFT 0x12
#define KBD_RSHIFT 0x59
#define KBD_LCTRL 0x14
#define KBD_LALT 0x11
#define KBD_RCTRL (KBD_EXT | 0x14)
#define KBD_RALT (KBD_EXT | 0x11)
#endif
+28 -13
View File
@@ -3,25 +3,40 @@
*
* Аппаратный приёмный FIFO SIO — 3 байта. Если прерывания запрещены
* достаточно долго (DI-окна графики/кадрового IRQ), а в этот момент
* приходит пачка скан-кодов (напр. одновременное отпускание shift+стрелка
* = F0 12 E0 F0 74, 5 байт) — FIFO переполняется, байт(ы) теряются
* аппаратно. Потерянный break → клавиша «залипает» в _kbdraw_down.
* приходит пачка скан-кодов (напр. быстрый тап стрелки = E0 74 + E0 F0 74,
* 5 байт) — FIFO переполняется, байт(ы) теряются аппаратно. Потерянный
* break → клавиша «залипает» в _kbdraw_down.
*
* Трамплин ловит overrun (RR1 бит5) и взводит _kbdraw_overrun. Приложение
* зовёт kbd_raw_sync() раз в кадр: при взведённом флаге сбрасывает ВСЁ
* held-состояние (какой именно break потерян — неизвестно). Реально
* зажатые клавиши «перечитаются» их make-повтором/следующим нажатием.
*
* ВНИМАНИЕ: путь overrun-recovery НЕ проверен детерминированно (баг
* интермиттентный, зависит от тайминга DI) — требует проверки на железе.
* зовёт kbd_raw_sync() раз в кадр: при взведённом флаге сбрасывается
* held-состояние ВСЕХ клавиш, КРОМЕ модификаторов (Shift/Ctrl/Alt —
* таблица ниже). Обычные реально зажатые клавиши «перечитаются» их
* make-повтором (typematic) за ~0.1 с. Модификаторы НЕ сбрасываем
* принципиально: PS/2 автоповторяет только ПОСЛЕДНЮЮ нажатую клавишу,
* удерживаемый Shift при повторе стрелки не шлёт НИЧЕГО — сброшенный
* модификатор невозможно перечитать, он «отваливался» до перенажатия
* (баг PoP: Shift+→ = осторожный шаг превращался в бег, 2026-07-22).
* Цена: если overrun потерял именно break модификатора, тот залипнет до
* следующего нажатия — редкий случай против регулярного отвала.
*/
#include <string.h>
#include "_kbdraw.h"
/* Скан-коды модификаторов (PS/2 Set 2): L/R Shift, LCtrl, LAlt,
* RCtrl (E0 14 → +256), RAlt (E0 11 → +256). */
static const uint16_t _kbd_mod_codes[6] = {
0x12, 0x59, 0x14, 0x11, 0x0114, 0x0111
};
void kbd_raw_sync(void)
{
if (_kbdraw_overrun) {
_kbdraw_overrun = 0;
memset(_kbdraw_down, 0, sizeof(_kbdraw_down));
}
uint8_t save[6];
uint8_t i;
if (!_kbdraw_overrun) return;
_kbdraw_overrun = 0;
for (i = 0; i < 6; i++) save[i] = _kbdraw_down[_kbd_mod_codes[i]];
memset(_kbdraw_down, 0, sizeof(_kbdraw_down));
for (i = 0; i < 6; i++) _kbdraw_down[_kbd_mod_codes[i]] = save[i];
}