/* * kbd_raw_sync.c — восстановление held-состояния после Rx-overrun SIO. * * Аппаратный приёмный FIFO SIO — 3 байта. Если прерывания запрещены * достаточно долго (DI-окна графики/кадрового IRQ), а в этот момент * приходит пачка скан-кодов — FIFO переполняется, байт(ы) теряются * аппаратно. Потерянный break → клавиша «залипает» в _kbdraw_down. * * Трамплин (и 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 #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) { uint8_t save[6]; uint8_t i; if (!_kbdraw_overrun) return; _kbdraw_overrun = 0; /* Карта упакована по битам (см. _kbdraw.h): сохраняем/возвращаем * именно БИТ модификатора, а не байт. */ for (i = 0; i < 6; i++) { uint16_t c = _kbd_mod_codes[i]; save[i] = (uint8_t)((_kbdraw_down[c >> 3] >> (c & 7)) & 1); } memset(_kbdraw_down, 0, sizeof(_kbdraw_down)); for (i = 0; i < 6; i++) { 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; }