diff --git a/applications/PoP/roomtest/pop_redraw.c b/applications/PoP/roomtest/pop_redraw.c index 6bbd9da..afd32d6 100644 --- a/applications/PoP/roomtest/pop_redraw.c +++ b/applications/PoP/roomtest/pop_redraw.c @@ -80,13 +80,9 @@ void pop_set_redraw_above(uint8_t col, uint8_t kind, uint8_t pages) __banked void pop_redraw_needed(void) __banked { uint8_t i, row = 0, col = 0, left = 0; - uint8_t cnt[10], tot = 0; /* ВРЕМЕННО: состав кадра */ if (!rd_pending) return; - for (i = 0; i < 10; i++) cnt[i] = 0; for (i = 0; i < NTILES; i++) { if (rd_cnt[i]) { - if (rd_kind[i] < 8) { cnt[rd_kind[i]]++; tot++; } /* ВРЕМЕННО */ - pop_dbg_kind(rd_kind[i]); /* ВРЕМЕННО */ switch (rd_kind[i]) { case POP_RD_SPIKE: pop_spike_redraw(row, col); break; case POP_RD_LOOSE: pop_loose_shake_draw(row, col); break; @@ -97,7 +93,6 @@ void pop_redraw_needed(void) __banked case POP_RD_CHOMP: pop_chomp_redraw(row, col); break; default: break; } - pop_dbg_m16(); /* ВРЕМЕННО */ if (--rd_cnt[i]) left++; else rd_kind[i] = 0; } @@ -105,18 +100,11 @@ void pop_redraw_needed(void) __banked } for (i = 0; i < NCOLS; i++) { if (rda_cnt[i]) { - cnt[8 + (rda_kind[i] == POP_RDA_CEIL ? 0 : 1)]++; tot++; /* ВРЕМЕННО */ - pop_dbg_kind((uint8_t)(8 + rda_kind[i])); /* ВРЕМЕННО */ if (rda_kind[i] == POP_RDA_CEIL) pop_ceil_shake_draw(i); else pop_ceil_bake_empty(i); - pop_dbg_m16(); /* ВРЕМЕННО */ if (--rda_cnt[i]) left++; else rda_kind[i] = 0; } } - if (tot > pop_dbg_rdmax_tot) { /* ВРЕМЕННО: расклад пика */ - pop_dbg_rdmax_tot = tot; - for (i = 0; i < 10; i++) pop_dbg_rdmax[i] = cnt[i]; - } rd_pending = left; } diff --git a/applications/PoP/roomtest/pop_room.c b/applications/PoP/roomtest/pop_room.c index 5d4b711..9c1dcdf 100644 --- a/applications/PoP/roomtest/pop_room.c +++ b/applications/PoP/roomtest/pop_room.c @@ -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; } } diff --git a/applications/PoP/roomtest/pop_tile.c b/applications/PoP/roomtest/pop_tile.c index a643753..110606c 100644 --- a/applications/PoP/roomtest/pop_tile.c +++ b/applications/PoP/roomtest/pop_tile.c @@ -503,10 +503,8 @@ void pop_blit_b(atlas_t *a, uint8_t idx, int x, int ybottom) pb_yb + POP_YOFF + 1 <= pop_t_fclip_ly0 || pb_yb + POP_YOFF - 64 >= pop_t_fclip_ly1)) return; - pop_dbg_b1(); /* ВРЕМЕННО */ pb_img = (const uint8_t *)atlas_image(pb_a, pb_idx); gfx_w0_map(pb_a->page); - pop_dbg_b2(); /* ВРЕМЕННО */ /* Габарит читаем БАЙТАМИ. С `uint16_t pb_w = pb_img[0] | (pb_img[1] << 8)` SDCC * разворачивал каждую такую строку в два десятка IX-относительных * пересылок: 16-битные pb_w/pb_h не влезали в регистры, и функция целиком @@ -547,23 +545,18 @@ void pop_blit_b(atlas_t *a, uint8_t idx, int x, int ybottom) /* pb_w<256 && pb_h<256 больше не проверяем — гарантировано типом. */ if (pb_x >= 0 && pb_top >= 0 && pb_x + (int)pb_w <= 320 && pb_top + (int)pb_h <= 256) { - pop_dbg_b6(); /* ВРЕМЕННО: пометка «шли в noclip» */ if (pop_upside) gfx_blit_noclip_vflip(pb_x, pb_top, pb_img); else gfx_blit_noclip(pb_x, pb_top, pb_img); } else { if (pop_upside) gfx_blit_part_vflip(pb_x, pb_top, pb_img, 0, 0, (int)pb_w, (int)pb_h); else gfx_blit_part(pb_x, pb_top, pb_img, 0, 0, (int)pb_w, (int)pb_h); } - pop_dbg_b3(); /* ВРЕМЕННО */ pop_cd_touch(pb_x, pb_top, (int)pb_w, (int)pb_h); - pop_dbg_b4(); /* ВРЕМЕННО */ } else { blit_b_clip(pb_img, pb_x, pb_top, pb_w, pb_h); - pop_dbg_b3(); pop_dbg_b4(); /* ВРЕМЕННО: медленный путь целиком в b3 */ } } gfx_w0_unmap(); - pop_dbg_b5(); /* ВРЕМЕННО */ } /* Блит getimage-блоба из ОБЫЧНОЙ памяти (W2), а не из EMM-страницы атласа: