KBD: состояние Shift по «fake shift» — ни залипания, ни отвала

Клавиатура PS/2 обёртывает КАЖДЫЙ расширенный код парой E0 F0 12 / E0 12,
пока реально зажат Shift (замер на железе/MAME: правый шифт обёртывается
своим кодом 0x59).  Это прямое и непрерывное свидетельство состояния
Shift — единственное доступное, потому что опросить PS/2 нельзя, а
typematic повторяет последнюю нажатую клавишу, то есть стрелку.

Оба декодера (_irq_tramp.c, kbd_raw_poll.c) читают обёртку в обе стороны:
обёртка есть -> Shift взвести; расширенный make без обёртки -> Shift
снять.  Бит взводит общий писатель — достаточно обнулить префиксы, и код
уходит в plain-половину карты как make.

Это снимает размен, между крайностями которого мы метались:
  - исключать модификаторы из сброса по overrun -> Shift залипал навсегда
    (BUG-KBD-3);
  - сбрасывать всю карту, как DSS -> Shift сносился каждым overrun'ом, а
    при зажатом Shift тап стрелки это 10 байт в 3-байтовый FIFO, то есть
    overrun почти гарантирован (BUG-KBD-4).
Теперь kbd_raw_sync снова не трогает модификаторы, и это безопасно:
залипание снимается первым же нажатием стрелки.

Раскладка трамплина: клавиатурный блок перевалил за 127 байт, а jp внутри
запрещён (копия в W2).  Префиксные обработчики переехали вплотную к своим
cp, посередине тела стоят ретрансляторы tr_kbd_hub/tr_hub_notkbd/
tr_hub_dss.  В kbd_raw_poll такого ограничения нет — там три jp.

Проверено в MAME: Shift переживает пять тапов подряд; штатное отпускание
снимает; искусственно залипший бит снимается первым тапом.
make size-check — роста нет.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Александр Петров
2026-08-03 22:16:27 +03:00
parent ba37bd1133
commit 1f16e8fa70
5 changed files with 200 additions and 33 deletions
+39 -14
View File
@@ -3,21 +3,40 @@
*
* Аппаратный приёмный FIFO SIO — 3 байта. Если прерывания запрещены
* достаточно долго (DI-окна графики/кадрового IRQ), а в этот момент
* приходит пачка скан-кодов (напр. быстрый тап стрелки = E0 74 + E0 F0 74,
* 5 байт) — FIFO переполняется, байт(ы) теряются аппаратно. Потерянный
* break → клавиша «залипает» в _kbdraw_down.
* приходит пачка скан-кодов — FIFO переполняется, байт(ы) теряются
* аппаратно. Потерянный break → клавиша «залипает» в _kbdraw_down.
*
* Трамплин ловит overrun (RR1 бит5) и взводит _kbdraw_overrun. Приложение
* зовёт kbd_raw_sync() раз в кадр: при взведённом флаге сбрасывается
* held-состояние ВСЕХ клавиш, КРОМЕ модификаторов (Shift/Ctrl/Alt —
* таблица ниже). Обычные реально зажатые клавиши «перечитаются» их
* make-повтором (typematic) за ~0.1 с. Модификаторы НЕ сбрасываем
* принципиально: PS/2 автоповторяет только ПОСЛЕДНЮЮ нажатую клавишу,
* удерживаемый Shift при повторе стрелки не шлёт НИЧЕГО — сброшенный
* модификатор невозможно перечитать, он «отваливался» до перенажатия
* (баг PoP: Shift+→ = осторожный шаг превращался в бег, 2026-07-22).
* Цена: если overrun потерял именно break модификатора, тот залипнет до
* следующего нажатия — редкий случай против регулярного отвала.
* Трамплин (и kbd_raw_poll) ловят overrun (RR1 бит5): делают Error Reset,
* чистят FSM префиксов _kbdraw_pending и взводят _kbdraw_overrun.
* Приложение зовёт kbd_raw_sync() раз в кадр — по флагу сбрасывается
* held-состояние карты. Реально зажатые клавиши перечитаются make-повтором
* (typematic) за ~0.1 с.
*
* МОДИФИКАТОРЫ ИЗ СБРОСА ИСКЛЮЧЕНЫ (2026-08-03, третья редакция). История
* вопроса — две неудачные крайности:
*
* 1) исключать модификаторы, не имея иного способа их проверить:
* потерянный break Shift снять нечем, Shift залипает навсегда
* (BUG-KBD-3);
* 2) сбрасывать ВСЁ, как DSS (KBD_Receiver_Overrun в KEYINTER.ASM чистит
* и KEYCTRL, и KEY_FLG): залипания нет, но удерживаемый Shift сносится
* каждым overrun'ом, а при зажатом Shift КАЖДЫЙ тап стрелки — это 10
* байт в 3-байтовый FIFO, то есть overrun почти гарантирован. Игрок
* жмёт Shift+→ ожидая осторожный шаг, а Кид уходит в бег — в PoP это
* прямо стоит жизни (яма/пики). Хуже залипания (BUG-KBD-4).
*
* Третий вариант убирает сам размен. Те самые лишние байты — это «fake
* shift» (E0 F0 12 перед расширенным make, E0 12 после break), и шлются они
* ТОЛЬКО пока Shift реально зажат. Декодеры (_irq_tramp.c, kbd_raw_poll.c)
* теперь читают их в обе стороны: обёртка есть → Shift взвести, расширенный
* make без обёртки → Shift снять. Значит:
* - сбрасывать модификаторы здесь НЕ НАДО (состояние Shift подтверждается
* независимо от того, что съел overrun);
* - залипание невозможно: первый же тап стрелки снимет ложный Shift.
*
* Ctrl/Alt fake-shift'ом не покрыты (клавиатура их не обёртывает). Их
* оставляем по тому же принципу «лучше залипание, чем отвал»: залипший
* Ctrl/Alt в PoP не управляет ничем критичным и снимается перенажатием.
*/
#include <string.h>
#include "_kbdraw.h"
@@ -47,4 +66,10 @@ void kbd_raw_sync(void)
uint16_t c = _kbd_mod_codes[i];
if (save[i]) _kbdraw_down[c >> 3] |= (uint8_t)(1 << (c & 7));
}
/* FSM префиксов трамплин уже сбросил; дублируем на случай overrun'а,
* пойманного вне его (kbd_raw_poll между кадрами). Незакрытую обёртку
* fake shift тоже считаем недействительной: её вторая половина могла
* пропасть вместе с потерянными байтами. */
_kbdraw_pending = 0;
_kbdraw_fakesh = 0;
}