Оптимизация: логический кадр уложился в бюджет, цикл 4 растровых кадра -> 3

Главный цикл спейсится тремя gfx_wait_vsync, поэтому работа сверх 430 000
тактов стоит сразу целый лишний растровый кадр.  Было 470 964, стало
~425 600 — игра быстрее на треть (16,7 логических кадров/с против 12,5).

- pop_y_to_row: цепочка сравнений вместо (y+60)/63%4-1.  ВАЖНО: медленный
  хвост вынесен в ОТДЕЛЬНУЮ функцию — SDCC видит одинаковое выражение в двух
  ветках и поднимает деление в вершину, быстрые возвраты не спасают.
- col_from_x (pop_bg) и get_tile_div_mod (pop_map) — общие резидентные
  таблицы POP_TILE_DIV/POP_TILE_MOD в pop_tile.c (const банка из чужого
  банка не читается).
- pop_fore_over_char: расширение окна считается арифметикой, а не перебором
  10 колонок и 3 рядов (условие монотонно -> границы).  Формулы сверены с
  прежним перебором перебором значений, расхождений нет.
- pop_cd_touch: цикл по страницам развёрнут, x+w/y+h считаются один раз.
  Зовётся с каждого блита фона, стоил 6 846 тактов.
- process_trobs: tp/10 и tp%10 у факелов — таблицей.
- Пустой слот соперника (стража на сцене нет, на странице ничего не
  нарисовано) считается «тихим»: ни heal, ни вход в pop_char_draw, ни
  fore-проход.

Приём для поиска делений: брейкпоинт на __divsint/__divuint/__divuchar с
печатью адреса возврата (printf "%04X", w@(sp)).

Профиль остатка — TASKS_OPEN.md#draw-cost.  tests-host: 5/5.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-09 15:49:46 +03:00
parent d0030922ff
commit b0524b9ad0
9 changed files with 261 additions and 111 deletions
+39 -16
View File
@@ -304,10 +304,16 @@ void pop_gate_back_b(uint8_t modl, int xh, int dby, int dmy) __banked
* X кадра Kid (до ×8/7), obj_y — низ спрайта, w/h — размер, dir — направление. */
static int8_t col_from_x(int xpos) /* get_tile_div_mod: колонка (58/14) */
{
int x = xpos - 58;
int xh = x / 14;
if (x % 14 < 0) xh--; /* округление вниз для отрицательных */
return (int8_t)xh;
/* Таблицей (резидентная POP_TILE_DIV, см. pop_tile.h) — как в оригинале.
* Было `/14` и `%14`: два полноценных 16-битных деления SDCC на вызов,
* а зовётся это дважды на каждый футпринт персонажа. */
if ((unsigned int)xpos < 256u) return POP_TILE_DIV[xpos];
{
int x = xpos - 58;
int xh = x / 14;
if (x % 14 < 0) xh--; /* округление вниз для отрицательных */
return (int8_t)xh;
}
}
#define y_to_row(y) pop_y_to_row((int16_t)(y)) /* общий (pop_geom) */
@@ -626,20 +632,37 @@ void pop_fore_over_char(const pop_char_t *ch, int obj_x, int obj_y,
* ради скорости (см. pop_fore_layer_cost). Поэтому перебор расширяется
* САМИМ ОКНОМ fore-клипа — оно и есть объединение «персонаж + клинок +
* брызги», считать его второй раз не надо. */
/* Границы считаются АРИФМЕТИКОЙ, а не перебором всех колонок и рядов.
* Условие монотонно по номеру (x0 растёт), значит принятые номера идут
* подряд — хватает первого и последнего. Перебор стоил 38 970 тактов на
* кадр (замер MAME): тринадцать итераций, в каждой пара 16-битных
* ЗНАКОВЫХ сверок, а SDCC пишет их через `jp PO / xor 0x80 / jp P`. */
if (pop_t_fclip_x1 > pop_t_fclip_x0 && pop_t_fclip_y1 > pop_t_fclip_y0) {
int8_t c2, r2;
for (c2 = 0; c2 <= 9; c2++) {
int x0 = POP_COL_XH[c2] * 8;
if (x0 + 40 > pop_t_fclip_x0 && x0 < pop_t_fclip_x1) {
if (c2 < fp_cL) fp_cL = c2;
if (c2 > fp_cR) fp_cR = c2;
}
int8_t lo, hi;
/* Колонки: x0 = POP_COL_XH[c]*8 = 32*c (таблица линейна, шаг 4).
* 32c + 40 > x0 -> c >= floor((x040)/32) + 1
* 32c < x1 -> c <= ceil(x1/32) 1
* Сдвиг вправо у SDCC z80 для int — арифметический, то есть floor и
* для отрицательных; на это здесь и рассчитано. */
lo = (int8_t)(((pop_t_fclip_x0 - 40) >> 5) + 1);
hi = (int8_t)((((pop_t_fclip_x1 + 31) >> 5)) - 1);
if (lo < 0) lo = 0;
if (hi > 9) hi = 9;
if (lo <= hi) {
if (lo < fp_cL) fp_cL = lo;
if (hi > fp_cR) fp_cR = hi;
}
for (r2 = 0; r2 <= 2; r2++) {
int y0 = POP_YOFF + 63 * r2;
if (y0 + 70 > pop_t_fclip_y0 && y0 - 8 < pop_t_fclip_y1) {
if (r2 < fp_rT) fp_rT = r2;
if (r2 > fp_rB) fp_rB = r2;
/* Ряды: y0 = POP_YOFF + 63*r, r только 0..2 — деления на 63 не надо,
* достаточно сравнить с границами полос.
* 63r > y0 POP_YOFF 70 и 63r < y1 POP_YOFF + 8 */
{
int a = pop_t_fclip_y0 - POP_YOFF - 70;
int b = pop_t_fclip_y1 - POP_YOFF + 8;
lo = a < 0 ? 0 : (a < 63 ? 1 : 2);
hi = b > 126 ? 2 : (b > 63 ? 1 : 0);
if (a < 126 && b > 0 && lo <= hi) {
if (lo < fp_rT) fp_rT = lo;
if (hi > fp_rB) fp_rB = hi;
}
}
}