libc/kbd: recovery по Rx-overrun SIO (залипание клавиш) + kbd_raw_sync

Симптом (интермиттентный): при отпускании shift+стрелка иногда стрелка
залипает.  Диагностика: на чистом одновременном release break-коды
обрабатываются верно (проверено MCP) → drain-логика ISR корректна.
Остаточное залипание = переполнение 3-байтного аппаратного FIFO SIO при
пачке скан-кодов (F0 12 E0 F0 74 = 5 байт) во время длинных DI-окон →
потерян break → залипание.

Фикс: трамплин после drain читает RR1 SIO (бит5 = Rx Overrun), при
overrun делает Error Reset (WR0=0x30) и взводит _kbdraw_overrun.
Новый kbd_raw_sync() (звать раз в кадр) по флагу сбрасывает всё
held-состояние _kbdraw_down (какой break потерян — неизвестно; реально
зажатые перечитаются).  pop_ctrl_tick зовёт kbd_raw_sync().  Буфер W2-
трамплина 288→320 (трамплин 244 Б).

ВНИМАНИЕ: путь overrun НЕ проверен детерминированно (баг интермиттентный,
зависит от тайминга DI) — ТРЕБУЕТ ПРОВЕРКИ на железе/в длинной сессии.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-18 12:26:42 +03:00
parent cbae48dbc4
commit 3c0baacbf6
7 changed files with 60 additions and 4 deletions
+7
View File
@@ -46,6 +46,13 @@ void kbd_raw_close(void);
* на проводе) прибавить KBD_EXT к базовому коду. */
uint8_t kbd_raw_down(uint16_t code);
/* Восстановление после Rx-overrun SIO: звать РАЗ В КАДР (до чтения
* kbd_raw_down). Аппаратный FIFO SIO 3 байта; при длинных DI-окнах пачка
* скан-кодов (напр. shift+стрелка release) переполняет его → потерян break
* → залипшая клавиша. Трамплин ловит overrun и взводит флаг; kbd_raw_sync
* по флагу сбрасывает ВСЁ held-состояние (реально зажатые перечитаются). */
void kbd_raw_sync(void);
#define KBD_EXT 0x0100 /* база кода была расширенной (0xE0-префикс) */
/* Позиционные коды PS/2 Set 2 — то, что реально нужно платформеру.
+4 -4
View File
@@ -30,10 +30,10 @@
#define _IRQ_VEC_BUF_SIZE (256 + 257)
/* Буфер под W2-копию кадрового трамплина (small/huge — _irq_tramp в
* W1). С запасом над реальным размером трамплина (225 Б после
* FIFO-drain цикла в raw-клавиатурной ветке, 2026-07-16 проверять
* __irq_tramp_end при правках, запас держать ≥ 60 Б). */
#define _IRQ_TRAMP_BUF_SIZE 288
* W1). С запасом над реальным размером трамплина (244 Б после
* FIFO-drain + overrun-recovery в raw-клавиатурной ветке, 2026-07-18
* проверять __irq_tramp_end при правках, запас держать ≥ 60 Б). */
#define _IRQ_TRAMP_BUF_SIZE 320
extern uint8_t _irq_vec_buf[_IRQ_VEC_BUF_SIZE];
+16
View File
@@ -104,6 +104,22 @@ void _irq_tramp(void) __naked
in a, (#_IRQ_SIO_A) ; ещё байт в FIFO?
rrca ; бит 0 CF
jr C, tr_kbd_drain ; да вычерпать следующий
;; --- FIFO опустошён: проверить Rx-overrun (потерян ли байт?) ---
;; RR1 SIO бит5 = Rx Overrun Error. При overrun break-код мог
;; потеряться (клавиша «залипнет») взводим _kbdraw_overrun, чтобы
;; kbd_raw_sync() в главном цикле снял held-состояние; и Error Reset
;; (WR0 команда 0x30), иначе бит залипнет. (z84c15 SIO, Z80-совм.)
ld a, #1
out (#_IRQ_SIO_A), a ; указатель на RR1
in a, (#_IRQ_SIO_A) ; читаем RR1 (указатель авто-сброс в 0)
bit 5, a
jr Z, tr_kbd_noovr
ld a, #0x30
out (#_IRQ_SIO_A), a ; WR0 Error Reset
ld a, #1
ld (__kbdraw_overrun), a
tr_kbd_noovr:
pop de
pop hl
pop af
+3
View File
@@ -20,6 +20,9 @@
extern uint8_t _kbdraw_active; /* 0 = raw выключен (дефолт, трамплин
* ведёт себя как раньше — прямо к DSS) */
extern uint8_t _kbdraw_pending; /* FSM декодера make/break, см. выше */
extern uint8_t _kbdraw_overrun; /* 1 = ISR поймал Rx-overrun SIO (потерян
* байт → возможно потерян break); снимает
* kbd_raw_sync (чистит _kbdraw_down) */
extern uint8_t _kbdraw_down[512]; /* 0/1 по коду; +256 для расширенных
* (префикс 0xE0) — см. kbd_raw_down */
+2
View File
@@ -7,4 +7,6 @@
uint8_t _kbdraw_active;
uint8_t _kbdraw_pending;
uint8_t _kbdraw_overrun; /* ISR взвёл при Rx-overrun SIO (потерян
* break) — kbd_raw_sync снимет залипшее */
uint8_t _kbdraw_down[512];
+27
View File
@@ -0,0 +1,27 @@
/*
* kbd_raw_sync.c — восстановление held-состояния после Rx-overrun SIO.
*
* Аппаратный приёмный FIFO SIO — 3 байта. Если прерывания запрещены
* достаточно долго (DI-окна графики/кадрового IRQ), а в этот момент
* приходит пачка скан-кодов (напр. одновременное отпускание shift+стрелка
* = F0 12 E0 F0 74, 5 байт) — FIFO переполняется, байт(ы) теряются
* аппаратно. Потерянный break → клавиша «залипает» в _kbdraw_down.
*
* Трамплин ловит overrun (RR1 бит5) и взводит _kbdraw_overrun. Приложение
* зовёт kbd_raw_sync() раз в кадр: при взведённом флаге сбрасывает ВСЁ
* held-состояние (какой именно break потерян — неизвестно). Реально
* зажатые клавиши «перечитаются» их make-повтором/следующим нажатием.
*
* ВНИМАНИЕ: путь overrun-recovery НЕ проверен детерминированно (баг
* интермиттентный, зависит от тайминга DI) — требует проверки на железе.
*/
#include <string.h>
#include "_kbdraw.h"
void kbd_raw_sync(void)
{
if (_kbdraw_overrun) {
_kbdraw_overrun = 0;
memset(_kbdraw_down, 0, sizeof(_kbdraw_down));
}
}