90255737c2
Симптом: через несколько комнат живого прохода перезагружался 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>
190 lines
14 KiB
Makefile
190 lines
14 KiB
Makefile
# roomtest — проверка порта статического фона PoP (Шаг 3): реальная
|
||
# композиция слоёв (pop_bg.c) для комнаты 1. --memory huge --gfx 256.
|
||
|
||
PROJ_ROOT := $(abspath $(CURDIR)/../../..)
|
||
EXAMPLE := roomtest
|
||
# small: W1+W2 как единое 32КБ-пространство, DATA сразу за CODE (иначе
|
||
# CODE ~23КБ переполняет W1 и лезет в фиксированный DATA@0x8000 в huge).
|
||
MEMORY ?= huge # small-раскладка + банки кода в W3 (стражи)
|
||
# Резидента --w3 БОЛЬШЕ НЕТ: отрисовка уехала в БАНК 2 (pop_bg + pop_cdraw
|
||
# в одной странице — вызовы между ними прямые, если функция не помечена
|
||
# __banked). Так снято главное ограничение резидента: из банка его было
|
||
# не достать ни прямо, ни транзитивно, а банк→банк работает (трамплин
|
||
# сохраняет страницу окна на СТЕКЕ, проверено вложенной цепочкой
|
||
# банк1→банк2→банк1 в MAME 2026-07-30). Теперь в банки можно уводить и
|
||
# логику, которая рисует. См. docs/layout_plan_v2.md §2, memory
|
||
# sdcc_banked_call_rules.
|
||
# Банк 5 = pop_ctrl.c (MEM-BANK5). CODE и DATA делят одно 32-КБ
|
||
# пространство W1+W2, поэтому килобайт кода, уехавший в банк, — это
|
||
# килобайт, доступный данным. Кандидат выбран не по размеру, а по частоте
|
||
# вызова: диспетчер управления дёргается один раз за кадр на персонажа и
|
||
# горячих банк→банк переходов не создаёт (в отличие от pop_level, чей
|
||
# pop_level_tile зовётся из банка 2 на КАЖДЫЙ тайл). Шина control_*
|
||
# переехала в pop_state.c — см. там.
|
||
# --max-allocs-per-node: во сколько вариантов размещения регистров SDCC
|
||
# упирается на узел. У sprinter-cc дефолт 100000 (агрессивно, как fast-сборки
|
||
# библиотек) — на roomtest это МИНУТЫ на банк, потому что модули крупные.
|
||
# Дефолт SDCC — 3000, и для разработки его достаточно: разница в размере
|
||
# единицы процента, а сборка кратно быстрее. Замер (2026-08-05) — в шапке
|
||
# TASKS_CLOSED.md, запись BUILD-FAST.
|
||
#
|
||
# make — быстрая сборка (ALLOCS=3000)
|
||
# make ALLOCS=100000 — как раньше: минимальный код, для замеров размера
|
||
# и для «релизного» образа
|
||
#
|
||
# ВАЖНО: любое сравнение занятости банков имеет смысл только при ОДНОМ и том
|
||
# же ALLOCS — иначе сравниваются не правки, а уровни оптимизации.
|
||
ALLOCS ?= 3000
|
||
# Писучие данные ЭТОГО банкового модуля живут в его же странице, а не в
|
||
# резиденте 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) — ВКЛЮЧЕНО по
|
||
# умолчанию, пока идёт работа с производительностью; make PROF=0 выключает.
|
||
# Каждая фаза кадра красит бордюр в свой цвет, высота полосы на скриншоте
|
||
# MAME = время фазы в растровых строках.
|
||
PROF ?= 1
|
||
PROF_FLAGS := -DPROF_BORDER=$(PROF)
|
||
# Стартовый уровень (отладка): make LEVEL=9 — начать сразу с девятого.
|
||
# Дефолт держим на отлаживаемом сейчас уровне; для «настоящей» игры — LEVEL=1.
|
||
# Константа объявлена в pop_tune.h (её видят обе половины главного цикла).
|
||
LEVEL ?= 11
|
||
PROF_FLAGS += -DFIRST_LEVEL=$(LEVEL)
|
||
# Стартовая КОМНАТА и позиция Кида в ней (отладка): make ROOM=15 POS=2 —
|
||
# начать прямо в целевой комнате оптимизации, минуя проход уровня. POS —
|
||
# тайл 0..29, то есть row*10+col. Дефолт — сцена 11/15 (Кид в 0,2), по
|
||
# которой сняты замеры в docs/perf_l11_room15.md. make ROOM= (пусто)
|
||
# возвращает штатный старт из данных уровня.
|
||
ROOM ?= 15
|
||
POS ?= 2
|
||
ifneq ($(strip $(ROOM)),)
|
||
PROF_FLAGS += -DDBG_START_ROOM=$(ROOM) -DDBG_START_POS=$(POS)
|
||
endif
|
||
EXTRA_SRCS := pop_vflip.c pop_pace.c pop_sfx.c pop_state.c pop_draw.c pop_tile.c pop_kid.c pop_level.c pop_geom.c pop_guard.c
|
||
|
||
BG_DIR := $(CURDIR)/../poc/res/bg
|
||
KID_DIR := $(CURDIR)/../poc/res/kid
|
||
LVL_DIR := $(CURDIR)/../SDLPoP/data/LEVELS
|
||
# ВСЕ уровни игры (res2001..res2015, по 2305 Б = 34 КБ на всех): переход
|
||
# уровень→уровень и чит Shift+L требуют, чтобы файл следующего лежал на
|
||
# диске. res2000 — демо-уровень, в скоуп не входит.
|
||
LVL_NUMS := 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15
|
||
LVL_DATA := $(foreach n,$(LVL_NUMS),$(LVL_DIR)/res20$(n).bin)
|
||
# ДВА тайлсета: pop_* — подземелье (уровни 1-3, 7-9, 12, 13, 15),
|
||
# pal_* — дворец (4-6, 10, 11, 14). Раскладка id у них общая, различаются
|
||
# файлы и палитра тайлсета (*tile.pal, 32 записи). chtab_1 (pop_pot.atl:
|
||
# пламя, меч-предмет, зелья) от набора не зависит — один на оба.
|
||
BG_SETS := pop pal
|
||
BG_PAGES := 0 1 2 3 4 5 6 7 8 9
|
||
BG_DATA := $(foreach s,$(BG_SETS),\
|
||
$(foreach n,$(BG_PAGES),$(BG_DIR)/$(s)_env$(n).atl) \
|
||
$(BG_DIR)/$(s)_wall.atl $(BG_DIR)/$(s)_fore.atl \
|
||
$(BG_DIR)/$(s)_tile.pal) \
|
||
$(BG_DIR)/pop_pot.atl
|
||
# ВСЕ 28 страниц Kid — иначе кадры торможения/разворота/приседа (image>23)
|
||
# не рисуются (отрисовка пропускает незагруженную страницу). Палитра —
|
||
# ЕДИНАЯ игровая (env+wall+kid).
|
||
KID_PAGES := $(foreach n,0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27,$(KID_DIR)/kid$(n).atl)
|
||
# Зеркальный ПО ВЕРТИКАЛИ набор (зелье инверсии, уровень 9): те же спрайты с
|
||
# развёрнутыми колонками. Готовит упаковщик, а не рантайм — разворот
|
||
# страницы стоит 0.59 с против 0.045 с на её чтение с диска (замер MAME
|
||
# 2026-08-12, протокол в pop_vflip.c).
|
||
KID_VPAGES := $(foreach n,0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27,$(KID_DIR)/kid$(n)_v.atl)
|
||
KID_DATA := $(KID_PAGES) $(KID_VPAGES) $(KID_DIR)/kid.pal \
|
||
$(KID_DIR)/sword.atl $(KID_DIR)/sword_v.atl
|
||
# Таблицы анимации (кадры+seqtbl) — отдельный бинарь в EMM-странице, его
|
||
# делает pop_extract_kid_data.py (не упаковщик спрайтов) — своё правило ниже.
|
||
KID_BIN := $(KID_DIR)/kid_data.bin
|
||
GUARD_DIR := $(CURDIR)/../poc/res/guard
|
||
GUARD_DATA := $(GUARD_DIR)/g0.atl $(GUARD_DIR)/g1.atl $(GUARD_DIR)/g2.atl \
|
||
$(GUARD_DIR)/g3.atl $(GUARD_DIR)/g4.atl \
|
||
$(GUARD_DIR)/g0_v.atl $(GUARD_DIR)/g1_v.atl $(GUARD_DIR)/g2_v.atl \
|
||
$(GUARD_DIR)/g3_v.atl $(GUARD_DIR)/g4_v.atl
|
||
# Скелет — ОТДЕЛЬНЫЙ набор спрайтов (SKEL.DAT в оригинале): его грузит
|
||
# уровень 3 (tbl_guard_type == 2), кадров меньше (28 против 34).
|
||
SKEL_DIR := $(CURDIR)/../poc/res/skel
|
||
# Зеркального набора у скелета НЕТ: зелье инверсии — уровень 9, там обычный
|
||
# страж (tbl_guard_type[9] = 0). См. pop_pack_guard.py.
|
||
SKEL_DATA := $(SKEL_DIR)/g0.atl $(SKEL_DIR)/g1.atl $(SKEL_DIR)/g2.atl \
|
||
$(SKEL_DIR)/g3.atl
|
||
# Джафар — тоже отдельный набор (VIZIER.DAT): его грузит уровень 13
|
||
# (tbl_guard_type == 3). Кадров столько же, сколько у стража (34), но
|
||
# палитра своя, из файла набора: curr_guard_color для не-стражей = 0.
|
||
# Зеркального набора нет по той же причине, что у скелета.
|
||
VIZIER_DIR := $(CURDIR)/../poc/res/vizier
|
||
VIZIER_DATA := $(VIZIER_DIR)/g0.atl $(VIZIER_DIR)/g1.atl $(VIZIER_DIR)/g2.atl \
|
||
$(VIZIER_DIR)/g3.atl $(VIZIER_DIR)/g4.atl
|
||
|
||
# ТЕНЬ — свой набор, запечённый упаковщиком (наложение спрайта на себя со
|
||
# сдвигом; наш блит так не умеет, см. ../docs/shadow_atlas_plan.md). Две
|
||
# половины в одном наборе: sk* — кадры вне боя (спрайты Кида), sf* — кадры
|
||
# 150..189 (SHADOW.DAT, это тоже графика Кида, а не стража).
|
||
SHADOW_DIR := $(CURDIR)/../poc/res/shadow
|
||
SHADOW_DATA := $(foreach n,0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27,$(SHADOW_DIR)/sk$(n).atl) \
|
||
$(foreach n,0 1 2 3,$(SHADOW_DIR)/sf$(n).atl)
|
||
|
||
# ЗВУКОВЫЕ ЭФФЕКТЫ — оригинальная оцифровка, приведённая к частоте CBL
|
||
# (10 937,5 Гц, 8 бит моно). 8 страниц по 16 КБ; см. ../docs/sound_plan.md.
|
||
SND_DIR := $(CURDIR)/../poc/res/sound
|
||
SND_DATA := $(foreach n,0 1 2 3 4 5 6 7,$(SND_DIR)/s$(n).bin)
|
||
|
||
EXTRA_DATA := $(BG_DATA) $(KID_DATA) $(KID_BIN) $(GUARD_DATA) $(SKEL_DATA) \
|
||
$(VIZIER_DATA) $(SHADOW_DATA) $(SND_DATA) $(LVL_DATA)
|
||
include $(PROJ_ROOT)/app.mk
|
||
|
||
# Образ HDD для MAME: ресурсы раскладываются ПО КАТАЛОГАМ (8.3, как в DSS),
|
||
# чтобы корень диска не зарастал десятками .atl.
|
||
# BG\ — фон (env/wall/fore/pot)
|
||
# KID\ — персонаж (kid0..27, палитра, меч, таблицы анимации)
|
||
# GUARD\ — стражи (появятся здесь же)
|
||
# LEVELS\ — уровни
|
||
hdd: $(EXAMPLE).exe
|
||
$(PROJ_ROOT)/toolchain/make_hdd.sh $(PROJ_ROOT)/mame/v306/IMG/test_hdd.chd \
|
||
$(EXAMPLE).exe \
|
||
$(foreach f,$(BG_DATA),BG:$(f)) \
|
||
$(foreach f,$(KID_DATA) $(KID_BIN),KID:$(f)) \
|
||
$(foreach f,$(GUARD_DATA),GUARD:$(f)) \
|
||
$(foreach f,$(SKEL_DATA),SKEL:$(f)) \
|
||
$(foreach f,$(VIZIER_DATA),VIZIER:$(f)) \
|
||
$(foreach f,$(SHADOW_DATA),SHADOW:$(f)) \
|
||
$(foreach f,$(SND_DATA),SND:$(f)) \
|
||
$(foreach f,$(LVL_DATA),LEVELS:$(f))
|
||
|
||
TC := $(PROJ_ROOT)/applications/PoP/toolchain
|
||
$(BG_DATA): $(TC)/pop_pack_bg.py $(TC)/render_room.py
|
||
cd $(TC) && python3 pop_pack_bg.py dungeon && python3 pop_pack_bg.py palace
|
||
$(GUARD_DATA): $(TC)/pop_pack_guard.py
|
||
cd $(TC) && python3 pop_pack_guard.py
|
||
|
||
$(SKEL_DATA): $(TC)/pop_pack_guard.py
|
||
cd $(TC) && python3 pop_pack_guard.py SKEL
|
||
|
||
$(VIZIER_DATA): $(TC)/pop_pack_guard.py
|
||
cd $(TC) && python3 pop_pack_guard.py VIZIER
|
||
|
||
$(SHADOW_DATA) pop_shadow_atlas.h: $(TC)/pop_pack_shadow.py $(TC)/pop_pack_bg.py $(TC)/pop_pack_kid.py
|
||
cd $(TC) && python3 pop_pack_shadow.py
|
||
|
||
$(SND_DATA) pop_sound_tbl.h: $(TC)/pop_pack_sound.py
|
||
cd $(TC) && python3 pop_pack_sound.py
|
||
|
||
$(KID_DATA): $(TC)/pop_pack_kid.py $(TC)/pop_pack_bg.py
|
||
cd $(TC) && python3 pop_pack_kid.py
|
||
kid_data.h $(KID_BIN): $(TC)/pop_extract_kid_data.py
|
||
cd $(TC) && python3 pop_extract_kid_data.py
|
||
|
||
$(EXAMPLE).exe: roomtest_cold.c roomtest_cold.h guards.c pop_guard.c pop_guard_cold.c pop_guard.h pop_cdraw.c pop_cdraw.h pop_draw.c _pop_draw.h pop_tile.c pop_tile.h pop_state.c pop_state.h pop_cheat.h pop_bg.c pop_bg.h pop_room.c _pop_bg.h pop_geom.c pop_geom.h pop_redraw.c pop_redraw.h pop_vflip.c pop_vflip.h pop_kid.c pop_kdraw.c pop_kboot.c _pop_kid.h _pop_kdraw.h pop_kid.h pop_ctrl.c pop_ctrl.h pop_map.c pop_map.h pop_level.c pop_level_cold.c _pop_level.h pop_level.h pop_trob.c pop_trob.h kid_data.h $(BG_DATA) $(KID_DATA) $(KID_BIN) $(LVL_DATA)
|