Files
Sprinter-SDCC/applications/SprPoP/src/pop_menu.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

45 lines
2.6 KiB
C

/*
* pop_menu.h — контракт pause menu (план docs/menu_settings_plan.md §10.3,
* этап MS0).
*
* UI НЕ вызывает gameplay-модули напрямую: меню формирует КОМАНДУ, а
* исполнение остаётся в главном цикле (sprpop.c). Вся работа с меню —
* код, шрифты и таблицы строк — живёт в БАНКЕ 9. Флаг запроса принадлежит
* главному циклу (W2): это единственная резидентная граница UI.
*/
#ifndef POP_MENU_H
#define POP_MENU_H
#include <stdint.h>
typedef enum {
POP_MENU_NONE = 0, /* меню не открывалось / пункт-заглушка */
POP_MENU_RESUME, /* закрыть меню, продолжить игру */
POP_MENU_QUICKSAVE, /* то же, что F6 */
POP_MENU_QUICKLOAD, /* то же, что F9 */
POP_MENU_RESTART_LEVEL, /* штатный reset текущего уровня (без CFG) */
POP_MENU_RESTART_GAME, /* новая игра с уровня 1, настройки и SAV */
POP_MENU_QUIT, /* выход из программы (после подтверждения) */
POP_MENU_QUICKLOAD_BACKUP, /* загрузить подтверждённый POP.BAK */
POP_MENU_SETTINGS /* внутренний экран Settings (не возвращается) */
} pop_menu_cmd_t;
/* Флаг запроса в sprpop.c (W2); банк читает напрямую. */
extern uint8_t pop_menu_request;
/* Загрузить/освободить EMM-атлас исходных шрифтов SDLPoP. Вызывается из
* холодного boot/shutdown, не из кадрового пути. */
int pop_menu_init(void) __banked;
void pop_menu_free(void) __banked;
/* Граница кадра. Запроса нет — мгновенный POP_MENU_NONE. Меню МОДАЛЬНО:
* пока открыто, не возвращается (игра стоит — логика, таймер и звуковой
* насос главного цикла просто не выполняются); по выбору пользователя
* отдаёт pop_menu_cmd_t. */
int8_t pop_menu_process(void) __banked;
/* Экран Sprinter build, если он включён в POP.CFG. Вызывается после
* открытия raw-клавиатуры и до первого игрового кадра. */
#endif