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

SprPoP — Prince of Persia на Sprinter Sp2000

Порт оригинального Prince of Persia (1989) на компьютер Sprinter Sp2000: режим 320×256×256, дабл-буфер, звук через CBL, код разложен по одиннадцати 16-килобайтным банкам.

Приложение автономно — код, оригинальные данные, конверторы ресурсов и сборка лежат внутри этой папки. Наружу нужен только компилятор:

export SPRINTER_ROOT=/путь/к/C-Compiler   # не нужен, если папка лежит внутри его дерева
make                                       # build/sprpop.exe + ресурсы в build/
make hdd                                   # + образ build/hdd/sprpop.chd

Что где

src/            рукописный C: главный цикл sprpop.c + движок pop_*.c
gen/            генерируемые заголовки — руками не править
assets/orig/    оригинальные данные игры (вне репозитория, см. там README)
assets/packed/  то, что ложится на диск игры, в раскладке диска
tools/          конверторы ресурсов; все пути — в tools/paths.py
docs/           планы, доски задач и багов, справочники (начинать с docs/README.md)
tests/host/     модульные тесты движка под ucsim_z80
build/          выход: sprpop.exe, каталоги ресурсов, hdd/, промежуточные atl/

Цели сборки

Команда Что делает
make собрать build/sprpop.exe и разложить ресурсы в build/
make hdd + образ жёсткого диска build/hdd/sprpop.chd
make mame-link однократно подставить образ в MAME (символьная ссылка на IMG/test_hdd.chd)
make resources перегенерировать ресурсы из assets/orig/
make music-mp3 музыка из другого набора записей: music-flac (умолчание), music-mp3, music-ogg, music-mt32
make -C tests/host модульные тесты движка (секунды, без эмулятора)
make clean снести build/ (ассеты не трогает)
make distclean clean + снести упакованные ассеты assets/packed/

Отладочные ключи: make LEVEL=9 — стартовый уровень, make ROOM=15 POS=2 — стартовая комната и тайл (row*10+col), make PROF=1 — профиль фаз кадра полосами бордюра, make ALLOCS=100000 — плотная упаковка регистров для замеров размера.

Сборка из чистого клона

Работает без assets/orig/: упакованные ресурсы (assets/packed/) и генерируемые заголовки (gen/) лежат в репозитории. Исключение — assets/packed/LEVELS/: файлы уровней ложатся на диск прямо из оригинала, без конвертации, поэтому они, как и assets/orig/, вне репозитория. Что и откуда положить — assets/orig/README.md.

assets/orig/ нужен только чтобы ресурсы перегенерировать (make resources) и как эталон механики: любая правка поведения сверяется с assets/orig/SDLPoP/src/ до кодинга, см. CLAUDE.md.

Происхождение

Выделен из applications/PoP/roomtest 2026-08-26; та папка заморожена и служит архивом закрытых задач, багов и исполненных планов. Референсные реализации (Apple II 6502, Princed Resources, mininim) остались в applications/PoP/.