Files
Sprinter-SDCC/libc/kbd/kbd_raw_sync.c
T
Александр Петров 1f16e8fa70 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>
2026-08-03 22:16:27 +03:00

76 lines
4.7 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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;
}