4e43890fce
Пройденная игра доходила до таблицы рекордов и умирала: программа
исчезала, машина следом вставала намертво (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>
85 lines
5.9 KiB
C
85 lines
5.9 KiB
C
/*
|
||
* _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
|