libc/kbd: kbd_raw_poll + замер потери нажатий при зажатом Shift (KBD-1)

Симптом: при удерживаемом Shift часть нажатий стрелок не отрабатывает
(~15 % по наблюдению пользователя), без Shift потерь нет.

Переведено в числа: нажимается Home — тоже расширенная клавиша (тот же
E0-префикс и тот же «fake shift»), но игрой игнорируется, поэтому рельеф
комнаты на результат не влияет.  Счётчики — брейкпоинты MAME с действием
{ b@ADDR = b@ADDR+1 ; g } на входе клавиатурной ветки трамплина, на чтении
порта 0x18 и на установке make-бита.

Что измерено (10 нажатий Shift+Home, дошло make):
  игра идёт, опрос ВКЛ   9/10     игра идёт, опрос ВЫКЛ  9/10
  игра ЗАМОРОЖЕНА (блитов нет вообще, длинных DI нет)  8/10

Обе исходные гипотезы отпали:
- длина наших DI-окон ни при чём (в замороженном кадре потерь больше);
- снятие di в accel-ядрах libbgi УРОНИЛО машину — режим «акселератор при
  EI» из docs/new/06-accel.md §6.6 в этой прошивке недоступен.

Байт теряется НИЖЕ нашего кода: на 49 прочитанных байт пришлось только 28
входов в клавиатурную ветку, то есть ~44 % импульсов запроса прерывания не
обслуживается и трёхбайтовый FIFO SIO переполняется.

Потолок приёма измерен отдельной программой tests/kbdpoll (ничего, кроме
kbd_raw_poll в цикле): 25 нажатий -> 25 make, 250 байт из 250, ноль потерь.
Значит опрос лечит полностью, вопрос только в плотности: нужно раз в
~0.5 мс, а шесть вызовов за 60-мс кадр давали раз в 10 мс.

Поэтому вызовы из roomtest.c УБРАНЫ (они стояли там, где прерывания и так
разрешены, и дублировали трамплин — 9/10 с ними и без).  Сама функция
kbd_raw_poll оставлена в libc: она корректна и нужна как основа плотного
опроса.  В заголовке и в libc-reference — честная оговорка, чтобы её не
ставили в игровой цикл «на всякий случай» без замера.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Александр Петров
2026-08-01 15:33:16 +03:00
parent 774b1cc7c4
commit b56f2b4582
7 changed files with 299 additions and 9 deletions
+24
View File
@@ -57,6 +57,30 @@ uint8_t kbd_raw_down(uint16_t code);
* «отваливался» при каждом overrun (см. kbd_raw_sync.c). */
void kbd_raw_sync(void);
/* Вычерпать приёмный FIFO ОПРОСОМ, не дожидаясь прерывания. Возвращает
* 0 (FIFO был пуст, ~40 тактов) или 1 (что-то вычерпано и декодировано в
* ту же карту, что ведёт трамплин).
*
* Зачем: на каждый принятый байт запрос прерывания живёт единицы
* микросекунд, и пропущенный импульс не «догоняется» — байт лежит в
* 3-байтовом FIFO до следующего прерывания. Пачки в 5 байт (тап стрелки;
* при зажатом Shift — ещё и «fake shift» вокруг расширенного кода) при
* этом переполняют FIFO: теряется make («нажатие не сработало») или break
* (залипание).
*
* ЧЕСТНАЯ ОГОВОРКА ПО ЭФФЕКТИВНОСТИ (замер 2026-08-01, PoP roomtest,
* см. applications/PoP/roomtest/TASKS.md, KBD-1): расстановка «несколько
* вызовов за кадр, после тяжёлых фаз» НЕ ДАЁТ НИЧЕГО — потери те же, что
* без неё. Такие вызовы попадают в участки, где прерывания и так
* разрешены, и лишь дублируют трамплин. Смысл появляется только у
* ПЛОТНОГО опроса (в цикле ожидания, десятки-сотни раз за кадр), и это
* ещё не подтверждено замером. Не ставить эту функцию в игровой цикл
* «на всякий случай» — сначала померить.
*
* Тело идёт под DI и БЕЗУСЛОВНО делает EI на выходе: рассчитано на вызов
* из главного цикла, из ISR звать нельзя. */
uint8_t kbd_raw_poll(void);
#define KBD_EXT 0x0100 /* база кода была расширенной (0xE0-префикс) */
/* Позиционные коды PS/2 Set 2 — то, что реально нужно платформеру.