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)); + } +}