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>
This commit is contained in:
+82
-15
@@ -46,12 +46,12 @@ void _irq_tramp(void) __naked
|
|||||||
push af ; сохранить прерванный AF
|
push af ; сохранить прерванный AF
|
||||||
in a, (#_IRQ_SIO_A)
|
in a, (#_IRQ_SIO_A)
|
||||||
rrca ; бит 0 — клавиатурный байт принят?
|
rrca ; бит 0 — клавиатурный байт принят?
|
||||||
jr NC, tr_notkbd
|
jr NC, tr_hub_notkbd ; не клавиатура (через ретранслятор — см. ниже)
|
||||||
|
|
||||||
;; --- клавиатура ---
|
;; --- клавиатура ---
|
||||||
ld a, (__kbdraw_active)
|
ld a, (__kbdraw_active)
|
||||||
or a, a
|
or a, a
|
||||||
jr Z, tr_kbd_dss ; raw выключен — прежнее поведение
|
jr Z, tr_hub_dss ; raw выключен — прежнее поведение
|
||||||
|
|
||||||
;; --- raw включён: байт наш целиком, DSS его не увидит ---
|
;; --- raw включён: байт наш целиком, DSS его не увидит ---
|
||||||
;; ВЫЧЕРПЫВАЕМ FIFO SIO В ЦИКЛЕ (как эталон keybIntHandler /
|
;; ВЫЧЕРПЫВАЕМ FIFO SIO В ЦИКЛЕ (как эталон keybIntHandler /
|
||||||
@@ -63,18 +63,85 @@ void _irq_tramp(void) __naked
|
|||||||
push hl
|
push hl
|
||||||
push de
|
push de
|
||||||
push bc ; маска бита строится в BC (см. ниже)
|
push bc ; маска бита строится в BC (см. ниже)
|
||||||
|
;; РАСКЛАДКА ЦИКЛА. Все переходы обязаны быть jr (см. шапку:
|
||||||
|
;; COPY-SAFE), а тело выросло за 127 байт — поэтому префиксные
|
||||||
|
;; обработчики стоят СРАЗУ за своими cp, а возврат на начало цикла
|
||||||
|
;; идёт через ретранслятор tr_kbd_hub посередине тела.
|
||||||
tr_kbd_drain:
|
tr_kbd_drain:
|
||||||
in a, (#_IRQ_SIO_DATA) ; читаем — тем самым дренируем байт
|
in a, (#_IRQ_SIO_DATA) ; читаем — тем самым дренируем байт
|
||||||
cp #0xF0
|
cp #0xF0
|
||||||
jr Z, tr_kbd_setbrk
|
jr NZ, tr_kbd_nF0
|
||||||
|
ld hl, #__kbdraw_pending
|
||||||
|
set 0, (hl)
|
||||||
|
jr tr_kbd_hub
|
||||||
|
tr_kbd_nF0:
|
||||||
cp #0xE0
|
cp #0xE0
|
||||||
jr Z, tr_kbd_setext
|
jr NZ, tr_kbd_nE0
|
||||||
|
ld hl, #__kbdraw_pending
|
||||||
|
set 1, (hl)
|
||||||
|
jr tr_kbd_hub
|
||||||
|
tr_kbd_nE0:
|
||||||
|
|
||||||
|
;; --- «fake shift»: расширенные коды 12/59 — не клавиши, а обёртка,
|
||||||
|
;; которой клавиатура сопровождает КАЖДЫЙ расширенный код, пока
|
||||||
|
;; реально зажат Shift (см. _kbdraw.h). Отсюда состояние Shift
|
||||||
|
;; определяется в обе стороны и не зависит от целости потока. ---
|
||||||
|
ld e, a ; E = код
|
||||||
|
ld a, (__kbdraw_pending)
|
||||||
|
bit 1, a ; EXT?
|
||||||
|
ld a, e ; A = код обратно (ld флаги не трогает)
|
||||||
|
jr Z, tr_kbd_code ; обычный код — прежний путь
|
||||||
|
cp #0x12
|
||||||
|
jr Z, tr_kbd_fake
|
||||||
|
cp #0x59
|
||||||
|
jr Z, tr_kbd_fake
|
||||||
|
;; расширенная КЛАВИША: make без обёртки => Shift не зажат
|
||||||
|
ld a, (__kbdraw_pending)
|
||||||
|
bit 0, a ; break — состояние Shift не судим
|
||||||
|
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:
|
||||||
|
;; Ретрансляторы: клавиатурный блок длиннее 127 байт, а jp внутри
|
||||||
|
;; трамплина запрещён (копия в W2, см. шапку). Стоят посередине
|
||||||
|
;; тела — так дотягиваются и переходы сверху, и снизу. Каждый идёт
|
||||||
|
;; после безусловного jr, провалиться сюда нельзя.
|
||||||
|
jr tr_kbd_drain
|
||||||
|
tr_hub_notkbd:
|
||||||
|
jr tr_notkbd
|
||||||
|
tr_hub_dss:
|
||||||
|
jr tr_kbd_dss
|
||||||
|
|
||||||
|
tr_kbd_fake:
|
||||||
|
;; E0 F0 12 / E0 12 — реальный Shift ЗАЖАТ. Бит взводит ОБЩИЙ
|
||||||
|
;; писатель ниже: достаточно обнулить префиксы — тогда он положит
|
||||||
|
;; код в PLAIN-половину карты (без +32) и именно взведёт бит (не
|
||||||
|
;; break). В расширенную половину не пишем: это не клавиша.
|
||||||
|
ld a, (__kbdraw_pending)
|
||||||
|
and #1 ; 1 = break-форма (идёт ПЕРЕД обёрнутым make)
|
||||||
|
ld (__kbdraw_fakesh), a
|
||||||
|
xor a, a
|
||||||
|
ld (__kbdraw_pending), a
|
||||||
|
ld a, e
|
||||||
|
;; fall through в tr_kbd_code
|
||||||
|
|
||||||
;; --- обычный байт кода: БИТ code&7 в байте _kbdraw_down[code>>3]
|
;; --- обычный байт кода: БИТ code&7 в байте _kbdraw_down[code>>3]
|
||||||
;; (карта упакована по битам: 64 Б вместо 512, см. _kbdraw.h).
|
;; (карта упакована по битам: 64 Б вместо 512, см. _kbdraw.h).
|
||||||
;; Маску строим сдвигом, а НЕ таблицей: таблица потребовала бы
|
;; Маску строим сдвигом, а НЕ таблицей: таблица потребовала бы
|
||||||
;; `ld hl,#метка` внутри трамплина, а он копируется в W2 побайтно
|
;; `ld hl,#метка` внутри трамплина, а он копируется в W2 побайтно
|
||||||
;; и обязан быть без абсолютных само-ссылок (см. шапку). ---
|
;; и обязан быть без абсолютных само-ссылок (см. шапку). ---
|
||||||
|
tr_kbd_code:
|
||||||
ld e, a ; E = код
|
ld e, a ; E = код
|
||||||
and #7
|
and #7
|
||||||
ld b, a
|
ld b, a
|
||||||
@@ -115,26 +182,24 @@ void _irq_tramp(void) __naked
|
|||||||
ld (hl), a
|
ld (hl), a
|
||||||
xor a, a
|
xor a, a
|
||||||
ld (__kbdraw_pending), a ; сброс накопленных префиксов
|
ld (__kbdraw_pending), a ; сброс накопленных префиксов
|
||||||
jr tr_kbd_next
|
|
||||||
|
|
||||||
tr_kbd_setbrk:
|
|
||||||
ld hl, #__kbdraw_pending
|
|
||||||
set 0, (hl)
|
|
||||||
jr tr_kbd_next
|
|
||||||
tr_kbd_setext:
|
|
||||||
ld hl, #__kbdraw_pending
|
|
||||||
set 1, (hl)
|
|
||||||
;; fall through в tr_kbd_next
|
;; fall through в tr_kbd_next
|
||||||
tr_kbd_next:
|
tr_kbd_next:
|
||||||
in a, (#_IRQ_SIO_A) ; ещё байт в FIFO?
|
in a, (#_IRQ_SIO_A) ; ещё байт в FIFO?
|
||||||
rrca ; бит 0 → CF
|
rrca ; бит 0 → CF
|
||||||
jr C, tr_kbd_drain ; да — вычерпать следующий
|
jr C, tr_kbd_hub ; да — вычерпать следующий (через ретранслятор)
|
||||||
|
|
||||||
;; --- FIFO опустошён: проверить Rx-overrun (потерян ли байт?) ---
|
;; --- FIFO опустошён: проверить Rx-overrun (потерян ли байт?) ---
|
||||||
;; RR1 SIO бит5 = Rx Overrun Error. При overrun break-код мог
|
;; RR1 SIO бит5 = Rx Overrun Error. При overrun break-код мог
|
||||||
;; потеряться (клавиша «залипнет») → взводим _kbdraw_overrun, чтобы
|
;; потеряться (клавиша «залипнет») → взводим _kbdraw_overrun, чтобы
|
||||||
;; kbd_raw_sync() в главном цикле снял held-состояние; и Error Reset
|
;; kbd_raw_sync() в главном цикле снял held-состояние; и Error Reset
|
||||||
;; (WR0 команда 0x30), иначе бит залипнет. (z84c15 SIO, Z80-совм.)
|
;; (WR0 команда 0x30), иначе бит залипнет. (z84c15 SIO, Z80-совм.)
|
||||||
|
;;
|
||||||
|
;; СБРОС _kbdraw_pending — по образцу DSS (KBD_Receiver_Overrun в
|
||||||
|
;; docs/sources/Estex-DSS/DSS/KEYINTER.ASM чистит KEY_FLG, свой FSM
|
||||||
|
;; префиксов E0/F0). Без этого потерянный 0xF0/0xE0 оставляет
|
||||||
|
;; префикс висеть, и СЛЕДУЮЩИЙ код декодируется наоборот
|
||||||
|
;; (make↔break) или уходит в расширенную половину карты — стойкий
|
||||||
|
;; рассинхрон, который лечится только новым нажатием (BUG-KBD-3).
|
||||||
ld a, #1
|
ld a, #1
|
||||||
out (#_IRQ_SIO_A), a ; указатель на RR1
|
out (#_IRQ_SIO_A), a ; указатель на RR1
|
||||||
in a, (#_IRQ_SIO_A) ; читаем RR1 (указатель авто-сброс в 0)
|
in a, (#_IRQ_SIO_A) ; читаем RR1 (указатель авто-сброс в 0)
|
||||||
@@ -142,7 +207,9 @@ void _irq_tramp(void) __naked
|
|||||||
jr Z, tr_kbd_noovr
|
jr Z, tr_kbd_noovr
|
||||||
ld a, #0x30
|
ld a, #0x30
|
||||||
out (#_IRQ_SIO_A), a ; WR0 Error Reset
|
out (#_IRQ_SIO_A), a ; WR0 Error Reset
|
||||||
ld a, #1
|
xor a, a
|
||||||
|
ld (__kbdraw_pending), a ; префиксы недействительны
|
||||||
|
inc a
|
||||||
ld (__kbdraw_overrun), a
|
ld (__kbdraw_overrun), a
|
||||||
tr_kbd_noovr:
|
tr_kbd_noovr:
|
||||||
pop bc
|
pop bc
|
||||||
|
|||||||
@@ -23,6 +23,33 @@ 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). Пока реально зажат
|
||||||
|
* Shift, клавиатура PS/2 обёртывает КАЖДЫЙ расширенный код парой служебных
|
||||||
|
* посылок: перед make идёт E0 F0 12 (снять fake shift), после break — E0 12
|
||||||
|
* (вернуть). Это не клавиши, а прямое и непрерывное свидетельство того,
|
||||||
|
* зажат ли Shift на самом деле, — и единственный способ узнать это, не
|
||||||
|
* полагаясь на целость потока (PS/2 состояние опросить нельзя, а typematic
|
||||||
|
* повторяет только ПОСЛЕДНЮЮ нажатую клавишу, то есть стрелку, не Shift).
|
||||||
|
*
|
||||||
|
* Декодеры (трамплин и kbd_raw_poll) используют его в обе стороны:
|
||||||
|
* - увидели fake shift -> реальный Shift ЗАЖАТ -> взвести plain-бит;
|
||||||
|
* - расширенный make БЕЗ предшествующей обёртки -> Shift ОТПУЩЕН ->
|
||||||
|
* снять plain-биты обоих Shift.
|
||||||
|
* Отсюда: залипший Shift снимается первым же нажатием стрелки, а
|
||||||
|
* kbd_raw_sync больше не обязан сбрасывать модификаторы «на всякий случай»
|
||||||
|
* (из-за чего Shift отваливался посреди игры — BUG-KBD-4).
|
||||||
|
*
|
||||||
|
* Хранит промежуточное состояние: 1 = разобрана break-форма (E0 F0 12),
|
||||||
|
* ждём расширенный код, который она обёртывает. */
|
||||||
|
extern uint8_t _kbdraw_fakesh;
|
||||||
|
|
||||||
|
/* Позиции бит модификаторов в карте (используют и декодеры на асме, и
|
||||||
|
* kbd_raw_sync): L Shift 0x12 -> байт 2 бит 2, R Shift 0x59 -> байт 11 бит 1. */
|
||||||
|
#define _KBDRAW_LSH_BYTE 2
|
||||||
|
#define _KBDRAW_LSH_BIT 2
|
||||||
|
#define _KBDRAW_RSH_BYTE 11
|
||||||
|
#define _KBDRAW_RSH_BIT 1
|
||||||
|
|
||||||
/* Held-состояние: БИТ на скан-код, код>>3 = байт, код&7 = номер бита;
|
/* Held-состояние: БИТ на скан-код, код>>3 = байт, код&7 = номер бита;
|
||||||
* коды 0..255 обычные, 256..511 — расширенные (префикс 0xE0), т.е.
|
* коды 0..255 обычные, 256..511 — расширенные (префикс 0xE0), т.е.
|
||||||
* смещение +32 байта. Упаковка вместо байта на код: 64 Б вместо 512 —
|
* смещение +32 байта. Упаковка вместо байта на код: 64 Б вместо 512 —
|
||||||
|
|||||||
@@ -9,4 +9,7 @@ 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) */
|
||||||
|
|||||||
+49
-4
@@ -61,13 +61,54 @@ uint8_t kbd_raw_poll(void) __naked
|
|||||||
kp_drain:
|
kp_drain:
|
||||||
in a, (#_IRQ_SIO_DATA) ; чтение деструктивно = дренаж байта
|
in a, (#_IRQ_SIO_DATA) ; чтение деструктивно = дренаж байта
|
||||||
cp #0xF0
|
cp #0xF0
|
||||||
jr Z, kp_setbrk
|
jp Z, kp_setbrk ; jp, а не jr: тело цикла длиннее 127 Б (fake shift)
|
||||||
cp #0xE0
|
cp #0xE0
|
||||||
jr Z, kp_setext
|
jp Z, kp_setext ; (в отличие от трамплина, здесь jp разрешён)
|
||||||
|
|
||||||
|
;; --- «fake shift» — тот же разбор, что в трамплине (см. _kbdraw.h):
|
||||||
|
;; расширенные 12/59 не клавиши, а обёртка вокруг расширенного кода,
|
||||||
|
;; которую клавиатура шлёт, пока реально зажат Shift. ---
|
||||||
|
ld e, a ; E = код
|
||||||
|
ld a, (__kbdraw_pending)
|
||||||
|
bit 1, a ; EXT?
|
||||||
|
ld a, e ; A = код обратно (ld флаги не трогает)
|
||||||
|
jr Z, kp_code ; обычный код — прежний путь
|
||||||
|
cp #0x12
|
||||||
|
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
|
||||||
|
jr kp_code
|
||||||
|
|
||||||
|
kp_fake:
|
||||||
|
;; Бит взводит ОБЩИЙ писатель ниже: обнуляем префиксы — и он кладёт
|
||||||
|
;; код в PLAIN-половину карты (без +32) и именно взведёт бит.
|
||||||
|
ld a, (__kbdraw_pending)
|
||||||
|
and #1 ; 1 = break-форма (идёт ПЕРЕД обёрнутым make)
|
||||||
|
ld (__kbdraw_fakesh), a
|
||||||
|
xor a, a
|
||||||
|
ld (__kbdraw_pending), a
|
||||||
|
ld a, e
|
||||||
|
;; fall through в kp_code
|
||||||
|
|
||||||
;; --- обычный байт кода: БИТ code&7 в _kbdraw_down[code>>3].
|
;; --- обычный байт кода: БИТ code&7 в _kbdraw_down[code>>3].
|
||||||
;; Маска сдвигом, а не таблицей — ровно как в трамплине, чтобы
|
;; Маска сдвигом, а не таблицей — ровно как в трамплине, чтобы
|
||||||
;; расхождения декодеров были невозможны by construction. ---
|
;; расхождения декодеров были невозможны by construction. ---
|
||||||
|
kp_code:
|
||||||
ld e, a ; E = код
|
ld e, a ; E = код
|
||||||
and #7
|
and #7
|
||||||
ld b, a
|
ld b, a
|
||||||
@@ -120,7 +161,7 @@ uint8_t kbd_raw_poll(void) __naked
|
|||||||
kp_next:
|
kp_next:
|
||||||
in a, (#_IRQ_SIO_A)
|
in a, (#_IRQ_SIO_A)
|
||||||
rrca ; ещё байт в FIFO?
|
rrca ; ещё байт в FIFO?
|
||||||
jr C, kp_drain
|
jp C, kp_drain
|
||||||
|
|
||||||
;; --- FIFO пуст: тот же разбор Rx-overrun, что и в трамплине.
|
;; --- FIFO пуст: тот же разбор Rx-overrun, что и в трамплине.
|
||||||
;; RR1 бит5 = Rx Overrun; без Error Reset (WR0 = 0x30) бит залипнет
|
;; RR1 бит5 = Rx Overrun; без Error Reset (WR0 = 0x30) бит залипнет
|
||||||
@@ -132,7 +173,11 @@ uint8_t kbd_raw_poll(void) __naked
|
|||||||
jr Z, kp_noovr
|
jr Z, kp_noovr
|
||||||
ld a, #0x30
|
ld a, #0x30
|
||||||
out (#_IRQ_SIO_A), a ; WR0 Error Reset
|
out (#_IRQ_SIO_A), a ; WR0 Error Reset
|
||||||
ld a, #1
|
;; Сброс FSM префиксов — как KBD_Receiver_Overrun у DSS (чистит
|
||||||
|
;; KEY_FLG): повисший 0xF0/0xE0 инвертировал бы следующий код.
|
||||||
|
xor a, a
|
||||||
|
ld (__kbdraw_pending), a
|
||||||
|
inc a
|
||||||
ld (__kbdraw_overrun), a ; снимет kbd_raw_sync()
|
ld (__kbdraw_overrun), a ; снимет kbd_raw_sync()
|
||||||
kp_noovr:
|
kp_noovr:
|
||||||
pop bc
|
pop bc
|
||||||
|
|||||||
+39
-14
@@ -3,21 +3,40 @@
|
|||||||
*
|
*
|
||||||
* Аппаратный приёмный FIFO SIO — 3 байта. Если прерывания запрещены
|
* Аппаратный приёмный FIFO SIO — 3 байта. Если прерывания запрещены
|
||||||
* достаточно долго (DI-окна графики/кадрового IRQ), а в этот момент
|
* достаточно долго (DI-окна графики/кадрового IRQ), а в этот момент
|
||||||
* приходит пачка скан-кодов (напр. быстрый тап стрелки = E0 74 + E0 F0 74,
|
* приходит пачка скан-кодов — FIFO переполняется, байт(ы) теряются
|
||||||
* 5 байт) — FIFO переполняется, байт(ы) теряются аппаратно. Потерянный
|
* аппаратно. Потерянный break → клавиша «залипает» в _kbdraw_down.
|
||||||
* break → клавиша «залипает» в _kbdraw_down.
|
|
||||||
*
|
*
|
||||||
* Трамплин ловит overrun (RR1 бит5) и взводит _kbdraw_overrun. Приложение
|
* Трамплин (и kbd_raw_poll) ловят overrun (RR1 бит5): делают Error Reset,
|
||||||
* зовёт kbd_raw_sync() раз в кадр: при взведённом флаге сбрасывается
|
* чистят FSM префиксов _kbdraw_pending и взводят _kbdraw_overrun.
|
||||||
* held-состояние ВСЕХ клавиш, КРОМЕ модификаторов (Shift/Ctrl/Alt —
|
* Приложение зовёт kbd_raw_sync() раз в кадр — по флагу сбрасывается
|
||||||
* таблица ниже). Обычные реально зажатые клавиши «перечитаются» их
|
* held-состояние карты. Реально зажатые клавиши перечитаются make-повтором
|
||||||
* make-повтором (typematic) за ~0.1 с. Модификаторы НЕ сбрасываем
|
* (typematic) за ~0.1 с.
|
||||||
* принципиально: PS/2 автоповторяет только ПОСЛЕДНЮЮ нажатую клавишу,
|
*
|
||||||
* удерживаемый Shift при повторе стрелки не шлёт НИЧЕГО — сброшенный
|
* МОДИФИКАТОРЫ ИЗ СБРОСА ИСКЛЮЧЕНЫ (2026-08-03, третья редакция). История
|
||||||
* модификатор невозможно перечитать, он «отваливался» до перенажатия
|
* вопроса — две неудачные крайности:
|
||||||
* (баг PoP: Shift+→ = осторожный шаг превращался в бег, 2026-07-22).
|
*
|
||||||
* Цена: если overrun потерял именно break модификатора, тот залипнет до
|
* 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 <string.h>
|
||||||
#include "_kbdraw.h"
|
#include "_kbdraw.h"
|
||||||
@@ -47,4 +66,10 @@ void kbd_raw_sync(void)
|
|||||||
uint16_t c = _kbd_mod_codes[i];
|
uint16_t c = _kbd_mod_codes[i];
|
||||||
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'а,
|
||||||
|
* пойманного вне его (kbd_raw_poll между кадрами). Незакрытую обёртку
|
||||||
|
* fake shift тоже считаем недействительной: её вторая половина могла
|
||||||
|
* пропасть вместе с потерянными байтами. */
|
||||||
|
_kbdraw_pending = 0;
|
||||||
|
_kbdraw_fakesh = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user