/* * 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). * * Выбран вариант (1) — модификаторы НЕ сбрасываем. Была и третья редакция, * обещавшая снять сам размен: «fake shift» (E0 F0 12 / E0 12) читался в обе * стороны, и расширенный make без обёртки снимал Shift, то есть залипание * лечилось первым же тапом стрелки. Обратный вывод оказался неверным * (замер 2026-08-05, BUG-GRAB-1: обёртку шлют не все клавиатуры и никогда — * на typematic-повторах), и он ломал Shift вместе со стрелками; убран, см. * _kbdraw.h. Значит размен вернулся, и здесь он решается в пользу * «лучше залипание, чем отвал»: залипший модификатор снимается его же * перенажатием, а сорванный посреди игры Shift в PoP стоит жизни. * * Overrun теперь редок сам по себе: FIFO вычерпывается опросом из главного * цикла (kbd_raw_poll, KBD-1), а не только по пойманному прерыванию. */ #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 между кадрами). */ _kbdraw_pending = 0; }