From 3c0baacbf61ab2189004d4c70fe93e258a4ec178 Mon Sep 17 00:00:00 2001 From: Alexander Petrov Date: Sat, 18 Jul 2026 12:26:42 +0300 Subject: [PATCH] =?UTF-8?q?libc/kbd:=20recovery=20=D0=BF=D0=BE=20Rx-overru?= =?UTF-8?q?n=20SIO=20(=D0=B7=D0=B0=D0=BB=D0=B8=D0=BF=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=BA=D0=BB=D0=B0=D0=B2=D0=B8=D1=88)=20+=20kbd=5Fraw?= =?UTF-8?q?=5Fsync?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Симптом (интермиттентный): при отпускании 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 --- applications/PoP/roomtest/pop_ctrl.c | 1 + libc/include/kbd_raw.h | 7 +++++++ libc/irq/_irq.h | 8 ++++---- libc/irq/_irq_tramp.c | 16 ++++++++++++++++ libc/kbd/_kbdraw.h | 3 +++ libc/kbd/_kbdraw_state.c | 2 ++ libc/kbd/kbd_raw_sync.c | 27 +++++++++++++++++++++++++++ 7 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 libc/kbd/kbd_raw_sync.c diff --git a/applications/PoP/roomtest/pop_ctrl.c b/applications/PoP/roomtest/pop_ctrl.c index 05baddc..bfc8e6e 100644 --- a/applications/PoP/roomtest/pop_ctrl.c +++ b/applications/PoP/roomtest/pop_ctrl.c @@ -402,6 +402,7 @@ int8_t pop_ctrl_shift_held(void) void pop_ctrl_tick(void) { + kbd_raw_sync(); /* снять залипшее после Rx-overrun SIO */ rest_ctrl_1(); read_input(); read_user_control(); diff --git a/libc/include/kbd_raw.h b/libc/include/kbd_raw.h index ee71f29..89e84a8 100644 --- a/libc/include/kbd_raw.h +++ b/libc/include/kbd_raw.h @@ -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 — то, что реально нужно платформеру. diff --git a/libc/irq/_irq.h b/libc/irq/_irq.h index b034093..91e8c7e 100644 --- a/libc/irq/_irq.h +++ b/libc/irq/_irq.h @@ -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]; diff --git a/libc/irq/_irq_tramp.c b/libc/irq/_irq_tramp.c index 7028d4f..f4207b0 100644 --- a/libc/irq/_irq_tramp.c +++ b/libc/irq/_irq_tramp.c @@ -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 diff --git a/libc/kbd/_kbdraw.h b/libc/kbd/_kbdraw.h index 5567280..7596466 100644 --- a/libc/kbd/_kbdraw.h +++ b/libc/kbd/_kbdraw.h @@ -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 */ diff --git a/libc/kbd/_kbdraw_state.c b/libc/kbd/_kbdraw_state.c index 837d680..8005359 100644 --- a/libc/kbd/_kbdraw_state.c +++ b/libc/kbd/_kbdraw_state.c @@ -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]; diff --git a/libc/kbd/kbd_raw_sync.c b/libc/kbd/kbd_raw_sync.c new file mode 100644 index 0000000..733dd6e --- /dev/null +++ b/libc/kbd/kbd_raw_sync.c @@ -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 +#include "_kbdraw.h" + +void kbd_raw_sync(void) +{ + if (_kbdraw_overrun) { + _kbdraw_overrun = 0; + memset(_kbdraw_down, 0, sizeof(_kbdraw_down)); + } +}