From e501982457f8fffb7448cae0ee837fc6a2531a6c Mon Sep 17 00:00:00 2001 From: Alexander Petrov Date: Wed, 19 Aug 2026 16:56:50 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA?= =?UTF-8?q?=D0=B0=20=C2=AB=D0=B7=D0=B0=D0=B4=D0=B5=D1=82=20=D0=BB=D0=B8=20?= =?UTF-8?q?=D1=81=D0=BB=D0=BE=D1=82=C2=BB=20=D0=B1=D0=B5=D0=B7=20=D0=BF?= =?UTF-8?q?=D1=8F=D1=82=D0=B8=20=D0=B0=D1=80=D0=B3=D1=83=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D0=BE=D0=B2:=20=D0=BC=D0=B8=D0=BD=D1=83=D1=81=203=20684?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pop_cd_hit принимает (p, x0, y0, x1, y1) — три последних идут стеком, и функция целиком уезжает в IX-фрейм: 45 % её тактов на `-n(ix)` (asm). А зовут её из cd_quiet до восьми раз за кадр. Новый pop_cd_hit_slot(who, p) берёт координаты прямо из pop_cd, а само сравнение вынесено в hit_rect с file-scope аргументами. Первая попытка — обёртка, которая внутри всё равно звала pop_cd_hit — не дала ничего (1799 Z80 вместо 1318, то есть стало хуже), и это записано здесь, чтобы не повторять: снимать аргументы со стека нужно у ТОГО, кто их читает. asm на путь «спрайт + накладной»: было 1799 + 2x1318 = 4435 тактов Z80, стало 1221 + 2x1009 = 3239 (-27 %). Замер 11/15, лёгкая позиция: синяя 219 894 -> 218 052, циан 41 280 -> 39 438, работа кадра 442 662 -> 438 978. Co-Authored-By: Claude Opus 5 --- applications/PoP/roomtest/pop_cdraw.c | 12 +------ applications/PoP/roomtest/pop_cdraw.h | 3 ++ applications/PoP/roomtest/pop_tile.c | 46 +++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 11 deletions(-) diff --git a/applications/PoP/roomtest/pop_cdraw.c b/applications/PoP/roomtest/pop_cdraw.c index ff6bf55..cec00e9 100644 --- a/applications/PoP/roomtest/pop_cdraw.c +++ b/applications/PoP/roomtest/pop_cdraw.c @@ -414,17 +414,7 @@ static uint8_t cd_quiet(uint8_t who, uint8_t p) * * Для skip_mask объединение по-прежнему годится: там вопрос «рядом ли * два слота», и загрубление в бОльшую сторону безопасно. */ - const pop_cdraw_t *q = &pop_cd[who]; - int y0 = q->y[p] + POP_YOFF; - if (pop_cd_hit(p, q->x[p], y0, - q->x[p] + (int)q->w[p] - 1, y0 + (int)q->h[p] - 1)) - return 0; - if (q->ovalid[p]) { - int oy = q->oy[p] + POP_YOFF; - if (pop_cd_hit(p, q->ox[p], oy, - q->ox[p] + (int)q->ow[p] - 1, oy + (int)q->oh[p] - 1)) - return 0; - } + if (pop_cd_hit_slot(who, p)) return 0; } return cd_sig_same(who, p); } diff --git a/applications/PoP/roomtest/pop_cdraw.h b/applications/PoP/roomtest/pop_cdraw.h index 3545047..25f233e 100644 --- a/applications/PoP/roomtest/pop_cdraw.h +++ b/applications/PoP/roomtest/pop_cdraw.h @@ -129,6 +129,9 @@ extern uint8_t pop_cd_ymin[2][10], pop_cd_ymax[2][10]; /* Задели ли прямоугольник (ЭКРАННЫЕ координаты, x1/y1 включительно) то, * что трогали на странице p. Резидент (pop_tile.c). */ uint8_t pop_cd_hit(uint8_t p, int x0, int y0, int x1, int y1); +/* То же для СЛОТА персонажа: спрайт и накладной, координаты изнутри pop_cd + * (см. тело — там про цену пяти аргументов). */ +uint8_t pop_cd_hit_slot(uint8_t who, uint8_t p); void pop_cd_clear(uint8_t p); /* снять метку страницы целиком */ void pop_cd_init(void); /* пустые диапазоны на обеих страницах */ void pop_cd_touch(int x, int y, int w, int h); diff --git a/applications/PoP/roomtest/pop_tile.c b/applications/PoP/roomtest/pop_tile.c index 2d6b0b3..9f15b7b 100644 --- a/applications/PoP/roomtest/pop_tile.c +++ b/applications/PoP/roomtest/pop_tile.c @@ -306,6 +306,52 @@ void pop_cd_touch(int x, int y, int w, int h) /* Задел ли прямоугольник (ЭКРАННЫЕ координаты, x1/y1 ВКЛЮЧИТЕЛЬНО) то, что * трогали на странице 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;