Откат bank-data для pop_trob: указатель на его статику уходил наружу
Симптом: через несколько комнат живого прохода перезагружался DSS. pop_trob_modif() возвращает указатель на room_modif[24][30], а зовут её из банков 2, 3, 7 и резидента. После переноса массив лежит по 0xC000+ в странице банка 6, но разыменовывает указатель ЧУЖОЙ код — когда замаплена его собственная страница. Значит чтение и запись идут поверх кода соседнего банка. Анализ Def/Ref такое не ловит: снаружи нет ссылки на символ, есть ссылка на функцию, которая отдаёт его адрес. Условий для кандидата два, и второе проверяется только чтением кода — ни один указатель на статику не должен уходить наружу. pop_room оба условия проходит (_mobs не читает никто; bake_copy статическая; atlas_load(&pop_env[i]) берёт адрес глобала из резидентного pop_tile.c). Остаётся −1620 Б: данные в W2 6774 -> 5157, свободно 129 -> 1746 Б. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -42,19 +42,45 @@ re.match(r'A\s+(\S+)\s+size\s+([0-9A-Fa-f]+)\s+flags', line)
|
||||
глобал, который читает соседний банк или резидент, обязан остаться
|
||||
замапленным всегда, иначе чтение придёт из чужой страницы.
|
||||
|
||||
Включено для `pop_room.c` и `pop_trob.c` — единственных модулей, у которых
|
||||
НИ ОДИН символ не Ref снаружи. Результат:
|
||||
Включено для `pop_room.c`. Результат:
|
||||
|
||||
| | было | стало |
|
||||
|---|---:|---:|
|
||||
| данные в W2 | 6774 | **4228** |
|
||||
| свободно до стека | 129 | **2675** |
|
||||
| BANK6 | 4199 | 5128 (31 %) |
|
||||
| данные в W2 | 6774 | **5157** |
|
||||
| свободно до стека | 129 | **1746** |
|
||||
| BANK7 | 12684 | 14301 (87 %) |
|
||||
|
||||
**Проверено в MAME**: уровень 1, комнаты 1 и 2 — фон, факелы, решётки,
|
||||
проваливающиеся полы и переход между комнатами работают.
|
||||
|
||||
## ГЛАВНАЯ ЛОВУШКА: указатель на банковую статику
|
||||
|
||||
Первый заход включал ещё и `pop_trob.c` — по Def/Ref он выглядел ИДЕАЛЬНЫМ
|
||||
кандидатом (экспортируемых данных нет вообще). Через несколько комнат
|
||||
живого прохода **перезагрузился DSS**.
|
||||
|
||||
Причина: `pop_trob_modif()` ВОЗВРАЩАЕТ УКАЗАТЕЛЬ на `room_modif[24][30]`, а
|
||||
зовут её из банков 2, 3, 7 и резидента. После переноса массив лежит по
|
||||
0xC000+ в странице банка 6, но разыменовывает указатель чужой код — когда
|
||||
замаплена ЕГО страница. То есть чтение и запись идут по тем же смещениям
|
||||
поверх кода соседнего банка. Отсюда и уход системы.
|
||||
|
||||
**Анализ Def/Ref этого не видит**: снаружи нет ссылки на символ, есть
|
||||
ссылка на функцию, которая отдаёт его адрес. Поэтому условий для
|
||||
кандидата ДВА, и второе проверяется только чтением кода:
|
||||
|
||||
1. ни один чужой модуль не Ref его глобалы (по `.rel`);
|
||||
2. **ни один указатель на его статику не уходит наружу** — ни возвратом, ни
|
||||
через out-параметр, ни записью в общий глобал.
|
||||
|
||||
У `pop_room` второе выполнено: единственные `&`-выражения наружу —
|
||||
`atlas_load(&pop_env[i])`, но `pop_env` определён в `pop_tile.c`, то есть в
|
||||
резиденте; `bake_copy` статическая.
|
||||
|
||||
Внутри своей страницы указатель безопасен всегда: любой вызов резидентной
|
||||
функции, которая перемапливает W3 (`atlas_load`, `pop_page_read`),
|
||||
восстанавливает окно перед возвратом.
|
||||
|
||||
### Два условия, без которых это молча ломается
|
||||
|
||||
1. **Нулевая инициализация.** crt0 зануляет только резидентный `_DATA`, до
|
||||
|
||||
Reference in New Issue
Block a user