Files
Sprinter-SDCC/libc/include/kbd_raw.h
T
snark13 47a4b084c4 Меню, статус-строка и оболочка игры: title/intro/cutscene/HoF, POP.CFG, палитры
Эта сессия (меню + текст в служебных полосах):

* Меню: рамка выделения считается от силуэта текста (SEL_PAD сверху и
  снизу), а не «на глаз»; все экраны центрируются в игровых 200 строках
  (UI_CENTER_TOP/UI_CENTER_FIELD) и в них помещаются; Enter и Space —
  равноправные клавиши действия (ui_action_down).
* Settings: убраны SHOW SPRINTER SCREEN и BACK, добавлены DEBUG BAR и
  ABOUT.  About показывает тот же текст, что стартовый Sprinter screen,
  минус строка про клавиши — общий about_text(), чтобы экраны не
  разъехались.  CONTROLS собирается таблицей и центрируется по
  фактическому числу строк.
* GAME PAUSED переехала в нижнюю статус-строку, как в оригинале
  (SDLPoP rect_bottom_text = {193,70,202,250}): это состояние программы,
  а не пункт меню.  POP_HP_Y вынесен в pop_cdraw.h — полосу делят два
  модуля.
* pop_status.c (банк 9) — текст в обеих служебных полосах.  Нижняя:
  порт display_text_bottom + таймера (QUICKSAVE/QUICKLOAD/SOUND ON/OFF,
  24 тика).  Верхняя отладочная переведена с палочек на текст
  «Level ##, Room ##, Speed: …, Sound: …, Immortal #» малым шрифтом, с
  своим форматированием чисел (без printf и без деления).
  Заявка сообщения — запись одного байта pop_status_msg: резидент W1/W2
  не растёт, весь рендер в банке.  Бюджет после правок не изменился
  (_CODE 23981, куча 267 Б).
* Цена вывода: блит глифа ~4,6 тыс. тактов независимо от размера, поэтому
  всё change-driven, отладочная строка перерисовывается ПО ПОЛЯМ, пробелы
  не блитятся вовсе, а вход в комнату заливает только игровое поле
  (pop_screen_fill_field) — борта от комнаты к комнате не меняются.
* Интро: в PV-сцене зазвучали пропавшие эффекты оригинала — закрытие
  ворот (4) и открытие двери покоев (51), из которой входит Джафар.
* docs/status_line_text.md — полная инвентаризация ВСЕХ текстов SDLPoP в
  статус-строке: геометрия, семантика text_time_total как идентификатора
  сообщения, мигание, рестарт по истечении 36/288.

Вместе с этим выкладывается накопленная работа по оболочке полной игры:
автомат состояний (pop_app), title, intro/PV и cutscene, attract-demo,
Hall of Fame, глобальный таймер, настройки и POP.CFG, модуль палитр и
fade, звуковой набор, host-тесты на новые швы.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 13:58:36 +03:00

112 lines
7.6 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.
/*
* kbd_raw.h — эксклюзивный raw-канал клавиатуры: держит битовую карту
* «клавиша N зажата ПРЯМО СЕЙЧАС», обновляемую напрямую из
* прерывания (декодирует PS/2 Scan Code Set 2 — make/break, тот же
* протокол, что и в docs/samples/sprinterKeybLib.asm).
*
* Прецедент и мотивация — applications/PoP/docs/PORT_PLAN.md §2:
* ESTEX kbhit/getch/getkey/CTRLKEY — событийные (нажатие, без
* release), обычные клавиши (не модификаторы) вообще не имеют
* live-state в ESTEX; трамплин кадрового прерывания
* (libc/irq/_irq_tramp.c) в штатном режиме отдаёт клавиатурные байты
* ПРЯМО в DSS, не читая порт данных — байт из аппаратного приёмного
* регистра можно прочитать только ОДИН раз, поэтому «подсмотреть, не
* мешая DSS» невозможно технически.
*
* ЖИЗНЕННЫЙ ЦИКЛ И ГЛАВНОЕ СЛЕДСТВИЕ: пока kbd_raw_open() активен, ВЕСЬ
* поток клавиатурных байт достаётся ЭТОМУ модулю — DSS их не видит.
* kbhit/getch/getkey/CTRLKEY (в т.ч. kbd_mod_state) НЕ получают новых
* событий, пока raw-канал открыт. Приложение обязано проверять «выход»
* (например ESC — KBD_ESC) через kbd_raw_down() само. Перед экранами,
* которым нужен обычный ESTEX-ввод (диалоги/меню на консольном I/O),
* закрыть канал kbd_raw_close(). Паттерн один в один как у
* cbl_open()/cbl_close() (<cbl.h>) — тот же приватный IM2-хук, не
* чейнящийся к DSS.
*
* КОДЫ КЛАВИШ: значения ниже — общеизвестный стандарт AT/PS-2 Scan
* Code Set 2 (не специфика Sprinter). TODO до использования в PoC:
* подтвердить в MAME, что реальный поток байт с SIO-A клавиатуры
* Sprinter именно этот набор кодов (см. defer_unexplained_quirks).
*/
#ifndef KBD_RAW_H
#define KBD_RAW_H
#include <stdint.h>
/* Включить raw-канал: 0 / -1+errno (EBUSY — уже открыт; прочие коды —
* см. errno.h, как у irq_install/cbl_open — общий IM2-механизм). */
int kbd_raw_open(void);
/* Выключить raw-канал, вернуть клавиатуру DSS. Идемпотентно; висит
* на atexit (страховка, как у cbl_close). */
void kbd_raw_close(void);
/* Зажата ли клавиша code ПРЯМО СЕЙЧАС (0/1). code вне 0..511 — 0
* (защитно). Для расширенных клавиш (стрелки и т.п., префикс 0xE0
* на проводе) прибавить KBD_EXT к базовому коду. */
uint8_t kbd_raw_down(uint16_t code);
/* Есть ли хотя бы одна зажатая клавиша (0/1). Полезно для экранов с
* семантикой «продолжить любой клавишей»; один вызов читает 64-байтную
* bitmap, а не перебирает все 512 scan-кодов через kbd_raw_down(). */
uint8_t kbd_raw_any_down(void);
/* Восстановление после Rx-overrun SIO: звать РАЗ В КАДР (до чтения
* kbd_raw_down). Аппаратный FIFO SIO 3 байта; при длинных DI-окнах пачка
* скан-кодов (напр. быстрый тап стрелки: make+break = 5 байт) переполняет
* его → потерян break → залипшая клавиша. Трамплин ловит overrun и
* взводит флаг; kbd_raw_sync по флагу сбрасывает held-состояние всех
* клавиш КРОМЕ модификаторов (Shift/Ctrl/Alt): PS/2 автоповторяет только
* последнюю нажатую клавишу, поэтому обычные зажатые перечитаются
* typematic'ом, а сброшенный модификатор восстановить нечем — он бы
* «отваливался» при каждом overrun (см. kbd_raw_sync.c). */
void kbd_raw_sync(void);
/* Вычерпать приёмный FIFO ОПРОСОМ, не дожидаясь прерывания. Возвращает
* 0 (FIFO был пуст, ~40 тактов) или 1 (что-то вычерпано и декодировано в
* ту же карту, что ведёт трамплин).
*
* Зачем: на каждый принятый байт запрос прерывания живёт единицы
* микросекунд, и пропущенный импульс не «догоняется» — байт лежит в
* 3-байтовом FIFO до следующего прерывания. Пачки (тап стрелки — 5 байт;
* одновременное отпускание нескольких клавиш — больше) при этом
* переполняют FIFO: теряется make («нажатие не сработало») или break
* (залипание).
*
* КАК ЕЁ ЗВАТЬ (замеры 2026-08-01, PoP roomtest — см.
* applications/PoP/roomtest/TASKS_CLOSED.md, KBD-1):
* - «несколько вызовов за кадр, после тяжёлых фаз» НЕ ДАЁТ НИЧЕГО —
* потери те же, что без них: такие вызовы попадают в участки, где
* прерывания и так разрешены, и лишь дублируют трамплин;
* - работает только ПЛОТНЫЙ опрос, порядка раза в 0.5 мс. Столько
* времени есть даром в ожидании кадра, поэтому штатный способ —
* повесить эту функцию idle-хуком графики:
* gfx_set_idle_hook(my_poll_wrapper); // <gfx.h>
* Проверено: 35 нажатий стрелки с зажатым Shift → 35 дошедших make
* против 9 из 10 без хука.
* Не ставить в игровой цикл «на всякий случай» — сначала померить.
*
* Тело идёт под DI и БЕЗУСЛОВНО делает EI на выходе: рассчитано на вызов
* из главного цикла, из ISR звать нельзя. */
uint8_t kbd_raw_poll(void);
#define KBD_EXT 0x0100 /* база кода была расширенной (0xE0-префикс) */
/* Позиционные коды PS/2 Set 2 — то, что реально нужно платформеру.
* TODO: подтвердить в MAME перед PoC (см. предупреждение выше). */
#define KBD_UP (KBD_EXT | 0x75)
#define KBD_DOWN (KBD_EXT | 0x72)
#define KBD_LEFT (KBD_EXT | 0x6B)
#define KBD_RIGHT (KBD_EXT | 0x74)
#define KBD_SPACE 0x29
#define KBD_ENTER 0x5A
#define KBD_ESC 0x76
#define KBD_LSHIFT 0x12
#define KBD_RSHIFT 0x59
#define KBD_LCTRL 0x14
#define KBD_LALT 0x11
#define KBD_RCTRL (KBD_EXT | 0x14)
#define KBD_RALT (KBD_EXT | 0x11)
#endif