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>
This commit is contained in:
Александр Петров
2026-08-05 12:33:15 +03:00
parent 3fe083331f
commit 030af74631
6 changed files with 55 additions and 92 deletions
+12 -27
View File
@@ -12,12 +12,11 @@
* акселератора держат DI на весь блит — сотни микросекунд. Импульс,
* попавший в такое окно, теряется НАСОВСЕМ: байт остаётся в FIFO до
* следующего прерывания (следующий байт либо кадровое, 50 Гц);
* - трафик от клавиатуры пачками: тап стрелки — 5 байт, а при зажатом
* Shift ещё и «fake shift» (E0 F0 12 / E0 12 вокруг расширенного кода)
* — по 5 байт на нажатие И на отпускание. Три ячейки FIFO такую пачку
* не держат: потерянный make = «нажатие не сработало», потерянный
* break = залипшая клавиша (её потом лечит kbd_raw_sync, ценой сброса
* всех немодификаторных клавиш).
* - трафик от клавиатуры пачками: тап стрелки — 5 байт (E0 75 + E0 F0 75),
* одновременное отпускание нескольких клавиш — больше. Три ячейки FIFO
* такую пачку не держат: потерянный make = «нажатие не сработало»,
* потерянный break = залипшая клавиша (её потом лечит kbd_raw_sync,
* ценой сброса всех немодификаторных клавиш).
* Опрос из главного цикла снимает зависимость от пойманного импульса: чем
* чаще позвали, тем меньше шанс, что в FIFO успеет накопиться четвёртый
* байт. Звать между фазами кадра (после тяжёлых блитов), НЕ раз в кадр.
@@ -66,8 +65,9 @@ uint8_t kbd_raw_poll(void) __naked
jp Z, kp_setext ; (в отличие от трамплина, здесь jp разрешён)
;; --- «fake shift» тот же разбор, что в трамплине (см. _kbdraw.h):
;; расширенные 12/59 не клавиши, а обёртка вокруг расширенного кода,
;; которую клавиатура шлёт, пока реально зажат Shift. ---
;; расширенные 12/59 не клавиши, а служебная обёртка, которую
;; НЕКОТОРЫЕ клавиатуры шлют вокруг расширенного кода, пока реально
;; зажат Shift. Только подтверждение «зажат», обратного вывода нет. ---
ld e, a ; E = код
ld a, (__kbdraw_pending)
bit 1, a ; EXT?
@@ -77,29 +77,14 @@ uint8_t kbd_raw_poll(void) __naked
jr Z, kp_fake
cp #0x59
jr Z, kp_fake
;; расширенная КЛАВИША: make без обёртки => Shift не зажат
ld a, (__kbdraw_pending)
bit 0, a ; break состояние Shift не судим
jr NZ, kp_fkclr
ld a, (__kbdraw_fakesh)
or a, a
jr NZ, kp_fkclr ; обёртка была => Shift зажат
ld hl, #(__kbdraw_down + _KBDRAW_LSH_BYTE)
res _KBDRAW_LSH_BIT, (hl)
ld hl, #(__kbdraw_down + _KBDRAW_RSH_BYTE)
res _KBDRAW_RSH_BIT, (hl)
kp_fkclr:
xor a, a
ld (__kbdraw_fakesh), a
ld a, e
;; расширенная КЛАВИША обычный путь. О состоянии Shift по ней НЕ
;; судим (почему см. kp_fake). A всё ещё = код.
jr kp_code
kp_fake:
;; Бит взводит ОБЩИЙ писатель ниже: обнуляем префиксы и он кладёт
;; код в PLAIN-половину карты (без +32) и именно взведёт бит.
ld a, (__kbdraw_pending)
and #1 ; 1 = break-форма (идёт ПЕРЕД обёрнутым make)
ld (__kbdraw_fakesh), a
;; код в PLAIN-половину карты (без +32) и именно ВЗВЕДЁТ бит, а не
;; снимет, даже у break-формы обёртки.
xor a, a
ld (__kbdraw_pending), a
ld a, e