/*
 * 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 <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)
{
    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;
}
