Files
Sprinter-SDCC/applications/PoP/roomtest/pop_tile.h
T
snark13 b2da0b85b5 Композит падающего куска: циан в бюджете (632 000 -> 273 078)
Три части падающей плиты (env 70/74/72) складываются в ОДИН getimage-блоб
при загрузке тайлсета, и кусок рисуется одним блитом вместо трёх.  Блоб
лежит в обычной памяти (W2), поэтому вызов идёт без atlas_image и без
gfx_w0_map/unmap — ещё ~1 350 такта.  Прозрачность соблюдена: части
ПЕРЕКРЫВАЮТСЯ (74 и 70 обе от mob_x), поэтому композит собирается
попиксельно с пропуском 0xFF, то есть точно как три прозрачных блита.

Мотив: у блита ~8 800 такта постоянных накладных против ~5 000 на пиксели.
Шесть кусков в воздухе = 18 вызовов = 258 708 такта, больше половины
цианового блока.  Резервный путь на три блита оставлен (mob_spr_ok).

  циан пик   435 180 -> 273 078   (цель 400 000 — ВЫПОЛНЕНА)
  работа     1 037 250 -> 931 782
  зелёная      558 498 -> 557 706 (не затронута, ею занимаемся дальше)

Заодно НАЙДЕН И ПОФИКШЕН БАГ КОРИДОРА heal.  Снятые из каталогов реальные
габариты частей оказались другими, чем в комментарии, И РАЗНЫМИ у тайлсетов:

    подземелье  70 = 32x16   74 = 26x15   72 = 26x16
    дворец      70 = 32x13   74 = 25x15   72 = 31x13

То есть во ДВОРЦЕ (уровни 4-6, 10, 11, 13, 14) кусок достаёт до mob_x+62, а
коридор heal был mob_x-4 .. mob_x+59 — правые три пикселя не стирались
никогда.  Четыре пикселя слева при этом чистились впустую: левее mob_x
кусок не рисует ничего.  Коридор стал mob_x .. mob_x+63, площадь та же.
Высоту (24 строки при следе 19) НЕ сужаем: 24 — это след подземелья плюс
две строки поля с каждой стороны, «сужение по палаццовому следу» сломало бы
подземелье.

Ещё три правки того же захода:

- pop_blit_b: аргументы в file-scope.  Третий и дальше SDCC передаёт стеком,
  и каждое чтение шло через `-N(ix)` — 76 обращений.  Стало 11.
- pop_loose_mob_tick: пометки всех кусков одним пакетом (было по 4 502 такта
  на кусок).  mobTk 176 772 -> 168 600.
- pop_floor_bake: окно клипа проверено ВТОРОЙ раз (после того как
  blit_b_clip подешевел) и снова хуже — 182 124 -> 188 460.  Причина
  записана в коде: у этого тайла клипа нет, блиты идут быстрым путём, а окно
  их уводит в клипованный и при этом не отсеивает ни одного куска и не
  режет ни одной строки.  Не пробовать в третий раз.

Новый лист pop_mem_b (резидент): блит блоба из обычной памяти с теми же
клипом полосы у потолка и окном перерисовки, что у pop_blit_b.

Проверено: 8 наборов tests-host зелёные; в MAME кадр с летящими плитами и
итоговая картинка совпадают с прежними.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 14:01:48 +03:00

178 lines
13 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);
void pop_t_win_clear(void);
/* ---- Таблица тайлов (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). В этом режиме дрожащие кадры
* ЧУЖИХ плит рисовать НЕЛЬЗЯ: транзиентный кадр законсервируется в копии, а
* так как запечка идёт двумя кадрами (по одному на страницу дабл-буфера),
* на страницах застынут РАЗНЫЕ кадры — вечное мерцание. Фон обязан быть
* состоянием ПОКОЯ; анимация каждый кадр кладётся поверх (банк SPRITE). */
extern uint8_t pop_t_bake_rest;
/* Кадр дрожащей/падающей плиты по её room_modif (seg007 loose_fram_*). */
uint8_t pop_loose_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);
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);
/* Чёрная плита по ЭКРАННЫМ координатам, с учётом переворота (POP_FLIP_TOP).
* Пишет банком 0x50 — и в видео-ОЗУ, и в ОЗУ-копию, иначе heal вернёт
* стёртое. ВСЕ стирания слоя фона обязаны идти через неё, а не через голый
* bar: иначе при перевороте плита ложится в неотражённое место (так
* ломались решётка и плита-потолок). */
void pop_bar_black(int x, int top, int w, int h);
/* ---- Чтение тайла отрисованной комнаты ------------------------------ */
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);
#endif