Оптимизация логики, шаг 2: пробеги вместо ветвления на колонку

- move_coll_to_prev -> memcpy (LDIR): цикл на C пересчитывал адрес
  назначения через слот кадра IX и обходился в 5 514 тактов на 14 байт.
- Окно перебора режется на НЕПРЕРЫВНЫЕ пробеги (комната слева / своя /
  справа), по каждому идёт coll_scan с шагающим указателем.  Прежний
  «быстрый путь для окна внутри комнаты» не срабатывал почти никогда: Кид
  в колонке 0 даёт окно с −1, и всегда шёл медленный сбор во временный
  буфер с тернарником на колонку (1 340 тактов на колонку).
- Пролог ряда (координата грани, длина окна, смещение слота) вынесен из
  тела ряда на кадр; базы соседних рядов — те же ±10 без пересчёта.

check_collisions 44 022 -> 38 334, физика Кида 67 518 -> 63 102, работа за
логический кадр 470 964 -> 466 560 (бюджет растрового кадра 430 000).
Замер итерации: пустая колонка 750 тактов, колонка-стена ~1 700.

tests-host: все 5 наборов прошли.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-09 15:09:51 +03:00
parent 8c4bc4f621
commit d0030922ff
2 changed files with 188 additions and 71 deletions
+18 -6
View File
@@ -379,20 +379,30 @@ wait-state'ы, и замеренная стоимость выходит **≈ 2
|---|---|---|
| окно перебора коллизии как в оригинале (было: все 14 колонок каждый кадр) | `check_collisions` 60 888 | 50 940 |
| `get_tile_div_mod` — таблицей (`tile_div_tbl`/`tile_mod_tbl`), было `/14` и `%14` | 5 400 тактов на вызов, 13 вызовов за кадр ≈ 70 000 = **16 % кадра** | ~250 на вызов |
| разрешение ряда вынесено из цикла колонок + грань шагом 14 + `wall_type` таблицей | `check_collisions` 58 026 | **42 750** |
| разрешение ряда вынесено из цикла колонок + грань шагом 14 + `wall_type` таблицей | `check_collisions` 58 026 | 42 750 |
| `move_coll_to_prev``memcpy` (LDIR) вместо цикла на C | 14 байт за 5 514 тактов (390 на байт!) | ~1 200 |
| окно режется на непрерывные пробеги (левый сосед / своя / правый), пролог ряда вынесен на кадр | `check_collisions` 44 022 | **38 334** |
Замер одной итерации перебора: **пустая колонка 750 тактов, колонка-стена
~1 700** (две 16-битные знаковые сверки граней — SDCC пишет их через
`jp PO / xor 0x80 / jp P`). Ловушка, на которую наступили: «быстрый путь для
окна внутри комнаты» не срабатывал ПОЧТИ НИКОГДА — Кид, стоящий в колонке 0,
даёт окно с −1, и шёл медленный сбор во временный буфер с тернарником на
колонку (1 340 тактов на колонку). Отсюда разбиение на пробеги: вопрос «чья
это колонка» решается раз на пробег, а `coll_scan` сам двигает `scan_left`.
Самое дорогое было НЕ там, где ожидалось: `/14` и `%14` SDCC разворачивает
в `__divsint` + `__modsint`, а `__modsint` внутри зовёт `__divsint` ещё раз —
два полноценных 16-битных деления на каждый вопрос «в какой колонке точка».
Оригинал делит таблицей (seg006:702) — мы просто не портировали это место.
**Профиль работы за один логический кадр ПОСЛЕ правок (470 964 такта):**
**Профиль работы за один логический кадр ПОСЛЕ правок (466 560 тактов):**
| блок | тактов | % растрового кадра |
|---|---|---|
| see_kid + ctrl_tick + heal | 46 920 | 11 |
| kid_tick (play_seq) | 6 468 | 1,5 |
| физика Кида (`pop_phys_tick`) | 67 518 | 16 |
| физика Кида (`pop_phys_tick`) | 63 102 | 15 |
| страж + боёвка | 10 350 | 2,4 |
| `pop_loose_tick` | 27 438 | 6,4 |
| **`pop_process_trobs`** | **92 346** | **21,5** |
@@ -402,8 +412,10 @@ wait-state'ы, и замеренная стоимость выходит **≈ 2
| борта | 732 | 0,2 |
Синяя полоса (ввод+heal+логика) была ~60 % → стала ~30 %. Внутри физики
`check_collisions` — по-прежнему больше половины (42 750 из 67 518), и в ней
15 разрешений тайла на кадр: три ряда × окно 4–5 колонок, как в оригинале.
`check_collisions` — по-прежнему больше половины (38 334 из 63 102): три ряда
× окно 4–5 колонок, как в оригинале. Дальше там режется плохо — осталась
арифметика граней, а её удешевление требует перевода сверок в 8 бит и
доказательства, что `coll_xl`/`coll_xr` не выходят из 0..255.
**Что осталось — по убыванию (это и есть остаток шага 2):**
@@ -415,7 +427,7 @@ wait-state'ы, и замеренная стоимость выходит **≈ 2
сидят внутри process_trobs. Разобрать, где именно.
3. **`pop_loose_tick` 27 438** при полном отсутствии падающих плит в комнате —
похоже, безусловный проход по всем 30 тайлам.
4. Работа за цикл 470 964 против 430 000 бюджета: **не хватает ~41 000**,
4. Работа за цикл 466 560 против 430 000 бюджета: **не хватает ~37 000**,
чтобы уложиться в три растровых кадра вместо четырёх. Любая из правок
выше даёт игре сразу +25 % скорости — граница проходит рядом.