diff --git a/applications/PoP/docs/resident_budget.md b/applications/PoP/docs/resident_budget.md index 8ac8fcc..3422b92 100644 --- a/applications/PoP/docs/resident_budget.md +++ b/applications/PoP/docs/resident_budget.md @@ -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`, до diff --git a/applications/PoP/roomtest/Makefile b/applications/PoP/roomtest/Makefile index ef785da..6d74a0e 100644 --- a/applications/PoP/roomtest/Makefile +++ b/applications/PoP/roomtest/Makefile @@ -35,14 +35,21 @@ MEMORY ?= huge # small-раскладка + банки кода в W # ВАЖНО: любое сравнение занятости банков имеет смысл только при ОДНОМ и том # же ALLOCS — иначе сравниваются не правки, а уровни оптимизации. ALLOCS ?= 3000 -# Писучие данные ЭТИХ банковых модулей живут в их же странице, а не в -# резиденте W1/W2 — это 2549 Б, которые иначе съедала бы база. Список -# короткий не по лени: у остальных банковых модулей часть глобалов читают -# соседние банки и резидент (hitp_*, pop_upside, pop_loose_modif, pop_cd), -# и такие данные ОБЯЗАНЫ остаться замапленными всегда. Проверять новичка в -# списке — по объектным файлам: символ должен быть Def только в своём .rel и -# нигде не Ref (см. ../docs/resident_budget.md). -BANK_DATA := --bank-data=pop_room.c --bank-data=pop_trob.c +# Писучие данные ЭТОГО банкового модуля живут в его же странице, а не в +# резиденте W1/W2 (−1620 Б базы). Список из одного файла — не лень, а +# результат проверки; ДВА условия, и второе стоило перезагрузки DSS: +# +# 1) ни один чужой модуль не ссылается на его глобалы (проверяется по +# .rel: символ Def только в своём объекте и нигде не Ref); +# 2) НИ ОДИН УКАЗАТЕЛЬ на его статику не уходит наружу. Def/Ref этого не +# видит. Так провалился pop_trob: pop_trob_modif() возвращает указатель +# на room_modif[24][30], и его разыменовывают банки 2/3/7 и резидент — +# то есть пишут по 0xC000+ в СВОЮ страницу, поверх чужого кода. +# +# У pop_room оба условия выполнены: _mobs не читает никто, bake_copy +# статическая, а atlas_load(&pop_env[i]) берёт адрес глобала из pop_tile.c +# (резидент), не своего. Подробности — ../docs/resident_budget.md. +BANK_DATA := --bank-data=pop_room.c EXTRA_FLAGS ?= --gfx 256 -I $(CURDIR)/../poc/res/bg --max-allocs $(ALLOCS) --bank 2=pop_bg.c --bank 7=pop_room.c --bank 4=pop_cdraw.c --bank 4=pop_kdraw.c --bank 3=pop_map.c --bank 1=guards.c --bank 5=pop_ctrl.c --bank 6=pop_trob.c --bank 7=pop_redraw.c --bank 8=roomtest_cold.c --bank 8=pop_level_cold.c --bank 8=pop_kboot.c --bank 8=pop_guard_cold.c --bank 8=pop_shadow.c --bank 8=pop_sfx_cold.c $(BANK_DATA) $(PROF_FLAGS) # Профилирование полосами бордюра (см. PROF() в roomtest.c) — ВКЛЮЧЕНО по