Files
Sprinter-SDCC/applications/PoP/docs/resident_budget.md
T
snark13 354582662c Резидент W1/W2: данные pop_room и pop_trob — в свои банки (−2546 Б)
sprinter-cc: новый повторяемый --bank-data=SRC — писучие данные ОДНОГО
банкового модуля в его же страницу.  Прежний --bank-data был всё-или-ничего
и потому неприменим: у большинства банковых модулей часть глобалов читают
соседние банки и резидент (hitp_*, pop_upside, pop_loose_modif, pop_cd), и
такие данные обязаны остаться замапленными всегда.

Кандидаты отобраны по объектным файлам, а не на глаз: символ должен быть
Def только в своём .rel и нигде не Ref.  Прошли ровно двое — pop_room
(_mobs не читает никто) и pop_trob (экспортируемых данных нет вовсе).

При любом --bank-data sprinter-cc сам добавляет mkexe -p 0: crt0 зануляет
только резидентный _DATA, а mkexe по умолчанию бьёт пустоты 0xFF — иначе
вся банковая статика поднялась бы мусором.

Итог: данные в W2 6774 -> 4228, свободно до стека 129 -> 2675 Б.
BANK7 87 %, BANK6 31 %.  Проверено в MAME: уровень 1, комнаты 1-2, фон,
факелы, решётки, проваливающиеся полы, переход между комнатами.

Метод замера и оставшиеся кандидаты — docs/resident_budget.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 21:14:02 +03:00

5.5 KiB
Raw Blame History

Бюджет резидента W1/W2 и как его освобождать

Резидент huge-режима — это окно 0x4100..0xBB00 (стек с 0xBB00): код в W1, данные в W2, между концом данных и стеком остаётся куча. Всё, что туда не влезло, обязано жить в банках.

Как СМОТРЕТЬ, а не гадать

Атрибуция по карте (roomtest.map) ВРЁТ: file-static SDCC в карту не попадает, и «дырка» между двумя именованными символами приписывается предыдущему модулю целиком. На этом легко получить «у pop_bg 1529 байт данных», когда на самом деле у него 289.

Правильный источник — объектные файлы: в .rel есть строки A <area> size <n> flags <f> с ТОЧНЫМ размером каждой области модуля.

# по каждому .rel: _DATA/_BSS/_INITIALIZED = резидентные данные модуля,
#                  _BANKn                  = его код в банке
re.match(r'A\s+(\S+)\s+size\s+([0-9A-Fa-f]+)\s+flags', line)

Кто на кого ссылается — оттуда же: S <sym> Def... — модуль определяет символ, S <sym> Ref... — ссылается на него.

Замер 2026-08-20 (до правки)

Данные банковых модулей, лежавшие в РЕЗИДЕНТЕ:

модуль данные в W2 разделяемые символы
pop_room (банк 7) 1620 нет (_mobs не читает никто)
pop_trob (банк 6) 929 НЕТ ВООБЩЕ
pop_bg (банк 2) 289 pop_bg_obj_col/row читает pop_room
pop_map (банк 3) 227 hitp_*, pop_upside*, pop_loose_modif, … — читают 6 модулей
pop_cdraw (банк 4) 175 pop_cd читают 4 модуля

Что сделано: данные банка — в страницу банка

sprinter-cc --bank-data=SRC (повторяемый) кладёт писучие данные ОДНОГО банкового модуля в его же 16-КБ страницу. Выборочность принципиальна: глобал, который читает соседний банк или резидент, обязан остаться замапленным всегда, иначе чтение придёт из чужой страницы.

Включено для pop_room.c и pop_trob.c — единственных модулей, у которых НИ ОДИН символ не Ref снаружи. Результат:

было стало
данные в W2 6774 4228
свободно до стека 129 2675
BANK6 4199 5128 (31 %)
BANK7 12684 14301 (87 %)

Проверено в MAME: уровень 1, комнаты 1 и 2 — фон, факелы, решётки, проваливающиеся полы и переход между комнатами работают.

Два условия, без которых это молча ломается

  1. Нулевая инициализация. crt0 зануляет только резидентный _DATA, до банковых страниц он не дотягивается, а mkexe по умолчанию забивает пустоты образа 0xFF. По правилам проекта file-scope переменные никто не инициализирует явно (crt0 же зануляет) — значит вся банковая статика поднялась бы с 0xFF. Поэтому sprinter-cc при любом --bank-data САМ добавляет mkexe -p 0.
  2. _INITIALIZED остаётся в резиденте. --dataseg переименовывает только _DATA; инициализированные переменные и их _INITIALIZER живут как раньше, и gsinit копирует их без участия банков. У pop_room таких 3 байта — они и остались в W2.

Что осталось на столе

  • malloc287 Б резидента, который никто не зовёт: его тянет cbl_open, где malloc/free стоят в мёртвой ветке CBL_UNDERRUN_SILENCE. Вынести аллокацию буфера тишины в отдельный модуль libc — и линкер выбросит malloc у всех приложений на CBL_UNDERRUN_APP.
  • pop_bg 289 Б — уедут, если вынести pop_bg_obj_col/row (2 байта!) в общий резидентный модуль состояния.
  • pop_tile5972 Б кода в резиденте, самый крупный жилец. Он там потому, что его листья зовут обе половины слоя фона из РАЗНЫХ банков. Резать имеет смысл только по этому признаку: что зовут из одного банка — туда и переносить.
  • BANK7 после правки на 87 %: следующий кандидат на разгрузку — pop_redraw.c (981 Б) в другой банк.