Files
Sprinter-SDCC/libc/kbd/_kbdraw.h
T
snark13 4e43890fce SprPoP: финал больше не убивает программу — прямой вызов в чужой банк
Пройденная игра доходила до таблицы рекордов и умирала: программа
исчезала, машина следом вставала намертво (di;halt на 0x0000) либо уходила
в reset.  Одинаково из Flex Navigator и из голого DSS.

КОРЕНЬ.  pop_ui.h объявлял группу pop_text_*_mapped БЕЗ __banked.  Пока
pop_hof.c лежал в банке 9 рядом с pop_ui.c, прямой call был верен; после
переноса pop_hof/pop_config/pop_pal в банк 10 тот же call стал уходить в
пустой хвост чужого банка.  Процессор полз по 0xFF до 0x0000, где ловушка
DSS ставит B=0x27 и сворачивает процесс — подмена страниц W1/W2/W3,
которую было видно на трупе, оказалась уборкой, а не причиной.

Точную инструкцию (call $E503 = _pop_text_map банка 9) дала трассировка
MAME на узком участке: trace включалась брейкпоинтом на входе в
pop_hof_show и выключалась на процедуре завершения процесса DSS (0x1E56).

ЧТО СДЕЛАНО

* pop_ui.h/.c — группа text_*_mapped помечена __banked.
* toolchain/check_bank_calls.py — две проверки банкового кода:
  1) прямой call в чужой банк (доказательна, ВАЛИТ сборку — проверено
     намеренной поломкой);
  2) указатель на данные своего банка, отданный в чужой (эвристика по
     форме кода, только предупреждает).
  Встроена в app.mk, запускается сразу после линковки.
* pop_hof.c — курсор ввода строится на стеке: литерал "_" лежал в _BANK10
  и после пометки __banked уезжал из-под ног чужому банку, заливая экран
  знаками вопроса.
* libc: kbd_raw_keypad_as_ext() — kbd_raw_sync переносит голые коды
  нумпада в EXT-половину карты.  Лечит залипание стрелок (потерянный
  префикс E0 сажал make в PLAIN как код нумпада, и снять его было нечем),
  заодно нумпад стал управлением: 7/8/9, 4/6, 2 и 5 = вниз.
* pop_pace.c — цикл ожидания луча зовёт тот же idle-хук, что и
  gfx_wait_vsync: без этого F10 в геймплее не работал вовсе.
* pop_hof.c — Esc в таблице рекордов отменяет запись (расхождение с
  оригиналом записано в docs/impl_diff.md).
* Экран версии показывается только через Menu/Settings/About: стартовый
  показ и Ctrl+V убраны, мёртвый код снят.
* sprpop_cold.c — pop_start_level зовёт pop_hp_invalidate: после Ctrl+A с
  выросшим за уровень максимумом полоса HP моргала между страницами.

Разбор всех четырёх багов — в applications/PoP/roomtest/BUGS_CLOSED.md
(FINAL-BANKCALL, FINAL-HOF-GARBAGE, KBD-ARROW-PHANTOM, F10-GAMEPLAY),
правило про банки — в applications/SprPoP/CLAUDE.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 22:11:01 +03:00

85 lines
5.9 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.
/*
* _kbdraw.h — внутренности raw-канала клавиатуры (НЕ публичный
* заголовок; публичный интерфейс — <kbd_raw.h>).
*
* Всё состояние — плоские глобалы (живут в W2/_DATA во ВСЕХ memory-
* режимах, см. sprinter_memory_modes) — трамплин прерывания
* (libc/irq/_irq_tramp.c) обращается к ним БЕЗ W1-remap и без call,
* поэтому раскладка памяти приложения тут ни при чём.
*/
#ifndef _KBDRAW_INTERNAL_H
#define _KBDRAW_INTERNAL_H
#include <stdint.h>
/* Биты _kbdraw_pending — накопленные префиксы PS/2 Scan Code Set 2,
* ждущие завершающего байта кода (см. _irq_tramp.c). */
#define _KBDRAW_PEND_BREAK 0x01
#define _KBDRAW_PEND_EXT 0x02
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) */
/* «Fake shift». Часть PS/2-клавиатур, пока реально зажат Shift, обёртывает
* расширенный код парой служебных посылок: перед make идёт E0 F0 12 (снять
* fake shift), после break — E0 12 (вернуть). Это не клавиши, а прямое
* свидетельство того, что Shift зажат, поэтому декодеры (трамплин и
* kbd_raw_poll) взводят по обёртке plain-бит Shift.
*
* ОБРАТНЫЙ ВЫВОД ЗАПРЕЩЁН (замер 2026-08-05, BUG-GRAB-1). Раньше отсюда
* делался и второй вывод: «расширенный make БЕЗ обёртки => Shift отпущен,
* снять plain-биты». Он неверен: обёртку шлют не все клавиатуры (в MAME
* Sprinter — pc_kbd ms_naturl — при зажатом Shift поток на стрелку вверх
* ровно `E0 75 E0 75 …`, ни одного F0/12), и она заведомо не сопровождает
* typematic-повторы. Результат: КАЖДЫЙ повтор зажатой стрелки снимал
* реально зажатый Shift, и Shift был неработоспособен вместе со стрелками.
*
* Состояние Shift ведётся его собственными make/break (12 / F0 12) — они
* приходят всегда; обёртка лишь подтверждает. Ценой этого потерянный при
* overrun'е break Shift снять нечем — лечится перенажатием Shift
* (см. kbd_raw_sync и BUG-KBD-3/4). */
/* Позиции бит модификаторов в карте (используют и декодеры на асме, и
* kbd_raw_sync): L Shift 0x12 -> байт 2 бит 2, R Shift 0x59 -> байт 11 бит 1. */
#define _KBDRAW_LSH_BYTE 2
#define _KBDRAW_LSH_BIT 2
#define _KBDRAW_RSH_BYTE 11
#define _KBDRAW_RSH_BIT 1
/* Held-состояние: БИТ на скан-код, код>>3 = байт, код&7 = номер бита;
* коды 0..255 обычные, 256..511 — расширенные (префикс 0xE0), т.е.
* смещение +32 байта. Упаковка вместо байта на код: 64 Б вместо 512 —
* заметная доля _DATA на 32-килобайтной раскладке (замер PoP roomtest
* 2026-07-30). Ведёт трамплин прерывания (libc/irq/_irq_tramp.c),
* читает kbd_raw_down(). */
extern uint8_t _kbdraw_down[64];
/* ГОЛЫЕ КОДЫ НУМПАДА = ТЕ ЖЕ КЛАВИШИ, ЧТО И РАСШИРЕННЫЕ
* (взводит kbd_raw_keypad_as_ext).
*
* Стрелки приходят как E0 6B / E0 72 / E0 74 / E0 75, то есть в EXT-половину
* карты. Если префикс E0 потерян (переполнение 3-байтового FIFO SIO в
* длинном DI-окне), make садится в PLAIN-половину как код нумпада, а его
* break приходит уже с префиксом и снимает бит в EXT-половине — PLAIN-бит
* остаётся зажатым НАВСЕГДА (BUG-KBD: 0x72 2026-08-24, 0x6B 2026-08-27).
*
* Лечится перекладкой: kbd_raw_sync() переносит биты голых кодов нумпада
* в EXT-половину и гасит их в PLAIN. Тогда make без префикса ставит ТОТ ЖЕ
* бит, который потом снимет обычный break `E0 F0 xx`, — залипание исчезает,
* а нумпад заодно работает как навигационные клавиши (в PS/2 это и есть
* одни и те же физические клавиши). Позиции бит совпадают: EXT — те же
* байты карты плюс 32.
*
* Диапазон Set 2 — 0x69..0x7D, но ESC (0x76) и F11 (0x78) лежат внутри и
* перекладке НЕ подлежат.
*
* Чего это не лечит: потерю префикса у BREAK (`E0 F0 6B` -> `F0 6B`). Тогда
* бит остаётся стоять уже в EXT-половине, то есть выглядит как реально
* зажатая клавиша; снимается перенажатием или typematic-повтором. */
extern uint8_t _kbdraw_keypad_ext;
#endif