Files
Sprinter-SDCC/applications/PoP/roomtest/pop_state.h
T
snark13 b27b31318a Разбор draw_tile: модель цены блита + пакетная пометка pop_cd_touch
Замер (MAME, комната 23 ур.13) разложил цену одного gfx_blit_noclip
регрессией по 339 блитам, высоты 3..63:

    такты = 8791 + 198.2 * h + 5.96 * (w * h)

Модель ложится на весь диапазон (32x3 -> 9 891, 32x13 -> 13 887,
32x60 -> 32 073), разброс внутри размерной группы — ТРИ такта.

Выводы:
- 5.96 на байт — предел железа: байт идёт через акселератор дважды
  (burst src->память акселератора, burst ->экран), по 3 такта на проход
  при системном клоке 21 МГц.  Ускорять передачу нечем.
- 198 на строку — цикл _bgi_blit_rows_raw; при w=32 это половина
  построчной цены.
- 8791 на ВЫЗОВ — крупнейшая статья, НЕ объяснена.  Проверено, что это не
  W3-скобка (в обеих половинах по 5 инструкций) и не прерывания (разброс
  3 такта).  Один тайл = 5-6 спрайтов ~ 107 000 тактов, из них ~50 000 —
  постоянные накладные вызовов.  Это и есть главный резерв.

Заодно: pop_cd_touch собирается в ПАКЕТ на тайл (pop_cd_batch_begin/end,
скобка в draw_tile) вместо вызова на каждый кусок — раньше 2 866 тактов
на спрайт, 15% цены блита.  ВНИМАНИЕ: выигрыш замером НЕ подтверждён —
в захваченных кадрах скобка не сработала (блиты шли из холодной отрисовки
комнаты, мимо draw_tile).  Правка безопасна по построению: объединение
прямоугольников может пометку только расширить, не сузить.

Снятые сегодня неверные утверждения (чтобы не всплыли):
- «клипованный блит дороже полного» — артефакт сравнения разных выборок
  спрайтов; 32x60 с клипом до полосы стоит 7 236, то есть клип работает;
- «блиты идут программным циклом со скоростью ldir» — нет, ядро на
  акселераторе, см. модель выше;
- «отложить запекание на кадр» — НЕЛЬЗЯ: запекание пишет ОЗУ-копию фона,
  из которой heal восстанавливает; отложенное даёт призрак плиты.

Все 8 наборов tests-host проходят.  Оснастка замера временная.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 23:36:48 +03:00

99 lines
5.7 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_state.h — состояние, общее для банков и W1/W2. См. pop_state.c.
*
* Правило: если переменную читают/пишут по обе стороны банковой границы —
* её место здесь, а НЕ в банковом модуле (там она уедет в страницу банка).
*/
#ifndef POP_STATE_H
#define POP_STATE_H
#include <stdint.h>
/* Кусок loose приземлился (loose_land, seg007:11E8): 0 = нет, иначе
* tilepos+1 тайла, на который он лёг. Ставит отрисовка mob (банк),
* разбирает логика loose-полов (pop_map, W1/W2). */
extern uint8_t pop_loose_landed;
/* Кусок loose ушёл ниже поля своей комнаты: 0 = нет, иначе col+1; комната
* отправления — pop_loose_exit_room. Ставит отрисовка mob (банк 7),
* разбирает главный цикл (посадка в комнате снизу). */
extern uint8_t pop_loose_exit;
extern uint8_t pop_loose_exit_room;
/* Тайл СОСЕДНЕЙ (не отрисованной) комнаты изменился — перечитать кромки.
* Подробности — в pop_state.c. */
extern uint8_t pop_neigh_dirty;
/* Бесшовный переход между уровнями (seamless): подробности в pop_state.c.
* Ставится главным циклом при попадании в комнату-выход, гасится стартом
* уровня — он же единственный, кто на флаг смотрит. */
extern uint8_t pop_seamless;
/* Держатель бесшовного перехода после ЧИТ-телепорта (см. pop_state.c).
* Ставит pop_dbg_roomnav, снимает первая смена комнаты обычным ходом. */
extern uint8_t pop_nav_hold;
/* Чит «бессмертие» (I): Кида бьют, но HP не убавляется. Читают пути урона
* (боёвка в банке, пики в pop_map), переключает главный цикл. */
extern uint8_t pop_immortal;
/* Габариты ПОСЛЕДНЕЙ нарисованной двери уровня — порт глобалов
* leveldoor_right / leveldoor_ybottom (seg008 draw_leveldoor). Их ставит
* отрисовка (банк pop_bg), а читает clip_char (pop_map, W1/W2): кадры
* подъёма по лестнице (224..228) обрезаются по правому косяку проёма и по
* низу поднятой створки. Дверь рисуется ПОД персонажем, поэтому перекрыть
* его не может — оригинал именно режет спрайт.
* right — экранный X правой кромки; ybottom — room-space Y (без POP_YOFF).
* 0 = двери в отрисованной комнате нет. */
extern int pop_leveldoor_right;
extern int pop_leveldoor_ybottom;
/* droppedout (seg006:1140): Кида СПИХНУЛИ с уступа в бою — он падает из
* боевой стойки. Ставит start_fall (pop_map, банк 3), читает ИИ стража
* (guards.c, банк 1): по этому флагу страж решает, идти ли за упавшим вниз
* (guard_follows_kid_down, seg002:09F8). Отсюда и место в общем модуле —
* банки друг другу данные не отдают. */
extern uint8_t pop_droppedout;
/* leveldoor_open (seg007:456): 1 — дверь уровня открыта полностью. Пишет
* анимация двери (банк 6), читает подъём скелета (банк 1). Word, как в
* оригинале: на уровне 8 в него же СЧИТАЕТСЯ задержка до мыши (см.
* pop_state.c и pop_check_mouse в guards.c). */
extern uint16_t pop_leveldoor_open;
/* Отладочная «пустышка» для брейкпоинтов из банков — см. pop_state.c.
* Звать из отлаживаемого места под нужным `if`, брейкпоинт ставить на
* _pop_dbg_trap (резидент W1, адрес однозначен). */
extern uint8_t pop_dbg_hits;
void pop_dbg_trap(void);
#endif
/* Зеркало уровня 4 (jumped_through_mirror, seg003). 1 — Кид ТОЛЬКО ЧТО
* пролетел сквозь зеркало (ставит is_obstacle в pop_map, банк 3), и
* следующий pop_check_mirror родит тень; 0 — обычное состояние.
* Межбанковый: пишет pop_map, читает pop_map же, но через главный цикл. */
extern int8_t pop_jumped_mirror;
/* ВРЕМЕННО: маркеры фаз кадра (замер). */
void pop_dbg_m1(void); void pop_dbg_m2(void);
void pop_dbg_m3(void); void pop_dbg_m4(void);
/* ВРЕМЕННО: состав перерисовок в самом тяжёлом кадре (замер). */
extern uint8_t pop_dbg_rdmax[10];
extern uint8_t pop_dbg_rdmax_tot;
/* ВРЕМЕННО: маркеры внутренних замеров. */
void pop_dbg_m5(void); void pop_dbg_m6(void);
void pop_dbg_m7(void); void pop_dbg_m8(void);
void pop_dbg_m9(void); void pop_dbg_m10(void);
void pop_dbg_m11(void); void pop_dbg_m12(void);
void pop_dbg_m13(void); void pop_dbg_m14(void);
void pop_dbg_m15(void);
void pop_dbg_kind(uint8_t k); void pop_dbg_m16(void);
void pop_dbg_b1(void); void pop_dbg_b2(void); void pop_dbg_b3(void);
void pop_dbg_b4(void); void pop_dbg_b5(void); void pop_dbg_b6(void);
void pop_dbg_wh(uint16_t wh);