MEM-BANK2 шаг 3: холодная половина слоя фона в банк 7 (90.4% -> 35.8%)

pop_bg.c разрезан по ЧАСТОТЕ вызова, а не по размеру:

  pop_bg.c   (банк 2)  горячее  fore-проход, оверлеи, кладка, клип
  pop_room.c (банк 7)  холодное draw_tile, точечные перерисовки, mob,
                                загрузка атласов

Стык — три тонкие __banked-обёртки (wall_pattern, wall_pattern_reset,
draw_gate_back): тела остаются непомеченными, поэтому горячий fore-проход,
зовущий wall_pattern до девяти раз за кадр, платит ноль, а трамплин
достаётся только холодному пути — ~40 вызовов на вход в комнату, 26 000
тактов = 0.06 кадра РАЗОВО.  Общее состояние (pop_loose_modif, pop_ceil_modif,
obj_row/obj_col) писучее, лежит в _DATA/W2 и видно обеим половинам.

Заодно удалена мёртвая potion_bubble (169 Б).

Грабля: n_banks объявляет само приложение (roomtest.c), а не sprinter-cc.
Восьмой банк без правки константы линкуется молча, _bank_pages[7] остаётся
0xFF, и программа встаёт намертво до первого кадра.  Диагноз снят дампом
_bank_pages из MAME.

Замер (ALLOCS=3000): BANK2 11 942 -> 5 872, BANK7 6 035; _CODE и куча не
тронуты (22 556 / 4 301).

Проверено: tests-host зелёные; построчная сверка pop_bg.c+pop_room.c против
дорефакторного pop_bg.c — ни одной строки логики не пропало; 8 комнат в MAME
до/после совпали попиксельно по активному экрану (различия только в фазе
анимации факелов и кадре Кида); живой прогон с переходами комнат, боем и
воротами сверен контрольным запуском HEAD-бинаря.

DRAW-COST поднят в приоритете: замер пользователя (ур.1 комната 3, страж
убит, Кид стоит) — синяя 80%, зелёная 20%, циан 110%, итого ~210 %
кадрового периода В ПОКОЕ.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-08 16:28:02 +03:00
parent bbf91d10ee
commit 14190f0210
9 changed files with 1116 additions and 929 deletions
+117
View File
@@ -322,6 +322,123 @@ BANK4 pop_cdraw 4 625 5 905 +1 280 (сюда переехала отрис
<a id="clip-1"></a>
### <a id="mem-bank2"></a>MEM-BANK2. Разгрузка банка 2 — **ЗАКРЫТА 2026-08-08**
Банк 2 (`pop_bg.c`) упирался в потолок: 90.8 % ещё до чомперов. Разбор по
символам показал, что он держит ДВЕ разные вещи — горячий fore-проход (каждый
кадр) и холодную отрисовку тайлов (вход в комнату, точечные перерисовки), —
а общие «листья» (`blit_b`, `tile_code`, `tile_table`, `wall_pattern`) нужны
обеим.
**Ключевое ограничение платформы** (записано в memory
`sdcc_banked_call_rules`): писучие данные банка лежат в `_DATA`/W2 и видны
всем, а `const`-таблицы — в СТРАНИЦЕ банка, и из другого банка их не
прочитать вовсе. Плюс трамплин выбирается ОБЪЯВЛЕНИЕМ: пометить лист
`__banked` — значит заставить платить и горячих вызывающих (654 такта на
круг).
**Шаг 1 — общие листья в РЕЗИДЕНТ** (`pop_tile.c` + внутренний `pop_tile.h`).
W1 замаплен всегда, поэтому и горячая половина, и будущая холодная зовут их
прямым `call` и читают таблицы напрямую — без дублей и без трамплинов.
Уехали: `blit_b`, `env_b/wall_b/fore_b/pot_b`, `tile_code/tile_mod/
tile_code_drawn/wall_modifier`, `heal_off`, `get_loose_frame`,
`potion_flask`, `pop_fore_set_clip`, `pop_room_set_below/above` + таблицы
`tile_table`, `COL_XH`, `WALL_FRAM_*`, `SPIKES_FRAM_LEFT`,
`LOOSE_FRAM_LEFT/BOTTOM`. Заодно три функции перестали быть `__banked` —
минус трамплин на кадр.
**Шаг 2 — дедуп внутри банка.** `wall_pattern` (808 → 394) и `wall_rnd`
(786 → 654): четыре ветки по виду стены (SWS/SWW/WWS/WWW) отличались ТОЛЬКО
набором кусков и числами в одной и той же серии `prandom` — сведены к
таблицам `WP_PARTS` и `WR_RULE`. Порядок вызовов `prandom` сохранён
дословно (иначе разъедется раскладка кладки).
**Грабля, пойманная замером:** после выноса `tile_table` из `static const` в
глобальную SDCC перестала сворачивать `tile_table[10].fore_x/fore_y` в
константы и стала читать их в рантайме — `potion_flask` раздулся 115 → 265 Б.
Лечится макросами, которыми инициализируется та же запись таблицы (одно место
правки, дублирования данных нет): 265 → 230.
**Замер (ALLOCS=3000):**
```
было после ш.1 после ш.2
BANK2 14 815 12 460 11 942 (90.4 % -> 72.9 %, свободно 4442 Б)
_CODE 20 064 22 591 22 556 (куча 6793 -> 4301 Б)
```
С релизным `ALLOCS=100000` банк 2 будет ещё примерно на 500-600 Б меньше
(замер до разгрузки: 14 815 -> 14 176).
**Проверено:** `tests-host` зелёные; в MAME комната 1 уровня 1 после дедупа
совпала с дорефакторным снимком **попиксельно (0 различий из 227 520)**,
комната 3 (сплошная кладка, все четыре вида стены и марки) — та же раскладка
кирпича.
**Шаг 3 — ХОЛОДНАЯ половина в банк 7** (`pop_room.c` + внутренний
`_pop_bg.h`). Граница проведена по частоте вызова, а не по размеру:
```
pop_bg.c (банк 2) ГОРЯЧЕЕ fore-проход, оверлеи, кладка, клип каждый кадр
pop_room.c (банк 7) ХОЛОДНОЕ draw_tile, точечные перерисовки, mob, раз на комнату
загрузка атласов / на событие
```
Обратных зависимостей нет вовсе: холодная половина зовёт горячую ровно в
трёх местах (`wall_pattern`, `wall_pattern_reset`, `draw_gate_back`) — и
через ТОНКИЕ `__banked`-обёртки, а не пометкой самих тел. Тела остаются
непомеченными, поэтому fore-проход, который зовёт `wall_pattern` до девяти
раз за кадр, платит ноль. Общее состояние (`pop_loose_modif`,
`pop_ceil_modif`, `obj_row/obj_col`) — писучее, лежит в `_DATA`/W2 и видно
обеим половинам; `obj_row/obj_col` для этого перестали быть `static`.
**Цена трамплинов:** полная отрисовка комнаты — ~40 вызовов через границу
(по два `wall_pattern` на стенной тайл), 26 000 тактов = 0.06 кадра РАЗОВО
на вход в комнату. Анимация пик/ворот — 2-4 вызова на кадр, ~1 %. Ровно
тот порядок, который был признан приемлемым при постановке задачи.
Заодно удалена мёртвая `potion_bubble` (169 Б): её работу давно делают
`pop_potion_flask` (резидент) и `pop_potion_draw`, вызывающих не осталось.
**ГРАБЛЯ, стоившая прогона: `n_banks` объявляет САМО ПРИЛОЖЕНИЕ**
(`roomtest.c`), а не `sprinter-cc`. Восьмой банк без правки этой константы
собирается и линкуется без единого предупреждения, `_bank_pages[7]`
остаётся 0xFF, и трамплин уводит в мусор: exe грузится, но программа встаёт
намертво ещё до первого кадра — на экране остаётся приглашение DSS.
Диагноз снят дампом `_bank_pages` из MAME (`FF EC ED EE EF F0 F1 FF` —
семь страниц вместо восьми), а не гаданием. Предупреждение об этом в
шапке `roomtest.c` было — и всё равно не сработало, потому что число банков
живёт в двух местах.
**Замер (ALLOCS=3000):**
```
было ш.1 ш.2 ш.3
BANK2 14 815 12 460 11 942 5 872 (90.4 % -> 35.8 %)
BANK7 — — — 6 035 (36.8 %)
_CODE 20 064 22 591 22 556 22 556 (куча 4301 Б — не тронута)
```
**Проверено (шаг 3):**
- `tests-host` зелёные;
- построчная сверка `pop_bg.c` + `pop_room.c` против дорефакторного
`pop_bg.c`: НИ ОДНОЙ строки логики не пропало и не изменилось —
расхождения только шапки, включения, три обёртки и удалённый мёртвый код;
- в MAME **8 комнат** (стартовая + 7 по `ROOMNAV`) сняты до и после и сверены
попиксельно по активному экрану 320×256: различия ТОЛЬКО в фазе анимации
(пламя факелов, кадр Кида), статическая кладка совпадает пиксель в пиксель;
- живой прогон: переход между комнатами, бой со стражем, решётка ворот,
окклюзия Кида ближней колонной — как на дорефакторном билде (сверено
контрольным запуском HEAD-бинаря с тем же вводом).
**Что осталось в запасе, если места снова не хватит** (не делаем, пока нет
причины): вынести из РЕЗИДЕНТА холодные части — инициализацию в `main` +
`enter_room_side`/`pop_start_level` (~1.7 КБ), загрузчик атласов Кида
(~1.2 КБ), `pop_room_load`/`pop_level_load` (~1.4 КБ). Итого ~4-4.5 КБ
резидента, то есть куча 4301 -> ~8.6 КБ. Целиком `pop_level.c` в банк
нельзя: его `pop_level_tile` зовётся из банка на КАЖДЫЙ тайл — только
расщепление модуля.
### CLIP-1. Аудит блитов: где клип не нужен — **СДЕЛАНО 2026-08-01**
> **Итог.** Heal Кида и стража переведены на выбор ядра по тому же тесту,