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:
@@ -63,9 +63,9 @@ void kbd_raw_sync(void);
|
|||||||
*
|
*
|
||||||
* Зачем: на каждый принятый байт запрос прерывания живёт единицы
|
* Зачем: на каждый принятый байт запрос прерывания живёт единицы
|
||||||
* микросекунд, и пропущенный импульс не «догоняется» — байт лежит в
|
* микросекунд, и пропущенный импульс не «догоняется» — байт лежит в
|
||||||
* 3-байтовом FIFO до следующего прерывания. Пачки в 5 байт (тап стрелки;
|
* 3-байтовом FIFO до следующего прерывания. Пачки (тап стрелки — 5 байт;
|
||||||
* при зажатом Shift — ещё и «fake shift» вокруг расширенного кода) при
|
* одновременное отпускание нескольких клавиш — больше) при этом
|
||||||
* этом переполняют FIFO: теряется make («нажатие не сработало») или break
|
* переполняют FIFO: теряется make («нажатие не сработало») или break
|
||||||
* (залипание).
|
* (залипание).
|
||||||
*
|
*
|
||||||
* КАК ЕЁ ЗВАТЬ (замеры 2026-08-01, PoP roomtest — см.
|
* КАК ЕЁ ЗВАТЬ (замеры 2026-08-01, PoP roomtest — см.
|
||||||
|
|||||||
+12
-27
@@ -82,10 +82,10 @@ void _irq_tramp(void) __naked
|
|||||||
jr tr_kbd_hub
|
jr tr_kbd_hub
|
||||||
tr_kbd_nE0:
|
tr_kbd_nE0:
|
||||||
|
|
||||||
;; --- «fake shift»: расширенные коды 12/59 — не клавиши, а обёртка,
|
;; --- «fake shift»: расширенные коды 12/59 — не клавиши, а служебная
|
||||||
;; которой клавиатура сопровождает КАЖДЫЙ расширенный код, пока
|
;; обёртка, которой НЕКОТОРЫЕ клавиатуры сопровождают расширенный код
|
||||||
;; реально зажат Shift (см. _kbdraw.h). Отсюда состояние Shift
|
;; при зажатом Shift (см. _kbdraw.h). Используем её ТОЛЬКО как
|
||||||
;; определяется в обе стороны и не зависит от целости потока. ---
|
;; подтверждение «Shift зажат»; обратный вывод запрещён. ---
|
||||||
ld e, a ; E = код
|
ld e, a ; E = код
|
||||||
ld a, (__kbdraw_pending)
|
ld a, (__kbdraw_pending)
|
||||||
bit 1, a ; EXT?
|
bit 1, a ; EXT?
|
||||||
@@ -95,22 +95,9 @@ void _irq_tramp(void) __naked
|
|||||||
jr Z, tr_kbd_fake
|
jr Z, tr_kbd_fake
|
||||||
cp #0x59
|
cp #0x59
|
||||||
jr Z, tr_kbd_fake
|
jr Z, tr_kbd_fake
|
||||||
;; расширенная КЛАВИША: make без обёртки => Shift не зажат
|
;; расширенная КЛАВИША — обычный путь. О состоянии Shift по ней НЕ
|
||||||
ld a, (__kbdraw_pending)
|
;; судим (почему — см. tr_kbd_fake). A всё ещё = код.
|
||||||
bit 0, a ; break — состояние Shift не судим
|
jr tr_kbd_code
|
||||||
jr NZ, tr_kbd_fkclr
|
|
||||||
ld a, (__kbdraw_fakesh)
|
|
||||||
or a, a
|
|
||||||
jr NZ, tr_kbd_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)
|
|
||||||
tr_kbd_fkclr:
|
|
||||||
xor a, a
|
|
||||||
ld (__kbdraw_fakesh), a
|
|
||||||
ld a, e
|
|
||||||
jr tr_kbd_code ; дальше как обычная клавиша
|
|
||||||
|
|
||||||
tr_kbd_hub:
|
tr_kbd_hub:
|
||||||
;; Ретрансляторы: клавиатурный блок длиннее 127 байт, а jp внутри
|
;; Ретрансляторы: клавиатурный блок длиннее 127 байт, а jp внутри
|
||||||
@@ -124,13 +111,11 @@ void _irq_tramp(void) __naked
|
|||||||
jr tr_kbd_dss
|
jr tr_kbd_dss
|
||||||
|
|
||||||
tr_kbd_fake:
|
tr_kbd_fake:
|
||||||
;; E0 F0 12 / E0 12 — реальный Shift ЗАЖАТ. Бит взводит ОБЩИЙ
|
;; E0 F0 12 / E0 12 — реальный Shift ЗАЖАТ (обе формы обёртки шлются
|
||||||
;; писатель ниже: достаточно обнулить префиксы — тогда он положит
|
;; только пока он зажат). Бит взводит ОБЩИЙ писатель ниже: достаточно
|
||||||
;; код в PLAIN-половину карты (без +32) и именно взведёт бит (не
|
;; обнулить префиксы — тогда он положит код в PLAIN-половину карты
|
||||||
;; break). В расширенную половину не пишем: это не клавиша.
|
;; (без +32) и именно ВЗВЕДЁТ бит, а не снимет, даже у break-формы.
|
||||||
ld a, (__kbdraw_pending)
|
;; В расширенную половину не пишем: это не клавиша.
|
||||||
and #1 ; 1 = break-форма (идёт ПЕРЕД обёрнутым make)
|
|
||||||
ld (__kbdraw_fakesh), a
|
|
||||||
xor a, a
|
xor a, a
|
||||||
ld (__kbdraw_pending), a
|
ld (__kbdraw_pending), a
|
||||||
ld a, e
|
ld a, e
|
||||||
|
|||||||
+16
-17
@@ -23,25 +23,24 @@ extern uint8_t _kbdraw_pending; /* FSM декодера make/break, см. в
|
|||||||
extern uint8_t _kbdraw_overrun; /* 1 = ISR поймал Rx-overrun SIO (потерян
|
extern uint8_t _kbdraw_overrun; /* 1 = ISR поймал Rx-overrun SIO (потерян
|
||||||
* байт → возможно потерян break); снимает
|
* байт → возможно потерян break); снимает
|
||||||
* kbd_raw_sync (чистит _kbdraw_down) */
|
* kbd_raw_sync (чистит _kbdraw_down) */
|
||||||
/* «Fake shift» (замер на железе/MAME 2026-08-03). Пока реально зажат
|
/* «Fake shift». Часть PS/2-клавиатур, пока реально зажат Shift, обёртывает
|
||||||
* Shift, клавиатура PS/2 обёртывает КАЖДЫЙ расширенный код парой служебных
|
* расширенный код парой служебных посылок: перед make идёт E0 F0 12 (снять
|
||||||
* посылок: перед make идёт E0 F0 12 (снять fake shift), после break — E0 12
|
* fake shift), после break — E0 12 (вернуть). Это не клавиши, а прямое
|
||||||
* (вернуть). Это не клавиши, а прямое и непрерывное свидетельство того,
|
* свидетельство того, что Shift зажат, поэтому декодеры (трамплин и
|
||||||
* зажат ли Shift на самом деле, — и единственный способ узнать это, не
|
* kbd_raw_poll) взводят по обёртке plain-бит Shift.
|
||||||
* полагаясь на целость потока (PS/2 состояние опросить нельзя, а typematic
|
|
||||||
* повторяет только ПОСЛЕДНЮЮ нажатую клавишу, то есть стрелку, не Shift).
|
|
||||||
*
|
*
|
||||||
* Декодеры (трамплин и kbd_raw_poll) используют его в обе стороны:
|
* ОБРАТНЫЙ ВЫВОД ЗАПРЕЩЁН (замер 2026-08-05, BUG-GRAB-1). Раньше отсюда
|
||||||
* - увидели fake shift -> реальный Shift ЗАЖАТ -> взвести plain-бит;
|
* делался и второй вывод: «расширенный make БЕЗ обёртки => Shift отпущен,
|
||||||
* - расширенный make БЕЗ предшествующей обёртки -> Shift ОТПУЩЕН ->
|
* снять plain-биты». Он неверен: обёртку шлют не все клавиатуры (в MAME
|
||||||
* снять plain-биты обоих Shift.
|
* Sprinter — pc_kbd ms_naturl — при зажатом Shift поток на стрелку вверх
|
||||||
* Отсюда: залипший Shift снимается первым же нажатием стрелки, а
|
* ровно `E0 75 E0 75 …`, ни одного F0/12), и она заведомо не сопровождает
|
||||||
* kbd_raw_sync больше не обязан сбрасывать модификаторы «на всякий случай»
|
* typematic-повторы. Результат: КАЖДЫЙ повтор зажатой стрелки снимал
|
||||||
* (из-за чего Shift отваливался посреди игры — BUG-KBD-4).
|
* реально зажатый Shift, и Shift был неработоспособен вместе со стрелками.
|
||||||
*
|
*
|
||||||
* Хранит промежуточное состояние: 1 = разобрана break-форма (E0 F0 12),
|
* Состояние Shift ведётся его собственными make/break (12 / F0 12) — они
|
||||||
* ждём расширенный код, который она обёртывает. */
|
* приходят всегда; обёртка лишь подтверждает. Ценой этого потерянный при
|
||||||
extern uint8_t _kbdraw_fakesh;
|
* overrun'е break Shift снять нечем — лечится перенажатием Shift
|
||||||
|
* (см. kbd_raw_sync и BUG-KBD-3/4). */
|
||||||
|
|
||||||
/* Позиции бит модификаторов в карте (используют и декодеры на асме, и
|
/* Позиции бит модификаторов в карте (используют и декодеры на асме, и
|
||||||
* kbd_raw_sync): L Shift 0x12 -> байт 2 бит 2, R Shift 0x59 -> байт 11 бит 1. */
|
* kbd_raw_sync): L Shift 0x12 -> байт 2 бит 2, R Shift 0x59 -> байт 11 бит 1. */
|
||||||
|
|||||||
@@ -9,7 +9,4 @@ uint8_t _kbdraw_active;
|
|||||||
uint8_t _kbdraw_pending;
|
uint8_t _kbdraw_pending;
|
||||||
uint8_t _kbdraw_overrun; /* ISR взвёл при Rx-overrun SIO (потерян
|
uint8_t _kbdraw_overrun; /* ISR взвёл при Rx-overrun SIO (потерян
|
||||||
* break) — kbd_raw_sync снимет залипшее */
|
* break) — kbd_raw_sync снимет залипшее */
|
||||||
uint8_t _kbdraw_fakesh; /* декодирована break-форма fake shift —
|
|
||||||
* ждём расширенный код, который она
|
|
||||||
* обёртывает (см. _kbdraw.h) */
|
|
||||||
uint8_t _kbdraw_down[64]; /* БИТ на скан-код (см. _kbdraw.h) */
|
uint8_t _kbdraw_down[64]; /* БИТ на скан-код (см. _kbdraw.h) */
|
||||||
|
|||||||
+12
-27
@@ -12,12 +12,11 @@
|
|||||||
* акселератора держат DI на весь блит — сотни микросекунд. Импульс,
|
* акселератора держат DI на весь блит — сотни микросекунд. Импульс,
|
||||||
* попавший в такое окно, теряется НАСОВСЕМ: байт остаётся в FIFO до
|
* попавший в такое окно, теряется НАСОВСЕМ: байт остаётся в FIFO до
|
||||||
* следующего прерывания (следующий байт либо кадровое, 50 Гц);
|
* следующего прерывания (следующий байт либо кадровое, 50 Гц);
|
||||||
* - трафик от клавиатуры пачками: тап стрелки — 5 байт, а при зажатом
|
* - трафик от клавиатуры пачками: тап стрелки — 5 байт (E0 75 + E0 F0 75),
|
||||||
* Shift ещё и «fake shift» (E0 F0 12 / E0 12 вокруг расширенного кода)
|
* одновременное отпускание нескольких клавиш — больше. Три ячейки FIFO
|
||||||
* — по 5 байт на нажатие И на отпускание. Три ячейки FIFO такую пачку
|
* такую пачку не держат: потерянный make = «нажатие не сработало»,
|
||||||
* не держат: потерянный make = «нажатие не сработало», потерянный
|
* потерянный break = залипшая клавиша (её потом лечит kbd_raw_sync,
|
||||||
* break = залипшая клавиша (её потом лечит kbd_raw_sync, ценой сброса
|
* ценой сброса всех немодификаторных клавиш).
|
||||||
* всех немодификаторных клавиш).
|
|
||||||
* Опрос из главного цикла снимает зависимость от пойманного импульса: чем
|
* Опрос из главного цикла снимает зависимость от пойманного импульса: чем
|
||||||
* чаще позвали, тем меньше шанс, что в FIFO успеет накопиться четвёртый
|
* чаще позвали, тем меньше шанс, что в FIFO успеет накопиться четвёртый
|
||||||
* байт. Звать между фазами кадра (после тяжёлых блитов), НЕ раз в кадр.
|
* байт. Звать между фазами кадра (после тяжёлых блитов), НЕ раз в кадр.
|
||||||
@@ -66,8 +65,9 @@ uint8_t kbd_raw_poll(void) __naked
|
|||||||
jp Z, kp_setext ; (в отличие от трамплина, здесь jp разрешён)
|
jp Z, kp_setext ; (в отличие от трамплина, здесь jp разрешён)
|
||||||
|
|
||||||
;; --- «fake shift» — тот же разбор, что в трамплине (см. _kbdraw.h):
|
;; --- «fake shift» — тот же разбор, что в трамплине (см. _kbdraw.h):
|
||||||
;; расширенные 12/59 не клавиши, а обёртка вокруг расширенного кода,
|
;; расширенные 12/59 не клавиши, а служебная обёртка, которую
|
||||||
;; которую клавиатура шлёт, пока реально зажат Shift. ---
|
;; НЕКОТОРЫЕ клавиатуры шлют вокруг расширенного кода, пока реально
|
||||||
|
;; зажат Shift. Только подтверждение «зажат», обратного вывода нет. ---
|
||||||
ld e, a ; E = код
|
ld e, a ; E = код
|
||||||
ld a, (__kbdraw_pending)
|
ld a, (__kbdraw_pending)
|
||||||
bit 1, a ; EXT?
|
bit 1, a ; EXT?
|
||||||
@@ -77,29 +77,14 @@ uint8_t kbd_raw_poll(void) __naked
|
|||||||
jr Z, kp_fake
|
jr Z, kp_fake
|
||||||
cp #0x59
|
cp #0x59
|
||||||
jr Z, kp_fake
|
jr Z, kp_fake
|
||||||
;; расширенная КЛАВИША: make без обёртки => Shift не зажат
|
;; расширенная КЛАВИША — обычный путь. О состоянии Shift по ней НЕ
|
||||||
ld a, (__kbdraw_pending)
|
;; судим (почему — см. kp_fake). A всё ещё = код.
|
||||||
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
|
|
||||||
jr kp_code
|
jr kp_code
|
||||||
|
|
||||||
kp_fake:
|
kp_fake:
|
||||||
;; Бит взводит ОБЩИЙ писатель ниже: обнуляем префиксы — и он кладёт
|
;; Бит взводит ОБЩИЙ писатель ниже: обнуляем префиксы — и он кладёт
|
||||||
;; код в PLAIN-половину карты (без +32) и именно взведёт бит.
|
;; код в PLAIN-половину карты (без +32) и именно ВЗВЕДЁТ бит, а не
|
||||||
ld a, (__kbdraw_pending)
|
;; снимет, даже у break-формы обёртки.
|
||||||
and #1 ; 1 = break-форма (идёт ПЕРЕД обёрнутым make)
|
|
||||||
ld (__kbdraw_fakesh), a
|
|
||||||
xor a, a
|
xor a, a
|
||||||
ld (__kbdraw_pending), a
|
ld (__kbdraw_pending), a
|
||||||
ld a, e
|
ld a, e
|
||||||
|
|||||||
+12
-15
@@ -25,18 +25,18 @@
|
|||||||
* жмёт Shift+→ ожидая осторожный шаг, а Кид уходит в бег — в PoP это
|
* жмёт Shift+→ ожидая осторожный шаг, а Кид уходит в бег — в PoP это
|
||||||
* прямо стоит жизни (яма/пики). Хуже залипания (BUG-KBD-4).
|
* прямо стоит жизни (яма/пики). Хуже залипания (BUG-KBD-4).
|
||||||
*
|
*
|
||||||
* Третий вариант убирает сам размен. Те самые лишние байты — это «fake
|
* Выбран вариант (1) — модификаторы НЕ сбрасываем. Была и третья редакция,
|
||||||
* shift» (E0 F0 12 перед расширенным make, E0 12 после break), и шлются они
|
* обещавшая снять сам размен: «fake shift» (E0 F0 12 / E0 12) читался в обе
|
||||||
* ТОЛЬКО пока Shift реально зажат. Декодеры (_irq_tramp.c, kbd_raw_poll.c)
|
* стороны, и расширенный make без обёртки снимал Shift, то есть залипание
|
||||||
* теперь читают их в обе стороны: обёртка есть → Shift взвести, расширенный
|
* лечилось первым же тапом стрелки. Обратный вывод оказался неверным
|
||||||
* make без обёртки → Shift снять. Значит:
|
* (замер 2026-08-05, BUG-GRAB-1: обёртку шлют не все клавиатуры и никогда —
|
||||||
* - сбрасывать модификаторы здесь НЕ НАДО (состояние Shift подтверждается
|
* на typematic-повторах), и он ломал Shift вместе со стрелками; убран, см.
|
||||||
* независимо от того, что съел overrun);
|
* _kbdraw.h. Значит размен вернулся, и здесь он решается в пользу
|
||||||
* - залипание невозможно: первый же тап стрелки снимет ложный Shift.
|
* «лучше залипание, чем отвал»: залипший модификатор снимается его же
|
||||||
|
* перенажатием, а сорванный посреди игры Shift в PoP стоит жизни.
|
||||||
*
|
*
|
||||||
* Ctrl/Alt fake-shift'ом не покрыты (клавиатура их не обёртывает). Их
|
* Overrun теперь редок сам по себе: FIFO вычерпывается опросом из главного
|
||||||
* оставляем по тому же принципу «лучше залипание, чем отвал»: залипший
|
* цикла (kbd_raw_poll, KBD-1), а не только по пойманному прерыванию.
|
||||||
* Ctrl/Alt в PoP не управляет ничем критичным и снимается перенажатием.
|
|
||||||
*/
|
*/
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "_kbdraw.h"
|
#include "_kbdraw.h"
|
||||||
@@ -67,9 +67,6 @@ void kbd_raw_sync(void)
|
|||||||
if (save[i]) _kbdraw_down[c >> 3] |= (uint8_t)(1 << (c & 7));
|
if (save[i]) _kbdraw_down[c >> 3] |= (uint8_t)(1 << (c & 7));
|
||||||
}
|
}
|
||||||
/* FSM префиксов трамплин уже сбросил; дублируем на случай overrun'а,
|
/* FSM префиксов трамплин уже сбросил; дублируем на случай overrun'а,
|
||||||
* пойманного вне его (kbd_raw_poll между кадрами). Незакрытую обёртку
|
* пойманного вне его (kbd_raw_poll между кадрами). */
|
||||||
* fake shift тоже считаем недействительной: её вторая половина могла
|
|
||||||
* пропасть вместе с потерянными байтами. */
|
|
||||||
_kbdraw_pending = 0;
|
_kbdraw_pending = 0;
|
||||||
_kbdraw_fakesh = 0;
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user