Files
Sprinter-SDCC/libc/kbd/kbd_raw_sync.c
T
Александр Петров 030af74631 BUG-KBD-5: зажатый Shift снимался автоповтором стрелки
Симптом: Shift работал в одиночку и НЕ работал вместе со стрелками —
прыжок с зацепом не выходил (BUG-GRAB-1), а осторожный шаг срывался в бег.

Декодеры делали из «fake shift» два вывода, и второй был неверен:
  обёртка E0 F0 12 / E0 12 есть  -> Shift зажат -> взвести бит   — верно;
  расширенный make БЕЗ обёртки   -> Shift отпущен -> снять бит   — НЕТ.

Замер потока байт (MAME, breakpoint на выходе из in a,($18)): клавиатура
pc_kbd ms_naturl обёртку не шлёт вовсе — при зажатом Shift поток на ↑ ровно
`E0 75 E0 75 …`, ни одного F0/12.  А typematic-повторы идут непрерывно,
пока стрелка зажата, значит каждый повтор снимал реально зажатый Shift.
Короткий тап это маскировал: после отпускания стрелки Shift снова
становился последней клавишей, и его собственный автоповтор `12` взводил
бит обратно за ~30 мс.

Фикс: обратный вывод убран, расширенная клавиша о Shift не судит.
Состояние Shift ведут его собственные make/break 12 / F0 12 — они приходят
всегда.  Прямой вывод оставлен (дёшев и верен там, где обёртка есть).
Ушла ставшая ненужной _kbdraw_fakesh; трамплин короче на 36 Б (0x150→0x12C),
что важно — его клавиатурный блок упирается в диапазон jr.

Плата: потерянный при overrun break Shift снять нечем, модификатор может
залипнуть до перенажатия (BUG-KBD-3).  Размен решён как и раньше в
kbd_raw_sync: лучше залипание, чем отвал — сорванный посреди игры Shift
в PoP стоит жизни.

Проверено в MAME чтением _kbdraw_down: Shift+↑+→ зажаты 5 с (автоповтор
идёт) -> LSh остаётся 04; отпускание Shift -> 00.  Зацеп в игре
подтверждён пользователем.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 12:33:15 +03:00

73 lines
4.5 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).
*
* Выбран вариант (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 <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 между кадрами). */
_kbdraw_pending = 0;
}