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
+37 -71
View File
@@ -17,13 +17,14 @@
Уровень 3: **скелет сделан и проверен в MAME 2026-08-07** (бой, падение в
пропасть, окклюзия), чомперов ещё нет.
**Разгрузка банка 2 сделана 2026-08-08** ([MEM-BANK2](#mem-bank2)): 90.4 % →
**72.9 %, свободно 4442 Б**чомперам места хватает.
**Разгрузка банка 2 сделана 2026-08-08** ([MEM-BANK2](TASKS_CLOSED.md#mem-bank2)): 90.4 % →
**35.8 %, свободно 10 512 Б**холодная половина уехала в банк 7
(`pop_room.c`), чомперам места с запасом.
**DRAW-CHAR сделана и ПРОВЕРЕНА 2026-08-08** (протокол и замеры —
[`TASKS_CLOSED.md`](TASKS_CLOSED.md#draw-char)): отрисовка теперь одна на
всех `Char`. Банку 2 она дала всего +265 Б — место под чомперов дала уже
[MEM-BANK2](#mem-bank2).
[MEM-BANK2](TASKS_CLOSED.md#mem-bank2).
Правило проекта в силе: механику сверять с `../SDLPoP/src/` ДО кодинга;
диагноз платформы подтверждать артефактом (брейкпоинт/дамп/.asm), а не
@@ -50,10 +51,11 @@
|---|--------|-----|-----------|
| 1 | [GUARD-PHYS](#guard-phys) | физика стража = физика Кида (одна над `Char`) | **ядро сделано 2026-08-07**; открыт живой сценарий в MAME |
| — | [DRAW-CHAR](TASKS_CLOSED.md#draw-char) | отрисовка одна на всех `Char`**закрыта и проверена 2026-08-08** | — |
| — | [MEM-BANK2](#mem-bank2) | разгрузка банка 2 — **сделана 2026-08-08**: 90.4 % → 72.9 %, свободно 4442 Б | — |
| — | [MEM-BANK2](TASKS_CLOSED.md#mem-bank2) | разгрузка банка 2 — **закрыта 2026-08-08**: 90.4 % → 35.8 %, свободно 10 512 Б | — |
| 2 | [L3-CHOMP](#l3-chomp) | **СЛЕДУЮЩАЯ**: чомперы (5 шт) | прохождение ур. 3 |
| — | [L3-SKEL](#l3-skel) | скелет ур. 3 — **сделан 2026-08-07**, ждёт финальной приёмки | — |
| 3 | [L3-PASS](#l3-pass) | приёмка уровня 3 (обход комнат) | закрытие цели |
| — | [DRAW-COST](#draw-cost) | **кадр не укладывается в бюджет: ~210 % кадрового периода в ПОКОЕ** (замер 2026-08-08) | плавность на ВСЕХ уровнях |
| — | [L1-SPEED](#l1-speed) | игра на ~39 % быстрее оригинала | ощущение от ВСЕХ уровней; берётся в любой момент |
| — | [TUNE-1](#tune-1) | параметры движка → cfg-файл (сейчас `pop_tune.h`) | отладка таймингов и моды; берётся по мере надобности |
@@ -184,66 +186,6 @@
скриншотах пользователя). Обратная проверка: если в соседней комнате СВОЙ
живой страж — переход не происходит.
### <a id="mem-bank2"></a>MEM-BANK2. Разгрузка банка 2 — **шаги 1-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 (сплошная кладка, все четыре вида стены и марки) — та же раскладка
кирпича.
**Что осталось в запасе, если места снова не хватит** (не делаем, пока нет
причины): вынести ХОЛОДНУЮ половину (`draw_tile` 1800 + точечные
перерисовки 1438 + падающие плиты 1898 ≈ 5.1 КБ) в свободный банк 7. Теперь
это дёшево: листья уже в резиденте, а `wall_pattern`/`wall_rnd`/марки/
`draw_gate_back` нужны обеим половинам — им понадобится тонкая `__banked`-
обёртка (тело остаётся непомеченным, горячие вызывающие платят ноль).
### <a id="l3-chomp"></a>L3-CHOMP. Чомперы — механика уровня 3
**Где:** 5 штук, комнаты 5(тайл 4), 16(23,24,25), 22(26). Дальше они почти
@@ -254,10 +196,12 @@ _CODE 20 064 22 591 22 556 (куча 6793 -> 4301 Б)
коллизия и смерть Кида в сомкнутых челюстях (`seg004`/`seg006`); отрисовка
кадра по модификатору (`draw_tile_anim`, ветка `tiles_18_chomper`).
**Место в банке 2 расчищено** ([MEM-BANK2](#mem-bank2), 2026-08-08): 72.9 %,
**свободно 4442 Б** (было 1074 Б). Считать место ДО кодинга всё равно
обязательно (`levels_plan.md` §5.1), и если не хватит — в запасе вынос
холодной половины `pop_bg` в банк 7, там расписано как.
**Место в банке 2 расчищено** ([MEM-BANK2](TASKS_CLOSED.md#mem-bank2), 2026-08-08):
35.8 %, **свободно 10 512 Б** (было 1074 Б) — холодная половина слоя фона
уехала в банк 7 (`pop_room.c`). Считать место ДО кодинга всё равно
обязательно (`levels_plan.md` §5.1); чомпер — это анимация тайла (холодная
перерисовка), поэтому его отрисовка ложится в `pop_room.c`, а состояние —
в `room_modif`, как у пик и ворот.
**Ассеты УЖЕ упакованы (2026-08-07):** `pop_pack_bg.py` кладёт в атлас весь
набор кадров чомпера явно (`CHOMPER_BOT_IDS` 101-105, `CHOMPER_TOP_IDS`
@@ -372,10 +316,32 @@ offset 22790, **240 байт** = 5 палитр × 16 цветов × 3 байт
таблицу нельзя передавать прямо из банка (0xC000+), надо копировать в
W1/W2 (см. [BUG-GUARD-COLOR-1](bug_closed.md#bug-guard-color-1)).
### <a id="draw-cost"></a>DRAW-COST. Стоимость fore-прохода после DRAW-CHAR
### <a id="draw-cost"></a>DRAW-COST. Кадр НЕ УКЛАДЫВАЕТСЯ в бюджет — нужна оптимизация
**Наблюдение пользователя на приёмке 2026-08-08:** циан-полоса профиля
(спрайты + fore) выросла — «в пределах, но оптимизация отдельной задачей».
> **ЗАМЕР ПОЛЬЗОВАТЕЛЯ 2026-08-08 (после MEM-BANK2).** Уровень 1, комната 3,
> страж УБИТ, Кид СТОИТ (то есть это НЕ худший случай — ни боя, ни движения):
>
> | полоса | фаза | доля кадрового периода |
> |---|---|---|
> | синяя (`PROF(2)`) | ввод + heal + логика | ~80 % |
> | зелёная (`PROF(4)`) | слой фона (loose/ловушки) | ~20 % |
> | циан (`PROF(6)`) | спрайты + fore-проход | **~110 %** |
>
> **Итого ~210 % — больше ДВУХ кадровых периодов на кадр.** Это много; в
> покое должно оставаться место под бой, движение и вторую фигуру.
> Приоритет задачи поднят: это уже не косметика профиля.
Мерить в ЭТОЙ точке (комната 3, страж убит, Кид стоит) — она воспроизводима
и даёт нижнюю границу; худший случай (бой + бег + падающая плита) считать
отдельно. Метод — memory `z80_profiling_method` (брейкпоинты с
`totalcycles`), полосы бордюра дают только раскладку по фазам.
Обратить внимание: **синие 80 % при стоящем Киде** — это подозрительно
много для «ввод + heal + логика», heal там наверняка чистит больше, чем
нужно. Разобрать синюю фазу отдельно от циана.
**Наблюдение пользователя на приёмке DRAW-CHAR 2026-08-08:** циан-полоса
профиля (спрайты + fore) выросла — тогда «в пределах».
**Отчего именно.** Раньше перебор тайлов у Кида шёл строго по футпринту
кадра (`char_x_left/right`, seg006:1021), а он УЖЕ спрайта; расширение