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
|
||||
in a, (#_IRQ_SIO_A)
|
||||
rrca ; бит 0 — клавиатурный байт принят?
|
||||
jr NC, tr_notkbd
|
||||
jr NC, tr_hub_notkbd ; не клавиатура (через ретранслятор — см. ниже)
|
||||
|
||||
;; --- клавиатура ---
|
||||
ld a, (__kbdraw_active)
|
||||
or a, a
|
||||
jr Z, tr_kbd_dss ; raw выключен — прежнее поведение
|
||||
jr Z, tr_hub_dss ; raw выключен — прежнее поведение
|
||||
|
||||
;; --- raw включён: байт наш целиком, DSS его не увидит ---
|
||||
;; ВЫЧЕРПЫВАЕМ FIFO SIO В ЦИКЛЕ (как эталон keybIntHandler /
|
||||
@@ -63,18 +63,85 @@ void _irq_tramp(void) __naked
|
||||
push hl
|
||||
push de
|
||||
push bc ; маска бита строится в BC (см. ниже)
|
||||
;; РАСКЛАДКА ЦИКЛА. Все переходы обязаны быть jr (см. шапку:
|
||||
;; COPY-SAFE), а тело выросло за 127 байт — поэтому префиксные
|
||||
;; обработчики стоят СРАЗУ за своими cp, а возврат на начало цикла
|
||||
;; идёт через ретранслятор tr_kbd_hub посередине тела.
|
||||
tr_kbd_drain:
|
||||
in a, (#_IRQ_SIO_DATA) ; читаем — тем самым дренируем байт
|
||||
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
|
||||
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]
|
||||
;; (карта упакована по битам: 64 Б вместо 512, см. _kbdraw.h).
|
||||
;; Маску строим сдвигом, а НЕ таблицей: таблица потребовала бы
|
||||
;; `ld hl,#метка` внутри трамплина, а он копируется в W2 побайтно
|
||||
;; и обязан быть без абсолютных само-ссылок (см. шапку). ---
|
||||
tr_kbd_code:
|
||||
ld e, a ; E = код
|
||||
and #7
|
||||
ld b, a
|
||||
@@ -115,26 +182,24 @@ void _irq_tramp(void) __naked
|
||||
ld (hl), a
|
||||
xor a, 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
|
||||
tr_kbd_next:
|
||||
in a, (#_IRQ_SIO_A) ; ещё байт в FIFO?
|
||||
rrca ; бит 0 → CF
|
||||
jr C, tr_kbd_drain ; да — вычерпать следующий
|
||||
jr C, tr_kbd_hub ; да — вычерпать следующий (через ретранслятор)
|
||||
|
||||
;; --- FIFO опустошён: проверить Rx-overrun (потерян ли байт?) ---
|
||||
;; RR1 SIO бит5 = Rx Overrun Error. При overrun break-код мог
|
||||
;; потеряться (клавиша «залипнет») → взводим _kbdraw_overrun, чтобы
|
||||
;; kbd_raw_sync() в главном цикле снял held-состояние; и Error Reset
|
||||
;; (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
|
||||
out (#_IRQ_SIO_A), a ; указатель на RR1
|
||||
in a, (#_IRQ_SIO_A) ; читаем RR1 (указатель авто-сброс в 0)
|
||||
@@ -142,7 +207,9 @@ void _irq_tramp(void) __naked
|
||||
jr Z, tr_kbd_noovr
|
||||
ld a, #0x30
|
||||
out (#_IRQ_SIO_A), a ; WR0 Error Reset
|
||||
ld a, #1
|
||||
xor a, a
|
||||
ld (__kbdraw_pending), a ; префиксы недействительны
|
||||
inc a
|
||||
ld (__kbdraw_overrun), a
|
||||
tr_kbd_noovr:
|
||||
pop bc
|
||||
|
||||
Reference in New Issue
Block a user