Files
Sprinter-SDCC/tests/kbdpoll/kbdpoll.c
T
Александр Петров b56f2b4582 libc/kbd: kbd_raw_poll + замер потери нажатий при зажатом Shift (KBD-1)
Симптом: при удерживаемом Shift часть нажатий стрелок не отрабатывает
(~15 % по наблюдению пользователя), без Shift потерь нет.

Переведено в числа: нажимается Home — тоже расширенная клавиша (тот же
E0-префикс и тот же «fake shift»), но игрой игнорируется, поэтому рельеф
комнаты на результат не влияет.  Счётчики — брейкпоинты MAME с действием
{ b@ADDR = b@ADDR+1 ; g } на входе клавиатурной ветки трамплина, на чтении
порта 0x18 и на установке make-бита.

Что измерено (10 нажатий Shift+Home, дошло make):
  игра идёт, опрос ВКЛ   9/10     игра идёт, опрос ВЫКЛ  9/10
  игра ЗАМОРОЖЕНА (блитов нет вообще, длинных DI нет)  8/10

Обе исходные гипотезы отпали:
- длина наших DI-окон ни при чём (в замороженном кадре потерь больше);
- снятие di в accel-ядрах libbgi УРОНИЛО машину — режим «акселератор при
  EI» из docs/new/06-accel.md §6.6 в этой прошивке недоступен.

Байт теряется НИЖЕ нашего кода: на 49 прочитанных байт пришлось только 28
входов в клавиатурную ветку, то есть ~44 % импульсов запроса прерывания не
обслуживается и трёхбайтовый FIFO SIO переполняется.

Потолок приёма измерен отдельной программой tests/kbdpoll (ничего, кроме
kbd_raw_poll в цикле): 25 нажатий -> 25 make, 250 байт из 250, ноль потерь.
Значит опрос лечит полностью, вопрос только в плотности: нужно раз в
~0.5 мс, а шесть вызовов за 60-мс кадр давали раз в 10 мс.

Поэтому вызовы из roomtest.c УБРАНЫ (они стояли там, где прерывания и так
разрешены, и дублировали трамплин — 9/10 с ними и без).  Сама функция
kbd_raw_poll оставлена в libc: она корректна и нужна как основа плотного
опроса.  В заголовке и в libc-reference — честная оговорка, чтобы её не
ставили в игровой цикл «на всякий случай» без замера.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 15:33:16 +03:00

56 lines
3.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* kbdpoll — измерение ПОТОЛКА «плотного опроса» клавиатуры.
*
* Вопрос, на который отвечает тест (applications/PoP/roomtest/TASKS.md,
* KBD-1): при зажатом Shift часть нажатий расширенных клавиш теряется —
* байт не доезжает до порта 0x18, потому что импульс запроса прерывания
* пропускается примерно в 44 % случаев и трёхбайтовый FIFO SIO
* переполняется. Редкий опрос (несколько раз за кадр) не помог вовсе.
* Здесь опрос идёт МАКСИМАЛЬНО плотно — вся программа только этим и
* занята, — то есть измеряется ЛУЧШЕЕ, чего опрос в принципе может
* достичь. Если и здесь байты теряются, приём не спасёт и в игре, и
* искать надо в эмуляции/железе, а не у нас.
*
* Как мерить (MAME, счётчики брейкпоинтами, машина не останавливается):
* 1. Найти в .map адреса `_kbd_raw_poll` и хвоста карты `__kbdraw_down`.
* 2. bpset <адрес чтения порта 0x18 внутри kbd_raw_poll>,1,{ b@СЧ1 = b@СЧ1+1 ; g }
* bpset <адрес установки make-бита>,E==0x6C && (b@_kbdraw_pending & 1)==0,
* { b@СЧ2 = b@СЧ2+1 ; g }
* 3. Держать Left Shift, нажать Home ровно N раз
* (`:kbd:ms_naturl:P1.7` 0x0002 — Shift, `:kbd:ms_naturl:P2.5` 0x0001 — Home).
* Home выбран потому, что он расширенный (тот же «fake shift», что у
* стрелок), но в этой программе ничего не делает.
* 4. Ожидание: байт ровно 10*N (нажатие E0 F0 12 + E0 6C, отпускание
* E0 F0 6C + E0 12), make'ов ровно N.
*
* Итог теста на экран не выводится СПЕЦИАЛЬНО: любой вывод — это работа,
* которая разрежает опрос и портит замер. Числа снимаются из памяти.
*
* Выход — ESC. Чтобы отличить «программа жива» от «повисла», крутится
* бордюр: цвет меняется раз в 4096 опросов (медленное мерцание = работаем).
*/
#include <kbd_raw.h>
#include <stdint.h>
__sfr __at (0xFE) io_border;
int main(void)
{
uint16_t spin = 0;
if (kbd_raw_open() != 0)
return 1;
/* Ничего, кроме опроса. Ни vsync, ни графики, ни ввода-вывода —
* иначе замеряли бы не потолок приёма, а свою же нагрузку. */
while (!kbd_raw_down(KBD_ESC)) {
kbd_raw_poll();
if (++spin == 0)
io_border ^= 4; /* признак жизни: бит 2 (зелёный) */
}
kbd_raw_close();
return 0;
}