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:
@@ -155,6 +155,32 @@ Makefile склеивает архивы прямо в `assets/packed/<КАТА
|
||||
| `pop_ctrl.c/.h` | Управление: диспетчер `control()` (`seg005`) + ввод `read_user_control` (`seg006`) поверх `<kbd_raw.h>`. |
|
||||
| `pop_map.c/.h` | Коллизия с картой + физика падения/приземления/стены + loose-полы (`seg005/006/007`). |
|
||||
|
||||
## Банки: две мины, которых компилятор не видит
|
||||
|
||||
Обе стреляют молча, обе поймали нас 2026-08-27 (разбор — в
|
||||
`../PoP/roomtest/BUGS_CLOSED.md`, поиск «FINAL-BANKCALL»).
|
||||
|
||||
**1. Прямой вызов в чужой банк.** Трамплин SDCC выбирает по `__banked`
|
||||
в ОБЪЯВЛЕНИИ, а не по тому, где функция лежит. Функция без пометки
|
||||
зовётся прямым `call` — верно ровно пока вызывающий и вызываемый в одном
|
||||
банке. Перенесли модуль ради разгрузки банка — и тот же `call` уходит в
|
||||
пустой хвост чужого банка, процессор ползёт по 0xFF до 0x0000, DSS убивает
|
||||
процесс. Ловит `toolchain/check_bank_calls.py`, она встроена в сборку и
|
||||
ВАЛИТ её (проверено намеренной поломкой).
|
||||
|
||||
**2. Указатель на данные своего банка, отданный в чужой.** Обратная
|
||||
сторона: пока обе стороны в одном банке, указатель на литерал или
|
||||
const-таблицу работает; пометили функцию `__banked` — и трамплин на время
|
||||
вызова переключает W3, а указатель показывает уже в чужой банк. Так экран
|
||||
таблицы рекордов залило знаками вопроса: в `pop_text_draw_mapped` (банк 9)
|
||||
уходил литерал `"_"` из банка 10.
|
||||
|
||||
**Правило:** всё, что уходит указателем в другой банк, обязано лежать в
|
||||
W2 — стек, `_DATA` или копия. `const`-таблицы и строковые литералы
|
||||
банкового модуля наружу отдавать нельзя (то же, что уже записано про
|
||||
`pop_tile.c`). Вторая проверка скрипта предупреждает о явных случаях, но
|
||||
она эвристическая и гарантией не является.
|
||||
|
||||
## Порядок слоёв в кадре (важно для окклюзии)
|
||||
|
||||
`sprpop.c` каждый тик рисует в СКРЫТУЮ страницу: `pop_char_heal` (стереть
|
||||
|
||||
Reference in New Issue
Block a user