Ускорение холостого хода: указательный обход вместо 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:
2026-08-13 23:08:05 +03:00
parent 50e4eda2ad
commit f89b7dd0d2
7 changed files with 175 additions and 41 deletions
+13
View File
@@ -9,6 +9,7 @@
#include <stdint.h>
#include "pop_redraw.h"
#include "pop_bg.h"
#include "pop_state.h" /* ВРЕМЕННО: счётчики состава перерисовок */
#define NTILES 30
#define NCOLS 10
@@ -79,9 +80,13 @@ 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;
@@ -92,6 +97,7 @@ 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;
}
@@ -99,11 +105,18 @@ 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;
}