P2b: луч видимости стража 36 786 -> 13 002 (-65 %)
Замер отделил луч от pop_frame_timers: таймеры со всеми тремя
спецсобытиями уровней стоят 1 962, луч — 36 786, то есть 5,6 % работы
кадра на девять чтений байта.
Причина оказалась НЕ в алгоритме. Сверка трёх референсов:
SDLPoP (seg003:688) — идёт по x с шагом 14 и на каждом шаге переводит x
в колонку делением. Причём сам SDLPoP признаёт в комментарии, что
«DOS PoP does this: tile_div_tbl[xpos]» — то есть оригинал брал
таблицу, а порт заменил её на / и %, потому что на 32 битах так проще.
Apple II (MISC.S CHECKALERT) — тот же алгоритм байт в байт, но перевод
x -> блок через таблицу BlockTable[x]. Ровно то, что у нас уже было
сделано (POP_TILE_DIV, 2026-08-10).
mininim — другая архитектура (тайловые позиции, своя механика), для
сравнения реализации не годится.
То есть алгоритмически мы уже были на уровне Apple II, а платили за
другое: pop_tile_at объявлен __banked, луч живёт в guards.c (банк 1), и
на КАЖДУЮ колонку шёл трамплин банк 1 -> банк 3. На сцене 11/15 (Кид в
колонке 2, страж в 8) это девять трамплинов за кадр.
Сделано:
1. луч переведён на КОЛОНКИ вместо x-координат. Это эквивалентно:
начальные x — ровно центры тайлов персонажей, а обратный перевод даёт
ту же колонку (floor((58 + col*14 - 58)/14) == col). Ушли 16-битный
шаг, 16-битное сравнение и индексация таблицы на каждой итерации;
2. тайлы отрезка забираются ОДНИМ банковым вызовом (pop_row_tiles)
вместо девяти;
3. внутри pop_row_tiles — быстрый путь для отрезка целиком внутри
комнаты: get_tile при ряде 0..2 и колонке 0..9 сводится ровно к
g_fg[row*10+col] & 0x1F, идём указателем;
4. буфер тайлов — file-scope, а не локальный массив (иначе каждое
чтение это -n(ix)).
Замер по шагам: 36 786 -> 24 048 (колонки + один вызов) -> 13 002
(быстрый путь + буфер). Синяя фаза 283 215 -> 259 500, работа кадра
654 990 -> 628 542, то есть -26 448 при ожидании -30 000.
Кэш-гейт «пересчитывать только при смене позиции» НЕ понадобился:
расхождения с оригиналом нет, луч считается каждый кадр, как и должен.
Поведение проверено в MAME: страж в боевой стойке, но не идёт — между ним
и Кидом чомпер, то есть can_guard_see_kid = 1 («видит, но не пойдёт»).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -330,6 +330,38 @@ uint8_t pop_tile_at(int8_t col, int8_t row) __banked
|
||||
return get_tile(col, row);
|
||||
}
|
||||
|
||||
/* Тайлы ОТРЕЗКА ряда одним вызовом — для луча видимости стража.
|
||||
*
|
||||
* Зачем: pop_tile_at объявлен __banked, а луч живёт в guards.c (банк 1) и
|
||||
* звал его ПО ОДНОЙ КОЛОНКЕ. На сцене 11/15 (Кид в колонке 2, страж в 8)
|
||||
* это девять трамплинов банк 1 -> банк 3 за кадр, и весь луч стоил 36 786
|
||||
* тактов — 5,6 % работы кадра при том, что делает он девять чтений байта
|
||||
* (замер 2026-08-19). Цена трамплина здесь та же, что уже измерена у
|
||||
* pop_clip_char_top (8 892 такта ради одной проверки тайла).
|
||||
*
|
||||
* Колонки за пределами 0..9 разрешает сам get_tile (шов с соседней
|
||||
* комнатой), поэтому диапазон отдаём как есть, без клипа.
|
||||
*
|
||||
* out обязан вмещать c1 - c0 + 1 байт; вызывающий даёт буфер на 12
|
||||
* (колонки −1..10 плюс запас). */
|
||||
void pop_row_tiles(int8_t row, int8_t c0, int8_t c1, uint8_t *out) __banked
|
||||
{
|
||||
int8_t col;
|
||||
/* БЫСТРЫЙ ПУТЬ — отрезок целиком внутри комнаты. get_tile для ряда
|
||||
* 0..2 и колонки 0..9 сводится ровно к `g_fg[row*10+col] & 0x1F`
|
||||
* (см. его тело выше), а всё остальное там — разбор швов и краёв
|
||||
* уровня. Идём указателем: иначе на каждую колонку заново считается
|
||||
* row*10 + col. */
|
||||
if (row >= 0 && row <= 2 && c0 >= 0 && c1 <= 9 && g_fg) {
|
||||
const uint8_t *p = g_fg + (int)row * 10 + c0;
|
||||
for (col = c0; col <= c1; col++)
|
||||
*out++ = (uint8_t)(*p++ & 0x1F);
|
||||
return;
|
||||
}
|
||||
for (col = c0; col <= c1; col++)
|
||||
*out++ = get_tile(col, row);
|
||||
}
|
||||
|
||||
/* can_bump_into_gate (seg004:373): ОТКРЫТЫЕ ворота проходимы — Kid не бампит и
|
||||
* идёт сквозь них (на шве — уходит в соседнюю комнату; внутри комнаты — просто
|
||||
* проходит по полу под поднятыми барами). ВАЖНО: ворота остаются тайлом 4 (в
|
||||
|
||||
Reference in New Issue
Block a user