Разрез 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>
This commit is contained in:
@@ -0,0 +1,111 @@
|
|||||||
|
# Бюджет резидента W1/W2: как мерить и как освобождать
|
||||||
|
|
||||||
|
Резидент huge-режима — окно `0x4100..0xBB00` (стек с 0xBB00): код в W1,
|
||||||
|
данные в W2, между концом данных и стеком остаётся куча. Всё, что туда не
|
||||||
|
влезло, живёт в банках.
|
||||||
|
|
||||||
|
## Как СМОТРЕТЬ, а не гадать
|
||||||
|
|
||||||
|
**Карта линкера врёт.** File-static SDCC в неё не попадает, и «дырка» между
|
||||||
|
двумя именованными символами приписывается предыдущему целиком. По карте
|
||||||
|
выходило, что у `pop_bg` 1529 Б данных (на деле 289), а у `pop_t_win_clear`
|
||||||
|
1282 Б кода — при том, что это однострочник, а 1282 Б это два статических
|
||||||
|
помощника соседнего `pop_blit_b`.
|
||||||
|
|
||||||
|
Точный источник — объектные файлы: строки `A <area> size <n> flags <f>` в
|
||||||
|
`.rel` дают ровный размер каждой области модуля, а `S <sym> Def/Ref` — кто
|
||||||
|
символ определяет и кто на него ссылается.
|
||||||
|
|
||||||
|
**Частоту вызовов мерить в MAME счётчиком**, а не оценивать по смыслу:
|
||||||
|
|
||||||
|
```
|
||||||
|
bpset <frame_probe>,1,{printf "F %d ...",temp0,...; temp0=0;...; g}
|
||||||
|
bpset <func_addr>,1,{temp0=temp0+1; g}
|
||||||
|
```
|
||||||
|
|
||||||
|
Обязательна **канарейка** — счётчик заведомо горячей функции в том же
|
||||||
|
прогоне. Дважды спасала: один раз показала, что перехода комнаты в окне
|
||||||
|
замера не было (все нули), другой — что зонды вообще не встали (в zsh
|
||||||
|
`set -- $pair` НЕ разбивает строку на слова, и адрес уезжал в мусор).
|
||||||
|
Полную перерисовку комнаты форсировать читом `+`/`-`, ходьбой ненадёжно.
|
||||||
|
|
||||||
|
## Сделано
|
||||||
|
|
||||||
|
### 1. malloc вон из резидента (−613 Б)
|
||||||
|
|
||||||
|
`cbl_open` держал `malloc`/`free` в мёртвой ветке `CBL_UNDERRUN_SILENCE`, а
|
||||||
|
линкер тянет `.rel` целиком — и куча приезжала каждому приложению. Разведены
|
||||||
|
две публичные точки входа (`cbl_open` / `cbl_open_silence`) поверх общего
|
||||||
|
`_cbl_open_raw`; `cbl_close` больше не зовёт `free`.
|
||||||
|
|
||||||
|
### 2. Разрез pop_tile: холодная половина в банк 5 (−1788 Б)
|
||||||
|
|
||||||
|
`pop_tile.c` был крупнейшим жильцом резидента (5 972 Б кода). Целиком он не
|
||||||
|
уедет: его const-таблицы (`POP_TILE_DIV/MOD`, `pop_tile_table`, таблицы
|
||||||
|
кадров) читают банки 2, 3, 7 и 8, а таблица в чужом банке не видна.
|
||||||
|
|
||||||
|
Отбирали ЗАМЕРОМ, на двух тайлсетах (подземелье ур. 1 и дворец ур. 4 —
|
||||||
|
`pop_mem_b` рисует композитный кусок и мог оказаться дворцовым). Порог —
|
||||||
|
пик не больше 3 вызовов на кадр.
|
||||||
|
|
||||||
|
| уехало в банк 5 | пик/кадр | | осталось в резиденте | пик/кадр |
|
||||||
|
|---|---:|---|---|---:|
|
||||||
|
| `pop_mem_b` | 0 | | `pop_tile_code` | 296 |
|
||||||
|
| `pop_cd_hit` (+`hit_rect`) | 0 | | `pop_cd_touch` | 198 |
|
||||||
|
| `pop_t_win_set/clear` | 0..1 | | `pop_blit_b` (+2 статика) | 184 |
|
||||||
|
| `pop_heal_off` | 0..1 | | `pop_wall_modifier` | 101 |
|
||||||
|
| `pop_potion_flask` | 0..1 | | `pop_env_b` | 73 |
|
||||||
|
| `pop_room_set_above/below` | 1 | | `pop_tile_mod` | 70 |
|
||||||
|
| `pop_cd_init/clear` | 1 | | `pop_cd_batch_end` | 40 |
|
||||||
|
| `pop_bar_black` | 3 | | `pop_fore_set_clip` | 2 |
|
||||||
|
| `pop_cd_hit_slot` | 2..3 | | все const-таблицы | — |
|
||||||
|
|
||||||
|
`pop_fore_set_clip` (88 Б) оставлен намеренно: не стоит отказа от прямого
|
||||||
|
вызова из банка 4, ради которого он и заводился.
|
||||||
|
|
||||||
|
**Цена трамплина замерена**: 252 такта пролог + 84 эпилог + ~50 на стороне
|
||||||
|
вызывающего = **~410 тактов** на вызов. Итого ~1 000 тактов на кадр покоя
|
||||||
|
(0,2 % работы) и ~3 700 на кадр редрава (0,009 растра).
|
||||||
|
|
||||||
|
**Ключ, почему это безопасно:** вызов банк → резидент ПРЯМОЙ, трамплин не
|
||||||
|
нужен (W1/W2 замаплены всегда). Поэтому `blit_b_clip` просто перестал быть
|
||||||
|
`static` и объявлен в `_pop_tile.h`, а не переехал следом за `pop_mem_b`.
|
||||||
|
|
||||||
|
### Итог
|
||||||
|
|
||||||
|
| | было | стало |
|
||||||
|
|---|---:|---:|
|
||||||
|
| `_CODE` резидента | 24 329 | **21 928** |
|
||||||
|
| свободно до стека | **129 Б** | **2 535 Б** |
|
||||||
|
| BANK5 | 2 080 (13 %) | 3 954 (24 %) |
|
||||||
|
|
||||||
|
Проверено в MAME: уровень 1 (подземелье) и уровень 4 (дворец), переходы
|
||||||
|
комнат читом `+`, ходьба — фон, факелы, решётки, гобелены, колонны без
|
||||||
|
искажений.
|
||||||
|
|
||||||
|
## ЛОВУШКА: данные банка в его страницу — НЕ ДЕЛАТЬ без разбора
|
||||||
|
|
||||||
|
Отдельная попытка (`--bank-data=SRC`, коммиты 3545826/9025573) **откачена**:
|
||||||
|
перенос писучих данных банкового модуля в его 16-КБ страницу давал цветной
|
||||||
|
мусор блоками и ронял DSS.
|
||||||
|
|
||||||
|
У `pop_trob` причина найдена: `pop_trob_modif()` ВОЗВРАЩАЕТ УКАЗАТЕЛЬ на
|
||||||
|
`room_modif[24][30]`, а зовут её из банков 2, 3, 7 и резидента — после
|
||||||
|
переноса они пишут по 0xC000+ в СВОЮ страницу, поверх чужого кода.
|
||||||
|
Def/Ref-анализ такого не видит: снаружи ссылки на символ нет, есть ссылка на
|
||||||
|
функцию, отдающую его адрес. Но и `pop_room`, у которого утечки указателя
|
||||||
|
найти не удалось, ломался так же — механизм понят не до конца.
|
||||||
|
|
||||||
|
Нулевая инициализация при этом ни при чём: `mkexe -p 0` был проверен по
|
||||||
|
образу (прогон нулей 14 304 Б, самый длинный прогон 0xFF — 14).
|
||||||
|
|
||||||
|
**Перенос КОДА в банк — штатный путь, на нём стоят все наши банки. Ломался
|
||||||
|
именно перенос ДАННЫХ.**
|
||||||
|
|
||||||
|
## Что осталось
|
||||||
|
|
||||||
|
- `roomtest.c` 2 508 Б и `pop_kid.c` 2 418 Б — следующие по величине, но оба
|
||||||
|
горячие (главный цикл и `play_seq`).
|
||||||
|
- `pop_level.c` 956 Б кода + 660 Б данных (из них `pop_dl1`/`pop_dl2` по
|
||||||
|
256 Б — таблицы дверных связей).
|
||||||
|
- BANK7 на 77 %: если понадобится место в нём — выносить `pop_redraw.c`.
|
||||||
@@ -35,7 +35,7 @@ MEMORY ?= huge # small-раскладка + банки кода в W
|
|||||||
# ВАЖНО: любое сравнение занятости банков имеет смысл только при ОДНОМ и том
|
# ВАЖНО: любое сравнение занятости банков имеет смысл только при ОДНОМ и том
|
||||||
# же ALLOCS — иначе сравниваются не правки, а уровни оптимизации.
|
# же ALLOCS — иначе сравниваются не правки, а уровни оптимизации.
|
||||||
ALLOCS ?= 3000
|
ALLOCS ?= 3000
|
||||||
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 $(PROF_FLAGS)
|
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 5=pop_tile_cold.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 $(PROF_FLAGS)
|
||||||
# Профилирование полосами бордюра (см. PROF() в roomtest.c) — ВКЛЮЧЕНО по
|
# Профилирование полосами бордюра (см. PROF() в roomtest.c) — ВКЛЮЧЕНО по
|
||||||
# умолчанию, пока идёт работа с производительностью; make PROF=0 выключает.
|
# умолчанию, пока идёт работа с производительностью; make PROF=0 выключает.
|
||||||
# Каждая фаза кадра красит бордюр в свой цвет, высота полосы на скриншоте
|
# Каждая фаза кадра красит бордюр в свой цвет, высота полосы на скриншоте
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* _pop_tile.h — внутренний контракт между половинами общих листьев слоя
|
||||||
|
* фона (НЕ публичный, публичный — pop_tile.h). Горячая половина живёт в
|
||||||
|
* резиденте (pop_tile.c), холодная — в банке 5 (pop_tile_cold.c);
|
||||||
|
* критерий разреза и замеры — в шапке pop_tile_cold.c.
|
||||||
|
*/
|
||||||
|
#ifndef _POP_TILE_INTERNAL_H
|
||||||
|
#define _POP_TILE_INTERNAL_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* Клипованный путь блита — статик горячей половины, ставший общим: его
|
||||||
|
* зовёт pop_mem_b из холодной. Переносить его следом НЕЛЬЗЯ (его основной
|
||||||
|
* вызывающий pop_blit_b делает 184 вызова на кадр редрава), а вызов
|
||||||
|
* банк -> резидент и так прямой: W1/W2 замаплены всегда. */
|
||||||
|
void blit_b_clip(const uint8_t *img, int x, int top, uint8_t w, uint8_t h);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -64,12 +64,12 @@ void pop_room_draw(uint8_t room_num,
|
|||||||
* [10] — тайл (0,9) комнаты СНИЗУ-СЛЕВА (сосед угла (2,0); см.
|
* [10] — тайл (0,9) комнаты СНИЗУ-СЛЕВА (сосед угла (2,0); см.
|
||||||
* load_rowbelow, seg008:368). 0 — нет комнаты снизу. */
|
* load_rowbelow, seg008:368). 0 — нет комнаты снизу. */
|
||||||
/* НЕ __banked: тело в резиденте (pop_tile.c) — из банка зовётся прямым call. */
|
/* НЕ __banked: тело в резиденте (pop_tile.c) — из банка зовётся прямым call. */
|
||||||
void pop_room_set_below(const uint8_t *below_row0_fg);
|
void pop_room_set_below(const uint8_t *below_row0_fg) __banked;
|
||||||
|
|
||||||
/* Задать ряд 2 (fg-коды + модификаторы, 10 шт.) комнаты СВЕРХУ — для полосы
|
/* Задать ряд 2 (fg-коды + модификаторы, 10 шт.) комнаты СВЕРХУ — для полосы
|
||||||
* кладки у потолка (seg008 draw_room доп. верхний ряд из room_A). 0 — нет
|
* кладки у потолка (seg008 draw_room доп. верхний ряд из room_A). 0 — нет
|
||||||
* комнаты сверху (кромка уровня → пол). */
|
* комнаты сверху (кромка уровня → пол). */
|
||||||
void pop_room_set_above(const uint8_t *above_row2_fg, const uint8_t *above_row2_mod);
|
void pop_room_set_above(const uint8_t *above_row2_fg, const uint8_t *above_row2_mod) __banked;
|
||||||
|
|
||||||
/* Перерисовать передний слой (fore) и оверлеи тайлов ПОВЕРХ персонажа —
|
/* Перерисовать передний слой (fore) и оверлеи тайлов ПОВЕРХ персонажа —
|
||||||
* ОДИН проход на всех Char (Кид, страж, скелет, дальше тень/визирь): в
|
* ОДИН проход на всех Char (Кид, страж, скелет, дальше тень/визирь): в
|
||||||
|
|||||||
@@ -128,12 +128,12 @@ extern uint8_t pop_cd_dirty; /* биты страниц: ме
|
|||||||
extern uint8_t pop_cd_ymin[2][10], pop_cd_ymax[2][10];
|
extern uint8_t pop_cd_ymin[2][10], pop_cd_ymax[2][10];
|
||||||
/* Задели ли прямоугольник (ЭКРАННЫЕ координаты, x1/y1 включительно) то,
|
/* Задели ли прямоугольник (ЭКРАННЫЕ координаты, x1/y1 включительно) то,
|
||||||
* что трогали на странице p. Резидент (pop_tile.c). */
|
* что трогали на странице p. Резидент (pop_tile.c). */
|
||||||
uint8_t pop_cd_hit(uint8_t p, int x0, int y0, int x1, int y1);
|
uint8_t pop_cd_hit(uint8_t p, int x0, int y0, int x1, int y1) __banked;
|
||||||
/* То же для СЛОТА персонажа: спрайт и накладной, координаты изнутри pop_cd
|
/* То же для СЛОТА персонажа: спрайт и накладной, координаты изнутри pop_cd
|
||||||
* (см. тело — там про цену пяти аргументов). */
|
* (см. тело — там про цену пяти аргументов). */
|
||||||
uint8_t pop_cd_hit_slot(uint8_t who, uint8_t p);
|
uint8_t pop_cd_hit_slot(uint8_t who, uint8_t p) __banked;
|
||||||
void pop_cd_clear(uint8_t p); /* снять метку страницы целиком */
|
void pop_cd_clear(uint8_t p) __banked; /* снять метку страницы целиком */
|
||||||
void pop_cd_init(void); /* пустые диапазоны на обеих страницах */
|
void pop_cd_init(void) __banked; /* пустые диапазоны на обеих страницах */
|
||||||
void pop_cd_touch(int x, int y, int w, int h);
|
void pop_cd_touch(int x, int y, int w, int h);
|
||||||
#define POP_CD_TOUCH_ALL() pop_cd_touch(0, 0, 320, 256)
|
#define POP_CD_TOUCH_ALL() pop_cd_touch(0, 0, 320, 256)
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#include "pop_pace.h" /* pop_beam_sample — счёт кадров по лучу */
|
#include "pop_pace.h" /* pop_beam_sample — счёт кадров по лучу */
|
||||||
#include "pop_level.h" /* pop_doorlink2 — подстановка нажатой кнопки */
|
#include "pop_level.h" /* pop_doorlink2 — подстановка нажатой кнопки */
|
||||||
#include "_pop_draw.h" /* pop_heal_fast */
|
#include "_pop_draw.h" /* pop_heal_fast */
|
||||||
|
#include "_pop_tile.h" /* контракт с холодной половиной (pop_tile_cold.c) */
|
||||||
|
|
||||||
/* ---- Состояние (писучее, _DATA/W2 — видно из любого банка) ---------- */
|
/* ---- Состояние (писучее, _DATA/W2 — видно из любого банка) ---------- */
|
||||||
atlas_t pop_env[POP_ENV_PAGES], pop_wall_at, pop_fore_at, pop_pot_at;
|
atlas_t pop_env[POP_ENV_PAGES], pop_wall_at, pop_fore_at, pop_pot_at;
|
||||||
@@ -307,89 +308,6 @@ void pop_cd_touch(int x, int y, int w, int h)
|
|||||||
/* Задел ли прямоугольник (ЭКРАННЫЕ координаты, x1/y1 ВКЛЮЧИТЕЛЬНО) то, что
|
/* Задел ли прямоугольник (ЭКРАННЫЕ координаты, x1/y1 ВКЛЮЧИТЕЛЬНО) то, что
|
||||||
* трогали на странице p. Резидент: зовёт pop_cdraw.c из банка 4 прямым
|
* трогали на странице p. Резидент: зовёт pop_cdraw.c из банка 4 прямым
|
||||||
* вызовом, без трамплина. */
|
* вызовом, без трамплина. */
|
||||||
/* Прямоугольник слота против метки — БЕЗ передачи пяти аргументов.
|
|
||||||
*
|
|
||||||
* pop_cd_hit принимает (p, x0, y0, x1, y1): три последних идут стеком, и
|
|
||||||
* функция целиком уезжает в IX-фрейм — 45 % её тактов уходит на `-n(ix)`
|
|
||||||
* (замер asm 2026-08-19). А зовут её из cd_quiet дважды на слот, то есть
|
|
||||||
* до восьми раз за кадр. Здесь координаты берутся прямо из pop_cd, и
|
|
||||||
* аргументов остаётся два.
|
|
||||||
*
|
|
||||||
* Спрайт и накладной проверяются РАЗДЕЛЬНО — объединённый bbox ловит
|
|
||||||
* касания углом, которых нет (разбор в cd_quiet, pop_cdraw.c). */
|
|
||||||
static int hit_x, hit_y; /* аргументы hit_rect — file-scope, не стек */
|
|
||||||
static uint16_t hit_w, hit_h;
|
|
||||||
static uint8_t hit_p;
|
|
||||||
|
|
||||||
static uint8_t hit_rect(void)
|
|
||||||
{
|
|
||||||
int8_t c0, c1, c;
|
|
||||||
uint8_t ya, yb;
|
|
||||||
int x1 = hit_x + (int)hit_w - 1;
|
|
||||||
int y1 = hit_y + (int)hit_h - 1;
|
|
||||||
if (!hit_w || !hit_h) return 0;
|
|
||||||
if (x1 < 0 || hit_x > 319) return 0;
|
|
||||||
if (y1 < 0 || hit_y > 255) return 0;
|
|
||||||
ya = (uint8_t)(hit_y < 0 ? 0 : hit_y);
|
|
||||||
yb = (uint8_t)(y1 > 255 ? 255 : y1);
|
|
||||||
c0 = (int8_t)((hit_x < 0 ? 0 : hit_x) >> 5);
|
|
||||||
c1 = (int8_t)((x1 > 319 ? 319 : x1) >> 5);
|
|
||||||
for (c = c0; c <= c1; c++)
|
|
||||||
if (ya <= pop_cd_ymax[hit_p][c] && yb >= pop_cd_ymin[hit_p][c]) return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t pop_cd_hit_slot(uint8_t who, uint8_t p)
|
|
||||||
{
|
|
||||||
const pop_cdraw_t *s = &pop_cd[who];
|
|
||||||
if (!(pop_cd_dirty & (1 << p))) return 0;
|
|
||||||
hit_p = p;
|
|
||||||
hit_x = s->x[p]; hit_y = s->y[p] + POP_YOFF;
|
|
||||||
hit_w = s->w[p]; hit_h = s->h[p];
|
|
||||||
if (hit_rect()) return 1;
|
|
||||||
if (!s->ovalid[p]) return 0;
|
|
||||||
hit_x = s->ox[p]; hit_y = s->oy[p] + POP_YOFF;
|
|
||||||
hit_w = s->ow[p]; hit_h = s->oh[p];
|
|
||||||
return hit_rect();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t pop_cd_hit(uint8_t p, int x0, int y0, int x1, int y1)
|
|
||||||
{
|
|
||||||
int8_t c0, c1, c;
|
|
||||||
uint8_t ya, yb;
|
|
||||||
if (!(pop_cd_dirty & (1 << p))) return 0;
|
|
||||||
if (x1 < 0 || x0 > 319) return 0;
|
|
||||||
if (x0 < 0) x0 = 0;
|
|
||||||
if (x1 > 319) x1 = 319;
|
|
||||||
if (y1 < 0 || y0 > 255) return 0;
|
|
||||||
ya = (uint8_t)(y0 < 0 ? 0 : y0);
|
|
||||||
yb = (uint8_t)(y1 > 255 ? 255 : y1);
|
|
||||||
c0 = (int8_t)(x0 >> 5);
|
|
||||||
c1 = (int8_t)(x1 >> 5);
|
|
||||||
/* Пересечение отрезков [ya,yb] и [ymin,ymax] хотя бы в одной колонке.
|
|
||||||
* Пустая колонка держит ymin = 255, ymax = 0 — условие ниже её отсеет
|
|
||||||
* само, отдельной проверки «пусто» не нужно. */
|
|
||||||
for (c = c0; c <= c1; c++)
|
|
||||||
if (ya <= pop_cd_ymax[p][c] && yb >= pop_cd_ymin[p][c]) return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Страница приведена в порядок — снять с неё метку целиком. */
|
|
||||||
/* Начальное состояние обеих страниц — «ничего не трогали». ЯВНО, а не
|
|
||||||
* расчётом на обнуление _DATA: пустая колонка обозначается ymin = 255,
|
|
||||||
* ymax = 0, а нули от crt0 читались бы как «затронута строка 0». */
|
|
||||||
void pop_cd_init(void)
|
|
||||||
{
|
|
||||||
pop_cd_clear(0);
|
|
||||||
pop_cd_clear(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void pop_cd_clear(uint8_t p)
|
|
||||||
{
|
|
||||||
uint8_t c;
|
|
||||||
for (c = 0; c < 10; c++) { pop_cd_ymin[p][c] = 255; pop_cd_ymax[p][c] = 0; }
|
|
||||||
pop_cd_dirty = (uint8_t)(pop_cd_dirty & ~(1 << p));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- Блит куска атласа ---------------------------------------------- */
|
/* ---- Блит куска атласа ---------------------------------------------- */
|
||||||
/* Клипованный путь — ОТДЕЛЬНОЙ функцией. У него девять 16-битных локалей,
|
/* Клипованный путь — ОТДЕЛЬНОЙ функцией. У него девять 16-битных локалей,
|
||||||
@@ -399,27 +317,6 @@ void pop_cd_clear(uint8_t p)
|
|||||||
* перерисовка тайлов — идёт с pop_t_fclip_on == 0. */
|
* перерисовка тайлов — идёт с pop_t_fclip_on == 0. */
|
||||||
#define FLIP_TOP(top, h) POP_FLIP_TOP(top, h) /* формула — в pop_bg.h */
|
#define FLIP_TOP(top, h) POP_FLIP_TOP(top, h) /* формула — в pop_bg.h */
|
||||||
|
|
||||||
/* Окно под точечную перерисовку/запечку — контракт в шапке pop_tile.h.
|
|
||||||
* Логическая пара нужна для отсева тайлов (он считается из ряда комнаты),
|
|
||||||
* экранная — для клипа самого блита; при перевороте они разные. */
|
|
||||||
void pop_t_win_set(int x, int ytop, int w, int h)
|
|
||||||
{
|
|
||||||
pop_t_fclip_x0 = x;
|
|
||||||
pop_t_fclip_x1 = x + w;
|
|
||||||
pop_t_fclip_ly0 = ytop;
|
|
||||||
pop_t_fclip_ly1 = ytop + h;
|
|
||||||
if (pop_upside) {
|
|
||||||
int t = FLIP_TOP(ytop, h);
|
|
||||||
pop_t_fclip_y0 = t;
|
|
||||||
pop_t_fclip_y1 = t + h;
|
|
||||||
} else {
|
|
||||||
pop_t_fclip_y0 = ytop;
|
|
||||||
pop_t_fclip_y1 = ytop + h;
|
|
||||||
}
|
|
||||||
pop_t_fclip_on = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void pop_t_win_clear(void) { pop_t_fclip_on = 0; }
|
|
||||||
|
|
||||||
/* Клипованный путь блита. Габарит и смещения внутри куска — БАЙТЫ, а вся
|
/* Клипованный путь блита. Габарит и смещения внутри куска — БАЙТЫ, а вся
|
||||||
* рабочая переменная — file-scope, НЕ локали.
|
* рабочая переменная — file-scope, НЕ локали.
|
||||||
@@ -447,7 +344,7 @@ static int bc_dx, bc_dy;
|
|||||||
static int16_t bc_d;
|
static int16_t bc_d;
|
||||||
static uint8_t bc_w, bc_h, bc_dw, bc_dh, bc_sx, bc_sy;
|
static uint8_t bc_w, bc_h, bc_dw, bc_dh, bc_sx, bc_sy;
|
||||||
|
|
||||||
static void blit_b_clip(const uint8_t *img, int x, int top, uint8_t w, uint8_t h)
|
void blit_b_clip(const uint8_t *img, int x, int top, uint8_t w, uint8_t h)
|
||||||
{
|
{
|
||||||
bc_img = img; bc_w = w; bc_h = h;
|
bc_img = img; bc_w = w; bc_h = h;
|
||||||
bc_dx = x; bc_dy = top;
|
bc_dx = x; bc_dy = top;
|
||||||
@@ -653,59 +550,12 @@ void pop_blit_b(atlas_t *a, uint8_t idx, int x, int ybottom)
|
|||||||
gfx_w0_unmap();
|
gfx_w0_unmap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Блит getimage-блоба из ОБЫЧНОЙ памяти (W2), а не из EMM-страницы атласа:
|
|
||||||
* те же клип полосы у потолка и окно перерисовки, что у pop_blit_b, но без
|
|
||||||
* atlas_image и без gfx_w0_map/unmap (это ещё ~1 350 такта). Заведено под
|
|
||||||
* композитный спрайт падающего куска (pop_room.c: три части сложены в один
|
|
||||||
* блоб, один вызов блита вместо трёх). */
|
|
||||||
void pop_mem_b(const uint8_t *img, int x, int ybottom)
|
|
||||||
{
|
|
||||||
uint8_t w = img[0], h = img[2];
|
|
||||||
int top;
|
|
||||||
if (pop_t_fclip_on &&
|
|
||||||
(x >= pop_t_fclip_x1 || x + 64 <= pop_t_fclip_x0 ||
|
|
||||||
ybottom + POP_YOFF + 1 <= pop_t_fclip_ly0 ||
|
|
||||||
ybottom + POP_YOFF - 64 >= pop_t_fclip_ly1))
|
|
||||||
return;
|
|
||||||
if (!w || !h) return;
|
|
||||||
top = ybottom - (int)h + 1 + POP_YOFF;
|
|
||||||
if (pop_upside) top = FLIP_TOP(top, h);
|
|
||||||
if (!pop_t_fclip_on && !pop_t_clip_top &&
|
|
||||||
!(pop_upside && (top < POP_YOFF || top + (int)h > POP_CLIP_BOTTOM)) &&
|
|
||||||
x >= 0 && top >= 0 && x + (int)w <= 320 && top + (int)h <= 256) {
|
|
||||||
if (pop_upside) gfx_blit_noclip_vflip(x, top, img);
|
|
||||||
else gfx_blit_noclip(x, top, img);
|
|
||||||
pop_cd_touch(x, top, (int)w, (int)h);
|
|
||||||
} else {
|
|
||||||
blit_b_clip(img, x, top, w, h);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void pop_env_b(uint8_t id, int x, int yb) { pop_blit_b(&pop_env[id >> POP_ENV_SHIFT], (uint8_t)(id & POP_ENV_MASK), x, yb); }
|
void pop_env_b(uint8_t id, int x, int yb) { pop_blit_b(&pop_env[id >> POP_ENV_SHIFT], (uint8_t)(id & POP_ENV_MASK), x, yb); }
|
||||||
void pop_wall_b(uint8_t id, int x, int yb) { pop_blit_b(&pop_wall_at, id, x, yb); }
|
void pop_wall_b(uint8_t id, int x, int yb) { pop_blit_b(&pop_wall_at, id, x, yb); }
|
||||||
void pop_fore_b(uint8_t id, int x, int yb) { pop_blit_b(&pop_fore_at, id, x, yb); }
|
void pop_fore_b(uint8_t id, int x, int yb) { pop_blit_b(&pop_fore_at, id, x, yb); }
|
||||||
void pop_pot_b(uint8_t id, int x, int yb) { pop_blit_b(&pop_pot_at, id, x, yb); }
|
void pop_pot_b(uint8_t id, int x, int yb) { pop_blit_b(&pop_pot_at, id, x, yb); }
|
||||||
|
|
||||||
/* Heal по КОМНАТНОЙ координате (её и знают точечные перерисовки: чомпер,
|
|
||||||
* пики, loose, ворота, полоса потолка). При перевороте прямоугольник
|
|
||||||
* обязан отразиться ровно так же, как сам тайл, — иначе heal чистит одно
|
|
||||||
* место, а рисует в другое: именно так «ломалась анимация чомперов»
|
|
||||||
* (найдено пользователем на первом же прогоне зеркального фона). */
|
|
||||||
void pop_bar_black(int x, int top, int w, int h)
|
|
||||||
{
|
|
||||||
if (pop_upside) top = FLIP_TOP(top, h);
|
|
||||||
gfx_set_bank(GFX_BANK_NORMAL);
|
|
||||||
setfillstyle(SOLID_FILL, BLACK);
|
|
||||||
bar(x, top, x + w - 1, top + h - 1);
|
|
||||||
pop_cd_touch(x, top, w, h); /* bar идёт мимо pop_blit_b (DRAW-COST) */
|
|
||||||
}
|
|
||||||
|
|
||||||
void pop_heal_off(int x, int y, int w, int h)
|
|
||||||
{
|
|
||||||
int top = y + POP_YOFF;
|
|
||||||
if (pop_upside) top = FLIP_TOP(top, h);
|
|
||||||
pop_heal_fast(x, top, w, h);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- Чтение тайла отрисованной комнаты ------------------------------ */
|
/* ---- Чтение тайла отрисованной комнаты ------------------------------ */
|
||||||
uint8_t pop_tile_code(int row, int col)
|
uint8_t pop_tile_code(int row, int col)
|
||||||
@@ -757,25 +607,6 @@ uint8_t pop_wall_modifier(int row, int col)
|
|||||||
return 0; /* SWS */
|
return 0; /* SWS */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- Зелье: склянка (tiles_10_potion, seg008 draw_tile_fore) --------- *
|
|
||||||
* Тип = (modif & 0xF8) >> 3: 0 = пусто (ничего не рисуем), 2..4 — «большая»
|
|
||||||
* склянка (id 13), остальные — маленькая (id 12). Спрайты из chtab_1 (атлас
|
|
||||||
* pop_pot), поэтому НЕ из env: раньше рисовали chtab_6 id 12 (res212),
|
|
||||||
* которого в VDUNGEON нет — оттуда и «мусорный объект». Позиция оригинала:
|
|
||||||
* xh = fore_x + draw_xh, xl = 6, ybottom = fore_y + draw_main_y.
|
|
||||||
*
|
|
||||||
* Здесь, а не в банке: склянку рисует и полная отрисовка тайла (холодная
|
|
||||||
* половина), и fore-проход поверх персонажа (горячая). */
|
|
||||||
void pop_potion_flask(int row, int col, uint8_t modif)
|
|
||||||
{
|
|
||||||
uint8_t type = (uint8_t)((modif & 0xF8) >> 3);
|
|
||||||
uint8_t id = 12;
|
|
||||||
if (type == 0) return;
|
|
||||||
if (type >= 2 && type < 5) id = 13; /* большая склянка */
|
|
||||||
if (pop_palace) id += 2; /* seg008:747: во дворце склянки другие */
|
|
||||||
pop_pot_b(id, (POT_FORE_X + POP_COL_XH[col]) * 8 + 6,
|
|
||||||
POT_FORE_Y + 63 * row + 62);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Окно fore-клипа: ставит тот, кто нарисовал персонажа (pop_cdraw, банк 4),
|
/* Окно fore-клипа: ставит тот, кто нарисовал персонажа (pop_cdraw, банк 4),
|
||||||
* читает blit_b. Резидент — чтобы вызов из банка шёл без трамплина. */
|
* читает blit_b. Резидент — чтобы вызов из банка шёл без трамплина. */
|
||||||
@@ -791,13 +622,3 @@ void pop_fore_set_clip(int x, int y, int w, int h)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ряд 0 комнаты СНИЗУ (fg-коды) — нужен draw_tile_topright/floorright, когда
|
|
||||||
* тайл нижнего ряда стал видимым (empty после падения loose): «стена вниз» =
|
|
||||||
* топрайт тайла room_B(0, col-1). 0 — нет комнаты снизу (кромка = стена). */
|
|
||||||
void pop_room_set_below(const uint8_t *below_row0_fg) { pop_t_below = below_row0_fg; }
|
|
||||||
|
|
||||||
/* Ряд 2 (fg-коды + модификаторы) комнаты СВЕРХУ — для полосы кладки у
|
|
||||||
* потолка (seg008 draw_room: доп. верхний ряд из room_A, draw_tile_aboveroom
|
|
||||||
* при draw_main_y=-1). 0 — нет комнаты сверху (кромка уровня → пол). */
|
|
||||||
void pop_room_set_above(const uint8_t *above_row2_fg, const uint8_t *above_row2_mod)
|
|
||||||
{ pop_t_above = above_row2_fg; pop_t_abovemod = above_row2_mod; }
|
|
||||||
|
|||||||
@@ -93,8 +93,8 @@ extern uint8_t pop_t_fclip_on;
|
|||||||
* ВАЖНО: пока окно стоит, pop_blit_b НЕ ставит пометку «фон трогали» (признак
|
* ВАЖНО: пока окно стоит, pop_blit_b НЕ ставит пометку «фон трогали» (признак
|
||||||
* fore-прохода) — вызывающий обязан пометить прямоугольник САМ (pop_cd_touch
|
* fore-прохода) — вызывающий обязан пометить прямоугольник САМ (pop_cd_touch
|
||||||
* или pop_bar_black, который метит сам). */
|
* или pop_bar_black, который метит сам). */
|
||||||
void pop_t_win_set(int x, int ytop, int w, int h);
|
void pop_t_win_set(int x, int ytop, int w, int h) __banked;
|
||||||
void pop_t_win_clear(void);
|
void pop_t_win_clear(void) __banked;
|
||||||
|
|
||||||
/* ---- Таблица тайлов (piece, seg008.c:27) ---------------------------- */
|
/* ---- Таблица тайлов (piece, seg008.c:27) ---------------------------- */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -154,7 +154,7 @@ void pop_blit_b(atlas_t *a, uint8_t idx, int x, int ybottom);
|
|||||||
/* Блит getimage-блоба из обычной памяти (W2) — без atlas_image и без
|
/* Блит getimage-блоба из обычной памяти (W2) — без atlas_image и без
|
||||||
* маппинга страницы; клип полосы у потолка и окно перерисовки те же, что у
|
* маппинга страницы; клип полосы у потолка и окно перерисовки те же, что у
|
||||||
* pop_blit_b. Для композитных спрайтов, собранных в рантайме. */
|
* pop_blit_b. Для композитных спрайтов, собранных в рантайме. */
|
||||||
void pop_mem_b(const uint8_t *img, int x, int ybottom);
|
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_env_b(uint8_t id, int x, int yb);
|
||||||
void pop_wall_b(uint8_t id, int x, int yb);
|
void pop_wall_b(uint8_t id, int x, int yb);
|
||||||
@@ -162,14 +162,14 @@ void pop_fore_b(uint8_t id, int x, int yb);
|
|||||||
void pop_pot_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. */
|
/* heal в room-space (со сдвигом POP_YOFF) — все heal слоя фона/loose. */
|
||||||
void pop_heal_off(int x, int y, int w, int h);
|
void pop_heal_off(int x, int y, int w, int h) __banked;
|
||||||
|
|
||||||
/* Чёрная плита по ЭКРАННЫМ координатам, с учётом переворота (POP_FLIP_TOP).
|
/* Чёрная плита по ЭКРАННЫМ координатам, с учётом переворота (POP_FLIP_TOP).
|
||||||
* Пишет банком 0x50 — и в видео-ОЗУ, и в ОЗУ-копию, иначе heal вернёт
|
* Пишет банком 0x50 — и в видео-ОЗУ, и в ОЗУ-копию, иначе heal вернёт
|
||||||
* стёртое. ВСЕ стирания слоя фона обязаны идти через неё, а не через голый
|
* стёртое. ВСЕ стирания слоя фона обязаны идти через неё, а не через голый
|
||||||
* bar: иначе при перевороте плита ложится в неотражённое место (так
|
* bar: иначе при перевороте плита ложится в неотражённое место (так
|
||||||
* ломались решётка и плита-потолок). */
|
* ломались решётка и плита-потолок). */
|
||||||
void pop_bar_black(int x, int top, int w, int h);
|
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_code(int row, int col);
|
||||||
@@ -183,6 +183,6 @@ uint8_t pop_wall_modifier(int row, int col);
|
|||||||
|
|
||||||
/* Склянка зелья (draw_tile_fore, chtab_1): нужна и полной отрисовке тайла, и
|
/* Склянка зелья (draw_tile_fore, chtab_1): нужна и полной отрисовке тайла, и
|
||||||
* fore-проходу поверх персонажа. */
|
* fore-проходу поверх персонажа. */
|
||||||
void pop_potion_flask(int row, int col, uint8_t modif);
|
void pop_potion_flask(int row, int col, uint8_t modif) __banked;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,240 @@
|
|||||||
|
/*
|
||||||
|
* pop_tile_cold.c — ХОЛОДНАЯ половина общих листьев слоя фона (банк 5).
|
||||||
|
*
|
||||||
|
* ЗАЧЕМ РАЗРЕЗ. pop_tile.c был самым крупным жильцом резидента (5 972 Б
|
||||||
|
* кода), а резидент huge-режима — это всё окно 0x4100..0xBB00, и место в нём
|
||||||
|
* нужно под музыку, заставки и UI. Но целиком pop_tile не уедет: его
|
||||||
|
* const-таблицы (POP_TILE_DIV/MOD, pop_tile_table, таблицы кадров) читают
|
||||||
|
* банки 2, 3, 7 и 8, а таблица в чужом банке не видна.
|
||||||
|
*
|
||||||
|
* КРИТЕРИЙ ОТБОРА — ЗАМЕР, а не догадка. Каждая функция pop_tile была
|
||||||
|
* посчитана брейкпоинтом-счётчиком в MAME: сколько вызовов в кадре покоя и
|
||||||
|
* сколько в кадре ПОЛНОЙ перерисовки комнаты (её форсирует чит +/-). Сюда
|
||||||
|
* переехало только то, у чего пик <= 3 вызовов на кадр — и проверено это
|
||||||
|
* на ДВУХ тайлсетах: подземелье (уровень 1) и дворец (уровень 4), потому
|
||||||
|
* что pop_mem_b рисует композитный кусок и мог оказаться дворцовым.
|
||||||
|
* Замер: pop_mem_b 0, pop_cd_hit 0, pop_t_win_set 0..1, pop_heal_off 0..1,
|
||||||
|
* pop_room_set_above 1, pop_bar_black 3, pop_potion_flask 0..1,
|
||||||
|
* pop_cd_hit_slot 2..3 — при канарейке pop_blit_b 184..214 в том же кадре.
|
||||||
|
*
|
||||||
|
* ЦЕНА. Трамплин замерен: 252 такта пролог + 84 эпилог + ~50 на стороне
|
||||||
|
* вызывающего = ~410 тактов на вызов. На кадр покоя выходит ~1 000 тактов
|
||||||
|
* (0,2 % работы кадра), на кадр редрава ~3 700 (0,009 растра).
|
||||||
|
*
|
||||||
|
* ЧТО ОСТАЛОСЬ В РЕЗИДЕНТЕ и почему: pop_blit_b (184 вызова на редрав) со
|
||||||
|
* своими статиками blit_b_clip/blit_b_oversize, pop_cd_touch (198),
|
||||||
|
* pop_tile_code (296), pop_wall_modifier (101), pop_env_b (73),
|
||||||
|
* pop_tile_mod (70), pop_cd_batch_end (40) — и все const-таблицы.
|
||||||
|
* pop_fore_set_clip оставлен намеренно: 88 Б не стоят отказа от прямого
|
||||||
|
* вызова из банка 4, ради которого он и заводился.
|
||||||
|
*
|
||||||
|
* ВЫЗОВЫ ОТСЮДА В РЕЗИДЕНТ — прямые, без трамплина: W1/W2 замаплены всегда.
|
||||||
|
* Поэтому blit_b_clip (статик горячей половины) просто перестал быть static
|
||||||
|
* и объявлен в _pop_tile.h, а не переехал следом.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <graphics.h>
|
||||||
|
#include <gfx.h>
|
||||||
|
#include <sprite.h>
|
||||||
|
#include "pop_bg.h" /* POP_YOFF, POP_PLAYFIELD_H */
|
||||||
|
#include "pop_map.h" /* pop_upside — переворот (зелье инверсии) */
|
||||||
|
#include "pop_cdraw.h" /* pop_cd, pop_cd_dirty, pop_cd_ymin/ymax */
|
||||||
|
#include "pop_tile.h"
|
||||||
|
#include "_pop_draw.h" /* pop_heal_fast */
|
||||||
|
#include "_pop_tile.h" /* blit_b_clip — из горячей половины */
|
||||||
|
|
||||||
|
#define FLIP_TOP(top, h) POP_FLIP_TOP(top, h) /* формула — в pop_bg.h */
|
||||||
|
|
||||||
|
/* Смещение склянки внутри тайла (seg008 draw_tile_fore): те же числа, что в
|
||||||
|
* tile_table горячей половины — дублируем, потому что там они локальные
|
||||||
|
* define'ы, а тащить их в публичный заголовок ради одной функции незачем. */
|
||||||
|
#define POT_FORE_X 2
|
||||||
|
#define POT_FORE_Y (-3)
|
||||||
|
|
||||||
|
/* Прямоугольник слота против метки — БЕЗ передачи пяти аргументов.
|
||||||
|
*
|
||||||
|
* pop_cd_hit принимает (p, x0, y0, x1, y1): три последних идут стеком, и
|
||||||
|
* функция целиком уезжает в IX-фрейм — 45 % её тактов уходит на `-n(ix)`
|
||||||
|
* (замер asm 2026-08-19). А зовут её из cd_quiet дважды на слот, то есть
|
||||||
|
* до восьми раз за кадр. Здесь координаты берутся прямо из pop_cd, и
|
||||||
|
* аргументов остаётся два.
|
||||||
|
*
|
||||||
|
* Спрайт и накладной проверяются РАЗДЕЛЬНО — объединённый bbox ловит
|
||||||
|
* касания углом, которых нет (разбор в cd_quiet, pop_cdraw.c). */
|
||||||
|
static int hit_x, hit_y; /* аргументы hit_rect — file-scope, не стек */
|
||||||
|
static uint16_t hit_w, hit_h;
|
||||||
|
static uint8_t hit_p;
|
||||||
|
|
||||||
|
static uint8_t hit_rect(void)
|
||||||
|
{
|
||||||
|
int8_t c0, c1, c;
|
||||||
|
uint8_t ya, yb;
|
||||||
|
int x1 = hit_x + (int)hit_w - 1;
|
||||||
|
int y1 = hit_y + (int)hit_h - 1;
|
||||||
|
if (!hit_w || !hit_h) return 0;
|
||||||
|
if (x1 < 0 || hit_x > 319) return 0;
|
||||||
|
if (y1 < 0 || hit_y > 255) return 0;
|
||||||
|
ya = (uint8_t)(hit_y < 0 ? 0 : hit_y);
|
||||||
|
yb = (uint8_t)(y1 > 255 ? 255 : y1);
|
||||||
|
c0 = (int8_t)((hit_x < 0 ? 0 : hit_x) >> 5);
|
||||||
|
c1 = (int8_t)((x1 > 319 ? 319 : x1) >> 5);
|
||||||
|
for (c = c0; c <= c1; c++)
|
||||||
|
if (ya <= pop_cd_ymax[hit_p][c] && yb >= pop_cd_ymin[hit_p][c]) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t pop_cd_hit_slot(uint8_t who, uint8_t p) __banked
|
||||||
|
{
|
||||||
|
const pop_cdraw_t *s = &pop_cd[who];
|
||||||
|
if (!(pop_cd_dirty & (1 << p))) return 0;
|
||||||
|
hit_p = p;
|
||||||
|
hit_x = s->x[p]; hit_y = s->y[p] + POP_YOFF;
|
||||||
|
hit_w = s->w[p]; hit_h = s->h[p];
|
||||||
|
if (hit_rect()) return 1;
|
||||||
|
if (!s->ovalid[p]) return 0;
|
||||||
|
hit_x = s->ox[p]; hit_y = s->oy[p] + POP_YOFF;
|
||||||
|
hit_w = s->ow[p]; hit_h = s->oh[p];
|
||||||
|
return hit_rect();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t pop_cd_hit(uint8_t p, int x0, int y0, int x1, int y1) __banked
|
||||||
|
{
|
||||||
|
int8_t c0, c1, c;
|
||||||
|
uint8_t ya, yb;
|
||||||
|
if (!(pop_cd_dirty & (1 << p))) return 0;
|
||||||
|
if (x1 < 0 || x0 > 319) return 0;
|
||||||
|
if (x0 < 0) x0 = 0;
|
||||||
|
if (x1 > 319) x1 = 319;
|
||||||
|
if (y1 < 0 || y0 > 255) return 0;
|
||||||
|
ya = (uint8_t)(y0 < 0 ? 0 : y0);
|
||||||
|
yb = (uint8_t)(y1 > 255 ? 255 : y1);
|
||||||
|
c0 = (int8_t)(x0 >> 5);
|
||||||
|
c1 = (int8_t)(x1 >> 5);
|
||||||
|
/* Пересечение отрезков [ya,yb] и [ymin,ymax] хотя бы в одной колонке.
|
||||||
|
* Пустая колонка держит ymin = 255, ymax = 0 — условие ниже её отсеет
|
||||||
|
* само, отдельной проверки «пусто» не нужно. */
|
||||||
|
for (c = c0; c <= c1; c++)
|
||||||
|
if (ya <= pop_cd_ymax[p][c] && yb >= pop_cd_ymin[p][c]) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Страница приведена в порядок — снять с неё метку целиком. */
|
||||||
|
/* Начальное состояние обеих страниц — «ничего не трогали». ЯВНО, а не
|
||||||
|
* расчётом на обнуление _DATA: пустая колонка обозначается ymin = 255,
|
||||||
|
* ymax = 0, а нули от crt0 читались бы как «затронута строка 0». */
|
||||||
|
void pop_cd_init(void) __banked
|
||||||
|
{
|
||||||
|
pop_cd_clear(0);
|
||||||
|
pop_cd_clear(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pop_cd_clear(uint8_t p) __banked
|
||||||
|
{
|
||||||
|
uint8_t c;
|
||||||
|
for (c = 0; c < 10; c++) { pop_cd_ymin[p][c] = 255; pop_cd_ymax[p][c] = 0; }
|
||||||
|
pop_cd_dirty = (uint8_t)(pop_cd_dirty & ~(1 << p));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Окно под точечную перерисовку/запечку — контракт в шапке pop_tile.h.
|
||||||
|
* Логическая пара нужна для отсева тайлов (он считается из ряда комнаты),
|
||||||
|
* экранная — для клипа самого блита; при перевороте они разные. */
|
||||||
|
void pop_t_win_set(int x, int ytop, int w, int h) __banked
|
||||||
|
{
|
||||||
|
pop_t_fclip_x0 = x;
|
||||||
|
pop_t_fclip_x1 = x + w;
|
||||||
|
pop_t_fclip_ly0 = ytop;
|
||||||
|
pop_t_fclip_ly1 = ytop + h;
|
||||||
|
if (pop_upside) {
|
||||||
|
int t = FLIP_TOP(ytop, h);
|
||||||
|
pop_t_fclip_y0 = t;
|
||||||
|
pop_t_fclip_y1 = t + h;
|
||||||
|
} else {
|
||||||
|
pop_t_fclip_y0 = ytop;
|
||||||
|
pop_t_fclip_y1 = ytop + h;
|
||||||
|
}
|
||||||
|
pop_t_fclip_on = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pop_t_win_clear(void) __banked
|
||||||
|
{ pop_t_fclip_on = 0; }
|
||||||
|
|
||||||
|
/* Блит getimage-блоба из ОБЫЧНОЙ памяти (W2), а не из EMM-страницы атласа:
|
||||||
|
* те же клип полосы у потолка и окно перерисовки, что у pop_blit_b, но без
|
||||||
|
* atlas_image и без gfx_w0_map/unmap (это ещё ~1 350 такта). Заведено под
|
||||||
|
* композитный спрайт падающего куска (pop_room.c: три части сложены в один
|
||||||
|
* блоб, один вызов блита вместо трёх). */
|
||||||
|
void pop_mem_b(const uint8_t *img, int x, int ybottom) __banked
|
||||||
|
{
|
||||||
|
uint8_t w = img[0], h = img[2];
|
||||||
|
int top;
|
||||||
|
if (pop_t_fclip_on &&
|
||||||
|
(x >= pop_t_fclip_x1 || x + 64 <= pop_t_fclip_x0 ||
|
||||||
|
ybottom + POP_YOFF + 1 <= pop_t_fclip_ly0 ||
|
||||||
|
ybottom + POP_YOFF - 64 >= pop_t_fclip_ly1))
|
||||||
|
return;
|
||||||
|
if (!w || !h) return;
|
||||||
|
top = ybottom - (int)h + 1 + POP_YOFF;
|
||||||
|
if (pop_upside) top = FLIP_TOP(top, h);
|
||||||
|
if (!pop_t_fclip_on && !pop_t_clip_top &&
|
||||||
|
!(pop_upside && (top < POP_YOFF || top + (int)h > POP_CLIP_BOTTOM)) &&
|
||||||
|
x >= 0 && top >= 0 && x + (int)w <= 320 && top + (int)h <= 256) {
|
||||||
|
if (pop_upside) gfx_blit_noclip_vflip(x, top, img);
|
||||||
|
else gfx_blit_noclip(x, top, img);
|
||||||
|
pop_cd_touch(x, top, (int)w, (int)h);
|
||||||
|
} else {
|
||||||
|
blit_b_clip(img, x, top, w, h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Heal по КОМНАТНОЙ координате (её и знают точечные перерисовки: чомпер,
|
||||||
|
* пики, loose, ворота, полоса потолка). При перевороте прямоугольник
|
||||||
|
* обязан отразиться ровно так же, как сам тайл, — иначе heal чистит одно
|
||||||
|
* место, а рисует в другое: именно так «ломалась анимация чомперов»
|
||||||
|
* (найдено пользователем на первом же прогоне зеркального фона). */
|
||||||
|
void pop_bar_black(int x, int top, int w, int h) __banked
|
||||||
|
{
|
||||||
|
if (pop_upside) top = FLIP_TOP(top, h);
|
||||||
|
gfx_set_bank(GFX_BANK_NORMAL);
|
||||||
|
setfillstyle(SOLID_FILL, BLACK);
|
||||||
|
bar(x, top, x + w - 1, top + h - 1);
|
||||||
|
pop_cd_touch(x, top, w, h); /* bar идёт мимо pop_blit_b (DRAW-COST) */
|
||||||
|
}
|
||||||
|
|
||||||
|
void pop_heal_off(int x, int y, int w, int h) __banked
|
||||||
|
{
|
||||||
|
int top = y + POP_YOFF;
|
||||||
|
if (pop_upside) top = FLIP_TOP(top, h);
|
||||||
|
pop_heal_fast(x, top, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Зелье: склянка (tiles_10_potion, seg008 draw_tile_fore) --------- *
|
||||||
|
* Тип = (modif & 0xF8) >> 3: 0 = пусто (ничего не рисуем), 2..4 — «большая»
|
||||||
|
* склянка (id 13), остальные — маленькая (id 12). Спрайты из chtab_1 (атлас
|
||||||
|
* pop_pot), поэтому НЕ из env: раньше рисовали chtab_6 id 12 (res212),
|
||||||
|
* которого в VDUNGEON нет — оттуда и «мусорный объект». Позиция оригинала:
|
||||||
|
* xh = fore_x + draw_xh, xl = 6, ybottom = fore_y + draw_main_y.
|
||||||
|
*
|
||||||
|
* Здесь, а не в банке: склянку рисует и полная отрисовка тайла (холодная
|
||||||
|
* половина), и fore-проход поверх персонажа (горячая). */
|
||||||
|
void pop_potion_flask(int row, int col, uint8_t modif) __banked
|
||||||
|
{
|
||||||
|
uint8_t type = (uint8_t)((modif & 0xF8) >> 3);
|
||||||
|
uint8_t id = 12;
|
||||||
|
if (type == 0) return;
|
||||||
|
if (type >= 2 && type < 5) id = 13; /* большая склянка */
|
||||||
|
if (pop_palace) id += 2; /* seg008:747: во дворце склянки другие */
|
||||||
|
pop_pot_b(id, (POT_FORE_X + POP_COL_XH[col]) * 8 + 6,
|
||||||
|
POT_FORE_Y + 63 * row + 62);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ряд 0 комнаты СНИЗУ (fg-коды) — нужен draw_tile_topright/floorright, когда
|
||||||
|
* тайл нижнего ряда стал видимым (empty после падения loose): «стена вниз» =
|
||||||
|
* топрайт тайла room_B(0, col-1). 0 — нет комнаты снизу (кромка = стена). */
|
||||||
|
void pop_room_set_below(const uint8_t *below_row0_fg) __banked
|
||||||
|
{ pop_t_below = below_row0_fg; }
|
||||||
|
|
||||||
|
/* Ряд 2 (fg-коды + модификаторы) комнаты СВЕРХУ — для полосы кладки у
|
||||||
|
* потолка (seg008 draw_room: доп. верхний ряд из room_A, draw_tile_aboveroom
|
||||||
|
* при draw_main_y=-1). 0 — нет комнаты сверху (кромка уровня → пол). */
|
||||||
|
void pop_room_set_above(const uint8_t *above_row2_fg, const uint8_t *above_row2_mod) __banked
|
||||||
|
{ pop_t_above = above_row2_fg; pop_t_abovemod = above_row2_mod; }
|
||||||
Reference in New Issue
Block a user