Files
Sprinter-SDCC/applications/PoP/roomtest/pop_tile.h
T
snark13 b6699b3aef Разрез pop_tile: холодная половина в банк 5 (−1788 Б резидента)
pop_tile.c был крупнейшим жильцом резидента (5972 Б кода).  Целиком он не
уедет — его const-таблицы читают банки 2, 3, 7 и 8, а таблица в чужом банке
не видна.  Поэтому разрез, а не перенос.

Отбор ЗАМЕРОМ, а не по смыслу: каждая функция посчитана брейкпоинтом-
счётчиком в MAME — сколько вызовов в кадре покоя и сколько в кадре полной
перерисовки комнаты (форсируется читом +/-).  Порог — пик не больше 3.
Проверено на ДВУХ тайлсетах, подземелье и дворец: pop_mem_b рисует
композитный кусок и мог оказаться дворцовым, но и там 0 вызовов.

Уехало: pop_mem_b, pop_cd_hit (+hit_rect), pop_cd_hit_slot, pop_cd_init,
pop_cd_clear, pop_t_win_set/clear, pop_bar_black, pop_heal_off,
pop_potion_flask, pop_room_set_above/below.
Осталось: pop_blit_b со статиками (184 вызова на редрав), pop_cd_touch
(198), pop_tile_code (296), pop_wall_modifier (101), pop_env_b (73),
pop_tile_mod (70), все таблицы.  pop_fore_set_clip оставлен намеренно —
88 Б не стоят отказа от прямого вызова из банка 4.

Цена трамплина замерена: 252 такта пролог + 84 эпилог + ~50 у вызывающего
= ~410.  Это ~1000 тактов на кадр покоя (0,2 % работы) и ~3700 на редрав.

blit_b_clip перестал быть static и объявлен в _pop_tile.h: вызов
банк -> резидент прямой, трамплин не нужен, поэтому статик горячей половины
переносить следом не пришлось.

Итог: _CODE 23716 -> 21928, свободно 747 -> 2535 Б (с 129 Б до всех работ).
Проверено в MAME на уровнях 1 и 4, с переходами комнат.

Метод замера, таблица частот и ловушка с данными банка — docs/resident_budget.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 22:26:05 +03:00

189 lines
14 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* pop_tile.h — ОБЩИЕ ЛИСТЬЯ отрисовки фона: блит куска атласа, чтение тайла
* комнаты, таблица тайлов. Внутренний заголовок слоя фона, НЕ публичный
* API (публичный — pop_bg.h).
*
* ПОЧЕМУ ОТДЕЛЬНЫЙ РЕЗИДЕНТНЫЙ МОДУЛЬ (pop_tile.c в _CODE / W1), а не часть
* банка 2. Слой фона режется надвое: горячая половина (fore-проход поверх
* персонажей, каждый кадр) и холодная (полная отрисовка комнаты и точечные
* перерисовки тайлов, раз на комнату/событие). Обе половины зовут одни и те
* же листья — blit_b, tile_code, tile_table. Разложить их по банкам нельзя:
*
* - код в чужом банке зовётся ТОЛЬКО через трамплин (по `__banked` в
* объявлении), и пометка бьёт по ВСЕМ вызывающим, включая горячих;
* - `const`-таблицы банка лежат в его СТРАНИЦЕ (0xC000+) и видны, лишь
* пока банк замаплен — из другого банка их не прочитать вовсе;
* - дублировать листья в оба банка — лишние килобайты и два места правки.
*
* Резидент W1 замаплен ВСЕГДА, поэтому и горячая, и холодная половина зовут
* их обычным `call` без трамплина, а таблицы читают напрямую. Тот же приём,
* что у pop_heal_fast (pop_draw.c) и pop_sword_draw (pop_kid.c).
*
* ПИСУЧИЕ глобалы (атласы, указатели на комнату, окно клипа) лежат в _DATA
* (W2) и видны отовсюду независимо от банка — они здесь ради одного места
* определения, а не ради доступности.
*/
#ifndef POP_TILE_H
#define POP_TILE_H
#include <stdint.h>
#include <sprite.h>
#include "pop_geom.h" /* pop_rnd_t — сид кладки комнаты */
/* ---- Атласы фона (прямая адресация id) ------------------------------ *
* env[id>>POP_ENV_SHIFT] idx id&POP_ENV_MASK — chtab_6 environment;
* wall — chtab_7; fore — передний слой; pot — chtab_1 (зелья, меч).
* Раскладка и число страниц — из pop_bg_atlas.h (генерит упаковщик).
* Тайлсетов ДВА (подземелье/дворец), но раскладка id у них общая:
* подменяются только файлы и 32 записи палитры (см. pop_bg_set_tileset). */
#include "pop_bg_atlas.h"
extern atlas_t pop_env[POP_ENV_PAGES], pop_wall_at, pop_fore_at, pop_pot_at;
/* Загруженный тайлсет: 0 подземелье, 1 дворец. Ставит pop_bg_load. По нему
* расходится с десяток веток отрисовки (seg008) и модификатор пустой клетки
* от упавшей плиты (seg007:0EB8). */
extern uint8_t pop_palace;
/* ---- Текущая отрисованная комната ----------------------------------- *
* fg — foretable-коды, bg — модификаторы; lfg/lbg — правый столбец ЛЕВОГО
* соседа (кромка); below — ряд 0 комнаты снизу (11 байт, см. pop_bg.h);
* above/abovemod — ряд 2 комнаты сверху (полоса кладки у потолка). */
extern const uint8_t *pop_t_fg, *pop_t_bg, *pop_t_lfg, *pop_t_lbg;
extern const uint8_t *pop_t_below, *pop_t_above, *pop_t_abovemod;
extern uint8_t pop_t_room; /* номер комнаты — зерно раскладки стен */
extern pop_rnd_t pop_t_seed; /* сид кладки (seg009 PRNG) */
/* Верхний клип блитов фона (ЭКРАННАЯ y): !=0 — строки кадра выше границы не
* рисуются. Нужен полосе кладки у потолка (ряд −1): высокие куски не должны
* лезть в верхний борт. 0 — без клипа. */
extern int pop_t_clip_top;
/* ОКНО FORE-СЛОЯ (экранные координаты, полуинтервал). Пока включено,
* blit_b рисует только ту часть куска, что попадает в окно.
*
* Зачем: fore-слой существует РОВНО для того, чтобы вернуть куски тайлов
* поверх нарисованного персонажа — за пределами его прямоугольника в
* видеопамяти и так лежит правильный фон. А рисовал он тайлы ЦЕЛИКОМ:
* стена (код 20) — блок 64×63 = 4032 пикселя, и таких кусков за проход до
* девяти. Замер в MAME 2026-07-29 (комната 3, Kid + страж): проход поверх
* Kid — 402 816 тактов, поверх стража — 431 964, вместе 78 % всей работы
* кадра при бюджете 430 000. С окном большинство кусков отсекается целиком,
* остальные режутся до пересечения. */
extern int pop_t_fclip_x0, pop_t_fclip_y0, pop_t_fclip_x1, pop_t_fclip_y1;
/* Та же рамка в ЛОГИЧЕСКИХ координатах (без переворота). Всё, что решает
* «задевает ли ТАЙЛ окно», обязано сравнивать по ней: позиция тайла в
* комнате от переворота не зависит. Экранная пара — только для клипа
* блита, который режет уже перевёрнутый прямоугольник. */
extern int pop_t_fclip_ly0, pop_t_fclip_ly1;
extern uint8_t pop_t_fclip_on;
/* Поставить то же окно под ТОЧЕЧНУЮ ПЕРЕРИСОВКУ / ЗАПЕЧКУ. Прямоугольник —
* тот, который восстанавливаем (heal или бар-в-чёрный), в ЛОГИЧЕСКИХ
* экранных координатах (с POP_YOFF, но до переворота); экранную пару
* функция считает сама.
*
* Зачем: точечная перерисовка зовёт draw_tile, а тот честно рисует ВЕСЬ тайл.
* У полосы кладки у потолка это особенно дорого: куски ряда 0 высотой 63 px
* рисуются целиком, хотя восстановить надо девять верхних строк (замер
* 2026-08-17: блит в pop_ceil_bake_empty стоит 21 447 против 14 384 в
* pop_floor_bake — ровно разница в высоте). Окно и отсеивает куски, вовсе не
* попавшие в прямоугольник (предфильтр pop_blit_b — ДО atlas_image и
* gfx_w0_map), и режет остальные по нему.
*
* ВАЖНО: пока окно стоит, pop_blit_b НЕ ставит пометку «фон трогали» (признак
* fore-прохода) — вызывающий обязан пометить прямоугольник САМ (pop_cd_touch
* или pop_bar_black, который метит сам). */
void pop_t_win_set(int x, int ytop, int w, int h) __banked;
void pop_t_win_clear(void) __banked;
/* ---- Таблица тайлов (piece, seg008.c:27) ---------------------------- */
typedef struct {
uint8_t base_id, floor_left; int8_t base_y;
uint8_t right_id, floor_right; int8_t right_y;
uint8_t stripe_id, topright_id, bottom_id, fore_id, fore_x; int8_t fore_y;
} piece;
extern const piece pop_tile_table[31];
/* Таблицы, нужные ОБЕИМ половинам слоя фона (односторонние живут по месту). */
extern const uint8_t POP_COL_XH[10]; /* x тайла = COL_XH[col]*8 */
/* Деление координаты на ширину тайла таблицей (оригинал: tile_div_tbl /
* tile_mod_tbl, seg006:702). Индекс — координата 0..255 как есть, смещение
* -58 уже внутри: POP_TILE_DIV[x] = floor((x-58)/14), POP_TILE_MOD[x] —
* остаток. Резидент: нужны и карте (банк 3), и слою фона (банк 2). */
extern const int8_t POP_TILE_DIV[256];
extern const uint8_t POP_TILE_MOD[256];
extern const uint8_t POP_WALL_FRAM_BOTTOM[4]; /* нижняя грань стены по bg */
extern const uint8_t POP_WALL_FRAM_MAIN[4]; /* основная грань стены */
extern const uint8_t POP_SPIKES_FRAM_LEFT[10]; /* кадр пик в своей ячейке */
extern const uint8_t POP_LOOSE_FRAM_LEFT[12]; /* верх дрожащей плиты */
extern const uint8_t POP_LOOSE_FRAM_BOTTOM[12]; /* низ дрожащей плиты */
extern const uint8_t POP_CHOMP_FRAM_FOR[5]; /* чомпер: перед (pop_fore!) */
/* Режим ЗАПЕЧКИ статического фона (запись идёт и в ОЗУ-копию акселератора,
* из которой потом восстанавливает heal). В этом режиме ТРАНЗИЕНТНЫЕ кадры
* рисовать НЕЛЬЗЯ — ни свои, ни соседские: кадр законсервируется в копии, и
* heal будет возвращать его вечно, даже когда ловушка давно вернулась в
* покой (пики: BUG-SPIKE-BAKED). А так как запечка идёт двумя кадрами (по
* одному на страницу дабл-буфера), на страницах могут застыть ещё и РАЗНЫЕ
* кадры — вечное мерцание. Фон обязан быть состоянием ПОКОЯ; анимация
* каждый кадр кладётся поверх (банк SPRITE).
*
* Флаг читают ВСЕ функции «кадр по модификатору»: pop_loose_frame,
* pop_spike_frame, pop_chomp_pose. Появится новая анимированная ловушка —
* её кадр обязан спрашивать флаг ТАМ ЖЕ, а не в вызывающем слое: слоёв
* отрисовки у тайла четыре, и забыть один слишком легко. */
extern uint8_t pop_t_bake_rest;
/* Кадр дрожащей/падающей плиты по её room_modif (seg007 loose_fram_*). */
uint8_t pop_loose_frame(uint8_t modif);
/* Кадр пик по их modif (индекс в spikes_fram_*): 0 = не видно, 5 = вышли
* целиком. В режиме запечки — всегда 0 (см. pop_t_bake_rest). */
uint8_t pop_spike_frame(uint8_t modif);
/* Номер позы чомпера по его модификатору (seg008 chomper_fram1): 0..4 —
* индекс во ВСЕ три набора кусков (низ/верх/перед) и в кровь (+114/+119). */
uint8_t pop_chomp_pose(uint8_t modif);
/* ---- Блит куска атласа ---------------------------------------------- *
* НИЗ спрайта на ybottom (room-space, POP_YOFF добавляется внутри), левый
* край на x, прозрачно (0xFF). w/h читаются из getimage-заголовка ленты (в
* W0). Режется окном fore-клипа и pop_t_clip_top. */
void pop_blit_b(atlas_t *a, uint8_t idx, int x, int ybottom);
/* Блит getimage-блоба из обычной памяти (W2) — без atlas_image и без
* маппинга страницы; клип полосы у потолка и окно перерисовки те же, что у
* pop_blit_b. Для композитных спрайтов, собранных в рантайме. */
void pop_mem_b(const uint8_t *img, int x, int ybottom) __banked;
void pop_env_b(uint8_t id, int x, int yb);
void pop_wall_b(uint8_t id, int x, int yb);
void pop_fore_b(uint8_t id, int x, int yb);
void pop_pot_b(uint8_t id, int x, int yb);
/* heal в room-space (со сдвигом POP_YOFF) — все heal слоя фона/loose. */
void pop_heal_off(int x, int y, int w, int h) __banked;
/* Чёрная плита по ЭКРАННЫМ координатам, с учётом переворота (POP_FLIP_TOP).
* Пишет банком 0x50 — и в видео-ОЗУ, и в ОЗУ-копию, иначе heal вернёт
* стёртое. ВСЕ стирания слоя фона обязаны идти через неё, а не через голый
* bar: иначе при перевороте плита ложится в неотражённое место (так
* ломались решётка и плита-потолок). */
void pop_bar_black(int x, int top, int w, int h) __banked;
/* ---- Чтение тайла отрисованной комнаты ------------------------------ */
uint8_t pop_tile_code(int row, int col);
uint8_t pop_tile_mod(int row, int col);
/* Тип «как он РИСУЕТСЯ» — подстановка нажатой кнопки (get_tile_to_draw,
* seg008:240): opener → floor, closer → stuck при таймере связи > 1. */
uint8_t pop_tile_code_drawn(int row, int col);
/* seg008.c:1255: сосед — стена, если его тип == 20. 0 SWS / 1 SWW / 2 WWS
* / 3 WWW. На кромке col0 сосед по умолчанию = стена (как render_room). */
uint8_t pop_wall_modifier(int row, int col);
/* Склянка зелья (draw_tile_fore, chtab_1): нужна и полной отрисовке тайла, и
* fore-проходу поверх персонажа. */
void pop_potion_flask(int row, int col, uint8_t modif) __banked;
#endif