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>
This commit is contained in:
@@ -647,3 +647,40 @@ Enter/Shift — либо оставить как есть уже осознан
|
||||
отпущено ВСЁ, что игрок держал в момент смерти (иначе зажатая при падении
|
||||
стрелка перезапускает уровень мгновенно), и не раньше `RESPAWN_SETTLE`
|
||||
кадров — труп должен успеть лечь.
|
||||
|
||||
---
|
||||
|
||||
## Таблица рекордов: Esc отменяет запись, а оригинал уйти не даёт
|
||||
|
||||
**Как в оригинале.** `show_hof` вставляет результат в таблицу ДО ввода
|
||||
имени и крутит ввод, пока тот не вернёт положительную длину:
|
||||
|
||||
```c
|
||||
/* SDLPoP seg001.c:624 */
|
||||
while (input_str(&rect, hof[hof_index].name, 24, "", 0, 4, color, bgcolor) <= 0);
|
||||
/* seg009.c input_str: Esc -> return -1; Enter -> return length */
|
||||
```
|
||||
|
||||
То есть **уйти нельзя ни по Esc, ни с пустым именем**: и −1, и 0 просто
|
||||
начинают ввод заново. Запись в таблице остаётся в любом случае.
|
||||
|
||||
**Как у нас.** Enter записывает имя (пустое подставляется как `PLAYER`),
|
||||
**Esc отказывается от записи целиком**: таблица перечитывается с диска
|
||||
(`hof_load`), `hof_save` не вызывается, экран показывается уже без строки
|
||||
ввода.
|
||||
|
||||
**Зачем.** Решение пользователя, 2026-08-27. Оригинальное поведение
|
||||
означает, что случайно добравшийся до финала игрок обязан вписать имя,
|
||||
чтобы вообще уйти с экрана, — на клавиатуре без цифрового блока и с нашим
|
||||
raw-каналом это выглядит как зависание, а не как требование.
|
||||
|
||||
**Чем платим.** Расхождение видно игроку: в оригинале таблица после
|
||||
победы пополняется всегда. Плюс отмена стоит одного лишнего чтения
|
||||
POP.HOF — откатывать сдвиг строк, который сделал `hof_insert_current`,
|
||||
дешевле перечитыванием файла, чем обратным сдвигом (экран холодный,
|
||||
50 мс там никому не мешают).
|
||||
|
||||
**Что проверять при регрессе.** После Esc: таблица на экране прежняя (без
|
||||
новой строки), POP.HOF на диске не изменился, следующий показ таблицы —
|
||||
между credits и attract-demo — даёт тот же список. После Enter с пустым
|
||||
именем строка называется `PLAYER` и сохраняется.
|
||||
|
||||
Reference in New Issue
Block a user