Ускорение холостого хода: указательный обход вместо arr[i] в горячих циклах
Замер зелёного блока (комната 23 уровня 13, MAME, такты эмулятора) показал, что 86% его стоимости в ПОКОЕ — это pop_loose_tick, который не делает ничего. Причина — кодоген SDCC, подтверждена чтением .asm. С `int pos` и записью pop_loose_modif[pos] компилятор держал счётчик в IX-фрейме, каждую итерацию заново складывал 16-битный адрес элемента, клал его в локал и тут же вычитывал обратно парами `pop bc / pop hl / push hl / push bc`. 40 холостых итераций (30 тайлов + 10 потолков) стоили 27 936 тактов. То же в pop_loose_mob_tick: запись mobs[i] заставляла умножать i на sizeof(mob_t)=15 заново под КАЖДОЕ поле (.active/.clean/.x/.y), 14 пустых слотов — 35 790. Правка — обход указателем, счётчик uint8_t, пустые слоты отсеиваются в вызывающем цикле (а не гардом внутри mob_tick_one, до которого надо ещё дойти). Холостая итерация стала `ld a,(de) / or a,a / jp Z` — три инструкции вместо дюжины с обращениями к памяти. Результат (такты MAME, холостой кадр): циклы по тайлам 27 936 -> 9 852 (2,8x) pop_loose_mob_tick 35 790 -> 7 416 (4,8x) pop_loose_tick 70 866 -> 24 384 (2,9x) ЗЕЛЁНЫЙ БЛОК 82 242 -> 35 760 (2,3x) Банки ужались: BANK3 -90 Б, BANK7 -74 Б. Все 8 наборов tests-host проходят; комната 23 в MAME рисуется корректно. Плюс ВРЕМЕННАЯ оснастка замера (маркеры m9..m16, pop_dbg_kind) — она же показала, что пик зелёного блока сидит НЕ в тряске плит, а в запекании тайлов; разбор продолжается, оснастку снять перед закрытием темы. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2494,7 +2494,13 @@ static void check_loose_fall_on_kid(void)
|
||||
* (back) странице. Звать из основного цикла ПОСЛЕ set_draw_page(back). */
|
||||
void pop_loose_tick(void) __banked
|
||||
{
|
||||
int pos;
|
||||
uint8_t pos;
|
||||
/* Массивы обходим УКАЗАТЕЛЕМ, а не индексом. С `int pos` SDCC держал
|
||||
* счётчик в IX-фрейме, каждую итерацию заново складывал 16-битный адрес
|
||||
* элемента, клал его в локал и тут же вычитывал обратно парами pop/push:
|
||||
* 40 холостых итераций стоили 27 936 тактов (замер 2026-08-13). Ходьба
|
||||
* указателем убирает и адресную арифметику, и IX-фрейм. */
|
||||
uint8_t *lm = pop_loose_modif;
|
||||
/* row/col ведём бегущими счётчиками, а НЕ через pos/10 и pos%10: у Z80
|
||||
* нет аппаратного деления, и SDCC выносил оба деления (__divuchar/
|
||||
* __moduchar) в начало КАЖДОЙ итерации (общее подвыражение аргументов
|
||||
@@ -2502,14 +2508,15 @@ void pop_loose_tick(void) __banked
|
||||
* комнатах без loose-плит (условия if к тому моменту ещё не проверены).
|
||||
* Инкремент+сравнение считают row/col без деления. */
|
||||
uint8_t row = 0, col = 0;
|
||||
for (pos = 0; pos < 30; pos++) {
|
||||
if (pop_loose_modif[pos] != 0) {
|
||||
uint8_t m = ++pop_loose_modif[pos];
|
||||
pop_dbg_m9(); /* ЗАМЕР: вход pop_loose_tick */
|
||||
for (pos = 0; pos < 30; pos++, lm++) {
|
||||
if (*lm != 0) {
|
||||
uint8_t m = ++*lm;
|
||||
/* Отложенный старт уровня 13 досчитал до нуля. Ноль у нас
|
||||
* означает «не анимируется», и плита застряла бы навсегда —
|
||||
* перескакиваем на 1. Стартовое значение выбрано с учётом
|
||||
* этого перескока (см. pop_check_fall_flo). */
|
||||
if (m == 0) m = pop_loose_modif[pos] = 1;
|
||||
if (m == 0) m = *lm = 1;
|
||||
if (m & 0x80) { /* тряска (do_knock) */
|
||||
/* СПЕЦКЕЙС УРОВНЯ 13 (seg007:823): фазу со старшим битом НЕ
|
||||
* гасим и не трясём. На нём check_fall_flo раздаёт плитам
|
||||
@@ -2521,7 +2528,7 @@ void pop_loose_tick(void) __banked
|
||||
if (pop_current_level == POP_LOOSE_TILES_LEVEL) {
|
||||
/* ждём молча */
|
||||
} else if (m >= 0x84) { /* конец тряски: сброс + покой */
|
||||
pop_loose_modif[pos] = 0;
|
||||
*lm = 0;
|
||||
/* modif уже 0 → кадр покоя обоих тайлов (col и col+1:
|
||||
* правая грань loose живёт в соседе). ОБЕ страницы —
|
||||
* иначе на одной застывает дрожащий кадр → мерцание. */
|
||||
@@ -2535,7 +2542,7 @@ void pop_loose_tick(void) __banked
|
||||
* Иначе из двух плит, провалившихся в одном кадре, дыру
|
||||
* запекала бы только вторая, а первая оставалась нарисованной
|
||||
* целой (уровень 13: check_fall_flo роняет гряду разом). */
|
||||
pop_loose_modif[pos] = 10;
|
||||
*lm = 10;
|
||||
} else if (m >= 11) { /* loose_floor_delay — падение */
|
||||
/* remove_loose (seg007:0EB8): тайл → EMPTY (не debris!).
|
||||
* Debris появляется ТОЛЬКО там, где приземлился падающий
|
||||
@@ -2547,7 +2554,7 @@ void pop_loose_tick(void) __banked
|
||||
* его модификатором пустой клетки (seg007:846/1083): по нему
|
||||
* draw_tile_right выбирает blueline_fram1 у дыры. */
|
||||
pop_trob_modif(g_room)[pos] = pop_palace;
|
||||
pop_loose_modif[pos] = 0;
|
||||
*lm = 0;
|
||||
pop_set_redraw(pos, POP_RD_LOOSE_GONE, 2); /* запечь пусто, обе стр. */
|
||||
/* ...и СОСЕДА СПРАВА: передний торец пола заезжает в его
|
||||
* клетку, и без этой пометки он оставался висеть над пустым
|
||||
@@ -2565,10 +2572,11 @@ void pop_loose_tick(void) __banked
|
||||
/* Плиты-ПОТОЛКИ (ряд 2 комнаты сверху): тот же animate_loose, но кадры
|
||||
* рисуются в полосе у потолка, а провалившаяся плита становится EMPTY в
|
||||
* НАШЕЙ копии ряда (g_above) — сверху открывается колодец. */
|
||||
for (col = 0; col < 10; col++) {
|
||||
if (pop_ceil_modif[col] != 0) {
|
||||
uint8_t m = ++pop_ceil_modif[col];
|
||||
if (m == 0) m = pop_ceil_modif[col] = 1; /* см. pop_loose_tick выше */
|
||||
lm = pop_ceil_modif; /* тот же указательный обход */
|
||||
for (col = 0; col < 10; col++, lm++) {
|
||||
if (*lm != 0) {
|
||||
uint8_t m = ++*lm;
|
||||
if (m == 0) m = *lm = 1; /* см. pop_loose_tick выше */
|
||||
if (m & 0x80) { /* тряска от сотрясения (do_knock) */
|
||||
/* Тот же спецкейс уровня 13, и здесь он ГЛАВНЫЙ: плиты,
|
||||
* которым check_fall_flo раздал отложенный старт, лежат
|
||||
@@ -2576,13 +2584,13 @@ void pop_loose_tick(void) __banked
|
||||
* цикл, а не соседний. */
|
||||
if (pop_current_level == POP_LOOSE_TILES_LEVEL) {
|
||||
/* ждём молча */
|
||||
} else if (m >= 0x84) { pop_ceil_modif[col] = 0;
|
||||
} else if (m >= 0x84) { *lm = 0;
|
||||
pop_set_redraw_above(col, POP_RDA_CEIL, 2); }
|
||||
else pop_set_redraw_above(col, POP_RDA_CEIL, 1);
|
||||
} else if (m >= 11 && pop_ceil_fell) {
|
||||
pop_ceil_modif[col] = 10; /* сигнал занят — кадр подождём */
|
||||
*lm = 10; /* сигнал занят — кадр подождём */
|
||||
} else if (m >= 11) { /* loose_floor_delay — плита рушится */
|
||||
pop_ceil_modif[col] = 0;
|
||||
*lm = 0;
|
||||
if (g_above) g_above[col] = TILE_EMPTY; /* remove_loose */
|
||||
pop_set_redraw_above(col, POP_RDA_CEIL_GONE, 2); /* колодец, обе стр. */
|
||||
pop_loose_mob_spawn(-1, col); /* кусок отрывается от потолка */
|
||||
@@ -2592,8 +2600,11 @@ void pop_loose_tick(void) __banked
|
||||
}
|
||||
}
|
||||
}
|
||||
pop_dbg_m10(); /* ЗАМЕР: оба цикла по тайлам пройдены */
|
||||
pop_loose_mob_tick(); /* продвинуть+нарисовать падающий кусок (после тайлов) */
|
||||
pop_dbg_m11(); /* ЗАМЕР: pop_loose_mob_tick сделан */
|
||||
check_loose_fall_on_kid(); /* seg007:1192 — плита падает Киду на голову */
|
||||
pop_dbg_m12(); /* ЗАМЕР: check_loose_fall_on_kid сделан */
|
||||
/* Отложенная допечка ПРОШЛОГО приземления на вторую страницу — ПЕРЕД
|
||||
* разбором нового: иначе при двух плитах, севших подряд, land_bake
|
||||
* затирался новым и на одной странице оставался старый пол. */
|
||||
|
||||
Reference in New Issue
Block a user