From 90255737c27a268feb127dc21e8f838b2727ccb5 Mon Sep 17 00:00:00 2001 From: Alexander Petrov Date: Thu, 20 Aug 2026 21:21:44 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D1=82=D0=BA=D0=B0=D1=82=20bank-data=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20pop=5Ftrob:=20=D1=83=D0=BA=D0=B0=D0=B7?= =?UTF-8?q?=D0=B0=D1=82=D0=B5=D0=BB=D1=8C=20=D0=BD=D0=B0=20=D0=B5=D0=B3?= =?UTF-8?q?=D0=BE=20=D1=81=D1=82=D0=B0=D1=82=D0=B8=D0=BA=D1=83=20=D1=83?= =?UTF-8?q?=D1=85=D0=BE=D0=B4=D0=B8=D0=BB=20=D0=BD=D0=B0=D1=80=D1=83=D0=B6?= =?UTF-8?q?=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Симптом: через несколько комнат живого прохода перезагружался 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 --- applications/PoP/docs/resident_budget.md | 36 ++++++++++++++++++++---- applications/PoP/roomtest/Makefile | 23 +++++++++------ 2 files changed, 46 insertions(+), 13 deletions(-) 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) — ВКЛЮЧЕНО по