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

86 lines
5.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Бюджет резидента W1/W2 и как его освобождать
Резидент huge-режима — это окно `0x4100..0xBB00` (стек с 0xBB00): код в W1,
данные в W2, между концом данных и стеком остаётся куча. Всё, что туда не
влезло, обязано жить в банках.
## Как СМОТРЕТЬ, а не гадать
Атрибуция по карте (`roomtest.map`) ВРЁТ: file-static SDCC в карту не
попадает, и «дырка» между двумя именованными символами приписывается
предыдущему модулю целиком. На этом легко получить «у pop_bg 1529 байт
данных», когда на самом деле у него 289.
Правильный источник — объектные файлы: в `.rel` есть строки
`A <area> size <n> flags <f>` с ТОЧНЫМ размером каждой области модуля.
```python
# по каждому .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.
## Что осталось на столе
- `malloc`**287 Б** резидента, который никто не зовёт: его тянет
`cbl_open`, где `malloc`/`free` стоят в мёртвой ветке
`CBL_UNDERRUN_SILENCE`. Вынести аллокацию буфера тишины в отдельный
модуль libc — и линкер выбросит malloc у всех приложений на
`CBL_UNDERRUN_APP`.
- `pop_bg` 289 Б — уедут, если вынести `pop_bg_obj_col/row` (2 байта!) в
общий резидентный модуль состояния.
- `pop_tile`**5972 Б кода** в резиденте, самый крупный жилец. Он там
потому, что его листья зовут обе половины слоя фона из РАЗНЫХ банков.
Резать имеет смысл только по этому признаку: что зовут из одного банка —
туда и переносить.
- BANK7 после правки на 87 %: следующий кандидат на разгрузку —
`pop_redraw.c` (981 Б) в другой банк.