mob_tick_one в file-scope + снятие временной оснастки замеров
зелёная пик 423 558 -> 414 456 работа пик 793 266 -> 783 798 ПЕРИОД логического кадра в каскаде: было 5-6 растровых, стало РОВНО 4 mob_tick_one: рабочие переменные в file-scope (было 16 байт кадра и 99 обращений `-N(ix)`, стало 17) — то же лечение, что у draw_tile и blit_b_clip. Снята временная оснастка из ГОРЯЧИХ путей: шесть вызовов pop_dbg_b1..b6 в pop_blit_b (по ~65 такта каждый на КАЖДЫЙ блит), pop_dbg_kind/m16 и подсчёт состава кадра в pop_redraw_needed, pop_dbg_m13..m15 в pop_ceil_shake_draw. Сами пустышки в pop_state.c оставлены — вставить их обратно на один замер дешевле, чем заводить заново; как это делается, записано в docs/perf_l13_room23.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -757,9 +757,7 @@ void pop_bake_forget(void) __banked
|
||||
void pop_ceil_shake_draw(int col) __banked
|
||||
{
|
||||
int x = POP_COL_XH[col] * 8;
|
||||
pop_dbg_m13(); /* ЗАМЕР: вход pop_ceil_shake_draw */
|
||||
pop_heal_off(x, 0, 64, CEIL_BAND_H);
|
||||
pop_dbg_m14(); /* ЗАМЕР: heal сделан */
|
||||
/* Пометка «фон трогали» — ОДНА на восстановленный прямоугольник: пока
|
||||
* стоит окно (ниже), pop_blit_b пометок не ставит. Область та же, что у
|
||||
* heal'а, — он её и стирает. */
|
||||
@@ -771,7 +769,6 @@ void pop_ceil_shake_draw(int col) __banked
|
||||
pop_t_win_clear();
|
||||
pop_t_clip_top = 0;
|
||||
gfx_set_bank(GFX_BANK_NORMAL);
|
||||
pop_dbg_m15(); /* ЗАМЕР: draw_tile сделан */
|
||||
}
|
||||
|
||||
void pop_ceil_bake_empty(int col) __banked
|
||||
@@ -1257,22 +1254,30 @@ static void mob_break_loose(mob_t *m)
|
||||
mob_spawn_copy(m);
|
||||
}
|
||||
|
||||
static mob_t *mt_m;
|
||||
static uint8_t mt_pg, mt_here;
|
||||
|
||||
static void mob_tick_one(mob_t *m, uint8_t pg)
|
||||
{
|
||||
uint8_t here;
|
||||
if (!m->active && !m->clean) return;
|
||||
here = (uint8_t)(m->room == pop_t_room);
|
||||
/* Рабочие переменные — file-scope, не локали: у функции пять вызовов, и
|
||||
* живой через них указатель на слот SDCC спиливал в кадр (16 байт, 99
|
||||
* обращений `-N(ix)`). То же лечение, что у draw_tile и blit_b_clip.
|
||||
* Реентерабельности нет и не требуется: единственный вызывающий —
|
||||
* pop_loose_mob_tick, и слоты он обходит последовательно. */
|
||||
mt_m = m; mt_pg = pg;
|
||||
if (!mt_m->active && !mt_m->clean) return;
|
||||
mt_here = (uint8_t)(mt_m->room == pop_t_room);
|
||||
/* Стереть кусок, нарисованный на ЭТОЙ странице в прошлый раз (у каждой
|
||||
* страницы своя копия фона). Раньше коридор был фиксирован под ряд 2;
|
||||
* плита-потолок падает через весь ряд 0, поэтому чистим по фактической
|
||||
* прошлой позиции. */
|
||||
/* Гейта по `here` здесь БЫТЬ НЕ ДОЛЖНО: prev_y[pg] — это след, который
|
||||
/* Гейта по `mt_here` здесь БЫТЬ НЕ ДОЛЖНО: prev_y[mt_pg] — это след, который
|
||||
* кусок оставил ИМЕННО НА ЭТОЙ странице, и стереть его надо независимо
|
||||
* от того, где кусок сейчас. Пока чистка была под `here`, кусок,
|
||||
* от того, где кусок сейчас. Пока чистка была под `mt_here`, кусок,
|
||||
* улетевший в комнату снизу (mob_down_a_row), навсегда оставлял себя
|
||||
* на одной из двух страниц дабл-буфера — «застрявшая плита», видимая
|
||||
* через кадр (BUG-MOB-STALE-PAGE). */
|
||||
if (m->prev_y[pg] != MOB_Y_NONE) {
|
||||
if (mt_m->prev_y[mt_pg] != MOB_Y_NONE) {
|
||||
/* Ширина/высота — по СЛЕДУ, а не по спрайту. Спрайт куска целиком
|
||||
* влезает в 64x32 (41: 32x13 с y-3, 42: 26x15 с x+32,y-1, 43: 32x3),
|
||||
* но mob_render рисует ещё и ЦЕЛЫЙ соседний тайл поверх правой части
|
||||
@@ -1286,13 +1291,13 @@ static void mob_tick_one(mob_t *m, uint8_t pg)
|
||||
* ЗАДЕЛ: правильнее не перерисовывать соседа вовсе, а КЛИПОВАТЬ
|
||||
* правую часть куска — оригинал так и делает (add_mob_to_objtable,
|
||||
* seg007:1161: clip.right = 40). Тогда коридор вернётся к 64x32. */
|
||||
pop_heal_off(MOB_X0(m->x), m->prev_y[pg] - mob_heal_up, MOB_W, mob_heal_h);
|
||||
m->prev_y[pg] = MOB_Y_NONE;
|
||||
pop_heal_off(MOB_X0(mt_m->x), mt_m->prev_y[mt_pg] - mob_heal_up, MOB_W, mob_heal_h);
|
||||
mt_m->prev_y[mt_pg] = MOB_Y_NONE;
|
||||
}
|
||||
if (!m->active) { m->clean--; return; }
|
||||
if (!mt_m->active) { mt_m->clean--; return; }
|
||||
/* гравитация (move_loose): ускорение до 29 */
|
||||
if (m->speed < 29) m->speed += 3;
|
||||
m->y += m->speed;
|
||||
if (mt_m->speed < 29) mt_m->speed += 3;
|
||||
mt_m->y += mt_m->speed;
|
||||
/* move_loose (seg007:1126): низ куска пересёк границу своего ряда —
|
||||
* смотрим тайл этого ряда. Пусто/loose — валится дальше, пол —
|
||||
* приземление (loose_land: debris + do_knock). Через LOOSE кусок именно
|
||||
@@ -1304,9 +1309,9 @@ static void mob_tick_one(mob_t *m, uint8_t pg)
|
||||
* плиты (1,6) и (1,7), на которые сверху сыплется гряда из комнаты 1.
|
||||
* Куски пролетали сквозь них правильно, а сами плиты оставались целыми и
|
||||
* нарисованными (найдено прогоном 2026-08-13). */
|
||||
if (m->row < 3 && m->y < 226 && MOB_Y_BOUND[m->row + 1] <= m->y) {
|
||||
uint8_t t = mob_tile_at(m);
|
||||
if (t == 11) mob_break_loose(m);
|
||||
if (mt_m->row < 3 && mt_m->y < 226 && MOB_Y_BOUND[mt_m->row + 1] <= mt_m->y) {
|
||||
uint8_t t = mob_tile_at(mt_m);
|
||||
if (t == 11) mob_break_loose(mt_m);
|
||||
if (t == 0 || t == 11) {
|
||||
/* mob_down_a_row (seg007:1387): ряд ниже, а из НИЖНЕГО ряда —
|
||||
* в комнату СНИЗУ (y -= 192, ряд 0). Именно так кусок пролетает
|
||||
@@ -1315,55 +1320,55 @@ static void mob_tick_one(mob_t *m, uint8_t pg)
|
||||
* кнопку (2,7) комнаты 11. Раньше мы кусок на границе комнаты
|
||||
* ГАСИЛИ и подменяли одним «переходом вниз» из главного цикла —
|
||||
* дальше первой комнаты он не летел (BUG-MOB-MULTIROOM). */
|
||||
m->row++;
|
||||
if (m->row >= 3) {
|
||||
m->y = (int16_t)(m->y - 192);
|
||||
m->row = 0;
|
||||
m->room = pop_room_link(m->room, 3 /* вниз */);
|
||||
if (!m->room) { /* улетел из уровня — просто гаснет */
|
||||
m->active = 0; m->clean = 2; /* дочистить ОБЕ страницы */
|
||||
mt_m->row++;
|
||||
if (mt_m->row >= 3) {
|
||||
mt_m->y = (int16_t)(mt_m->y - 192);
|
||||
mt_m->row = 0;
|
||||
mt_m->room = pop_room_link(mt_m->room, 3 /* вниз */);
|
||||
if (!mt_m->room) { /* улетел из уровня — просто гаснет */
|
||||
mt_m->active = 0; mt_m->clean = 2; /* дочистить ОБЕ страницы */
|
||||
return;
|
||||
}
|
||||
here = (uint8_t)(m->room == pop_t_room);
|
||||
mt_here = (uint8_t)(mt_m->room == pop_t_room);
|
||||
}
|
||||
} else if (!here && pop_loose_exit) {
|
||||
} else if (!mt_here && pop_loose_exit) {
|
||||
/* Сигнал прошлой посадки в чужой комнате ещё не разобран (он
|
||||
* ОДИН на кадр) — держим кусок на границе ряда и садим следующим
|
||||
* кадром, как это уже сделано для pop_loose_landed ниже. Без
|
||||
* этого второй кусок затирал первый, и из нескольких плит,
|
||||
* улетевших в одну дыру, щебень оставляла только одна (найдено
|
||||
* прогоном 2026-08-13, уровень 13). */
|
||||
m->y = MOB_Y_BOUND[m->row + 1];
|
||||
} else if (!here) {
|
||||
mt_m->y = MOB_Y_BOUND[mt_m->row + 1];
|
||||
} else if (!mt_here) {
|
||||
/* Приземление в ПОКИНУТОЙ комнате: рисовать нечего, но щебень и
|
||||
* КНОПКА под куском обязаны сработать — сигналим главному циклу
|
||||
* комнатой и тайлом (loose_land, seg007:11E8, работает с
|
||||
* curmob.room, а не с отрисованной). */
|
||||
pop_loose_exit_room = m->room;
|
||||
pop_loose_exit = (uint8_t)(m->row * 10 + m->col + 1);
|
||||
pop_loose_exit_room = mt_m->room;
|
||||
pop_loose_exit = (uint8_t)(mt_m->row * 10 + mt_m->col + 1);
|
||||
/* clean = 2 даже для чужой комнаты: пока кусок летел, он мог
|
||||
* рисоваться в ЭТОЙ, и след остался на обеих страницах. */
|
||||
m->active = 0; m->clean = 2;
|
||||
mt_m->active = 0; mt_m->clean = 2;
|
||||
return;
|
||||
} else if (pop_loose_landed) {
|
||||
/* сигнал прошлого приземления ещё не разобран (один за кадр) —
|
||||
* подержим кусок на границе и приземлим следующим кадром */
|
||||
m->y = MOB_Y_BOUND[m->row + 1];
|
||||
mt_m->y = MOB_Y_BOUND[mt_m->row + 1];
|
||||
} else {
|
||||
m->y = MOB_Y_BOUND[m->row + 1];
|
||||
pop_loose_landed = (uint8_t)(m->row * 10 + m->col + 1);
|
||||
m->active = 0; m->clean = 2; /* кусок исчез — на его месте debris */
|
||||
mt_m->y = MOB_Y_BOUND[mt_m->row + 1];
|
||||
pop_loose_landed = (uint8_t)(mt_m->row * 10 + mt_m->col + 1);
|
||||
mt_m->active = 0; mt_m->clean = 2; /* кусок исчез — на его месте debris */
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Страховка: кусок ушёл ниже поля, не поймав границу ряда (в норме этого
|
||||
* не бывает — mob_down_a_row уводит его в комнату снизу). Просто гаснет,
|
||||
* как у оригинала кусок в комнате 0 (move_loose: `speed = -2`). */
|
||||
if (m->y - 27 >= POP_PLAYFIELD_H) {
|
||||
m->active = 0; m->clean = 2;
|
||||
if (mt_m->y - 27 >= POP_PLAYFIELD_H) {
|
||||
mt_m->active = 0; mt_m->clean = 2;
|
||||
return;
|
||||
}
|
||||
if (!here) return; /* чужая комната — считаем, но не рисуем */
|
||||
if (!mt_here) return; /* чужая комната — считаем, но не рисуем */
|
||||
/* Порядок «плита vs Kid». В оригинале объекты рисуются при обходе СВОИХ
|
||||
* тайлов (redraw_needed_tiles: ряды 2,1,0; колонки 0..9), и только внутри
|
||||
* ОДНОГО тайла сортируются по obj_y (sort_curr_objs, seg008:1561: кто
|
||||
@@ -1372,11 +1377,11 @@ static void mob_tick_one(mob_t *m, uint8_t pg)
|
||||
* поверх Kid, если его тайл идёт в обходе ПОЗЖЕ тайла Kid, а при
|
||||
* совпадении тайлов — если он ниже по y. */
|
||||
{
|
||||
int8_t mrow = pop_y_to_row((int16_t)m->y); /* y_to_row_mod4 */
|
||||
int8_t mrow = pop_y_to_row((int16_t)mt_m->y); /* y_to_row_mod4 */
|
||||
uint8_t over;
|
||||
if (mrow != pop_bg_obj_row) over = (mrow < pop_bg_obj_row); /* ряды идут 2,1,0 */
|
||||
else if (m->col != pop_bg_obj_col) over = (m->col > pop_bg_obj_col);
|
||||
else over = (m->y > pop_cd[POP_CH_KID].fpy);
|
||||
else if (mt_m->col != pop_bg_obj_col) over = (mt_m->col > pop_bg_obj_col);
|
||||
else over = (mt_m->y > pop_cd[POP_CH_KID].fpy);
|
||||
/* РИСОВАНИЯ ЗДЕСЬ БОЛЬШЕ НЕТ — только решение, в какой проход кусок
|
||||
* пойдёт. Пока heal и вывод шли в одной итерации, heal СЛЕДУЮЩЕГО
|
||||
* куска срезал верхнюю кромку уже нарисованного предыдущего: на 13-м
|
||||
@@ -1384,7 +1389,7 @@ static void mob_tick_one(mob_t *m, uint8_t pg)
|
||||
* по y, и перекрытие гарантировано (артефакт «чёрный бар», найден
|
||||
* прогоном 2026-08-13, комната 23). Теперь сперва тикают ВСЕ куски
|
||||
* (то есть heal'ятся все), и только потом рисуются. */
|
||||
m->defer = over;
|
||||
mt_m->defer = over;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user