354582662c
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>
86 lines
5.5 KiB
Markdown
86 lines
5.5 KiB
Markdown
# Бюджет резидента 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 Б) в другой банк.
|