scr_x: байтовая таблица x/7 вместо словарной — 1 такт быстрее, -1152 Б

Гипотеза «двухбайтная индексация съест выигрыш от сложения» не
подтвердилась.  Собраны ОБА варианта, такты посчитаны по сгенерированному
asm (хвост после проверки границ):

  int16_t готовое: add hl,hl / add hl,de / ld e,(hl) / inc hl / ld d,(hl)
                   = 132 такта, 2 304 байта
  int8_t  x/7:     add hl,bc / ld a,(hl) / ld l,a / rlca / sbc a,a /
                   ld h,a / add hl,de  = 131 такт, 1 152 байта

Расширение знака плюс 16-битное сложение стоят ровно столько же, сколько
лишний add hl,hl при двухбайтном индексе, а обращений к памяти на одно
меньше — под wait-state'ами Sprinter (2,4x номинала именно на обращениях
к ОЗУ) байтовый вариант ещё чуть выгоднее номинала.

Банк 4: 9 394 -> 8 243 из 16 384 (свободно 8 141 вместо 6 990) — запас под
рост pop_cdraw, о котором и был вопрос.

Тест переименован в geom_mul8div7_table_rules и проверяет ОБА правила
генерации таблицы: тождество 8x/7 == x + x/7 и усечение к нулю.
tests-host: [geom] 1992 -> 3144, все 5 наборов прошли.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-10 20:43:27 +03:00
parent 8175121d25
commit fc0ede91e1
3 changed files with 115 additions and 132 deletions
+26 -15
View File
@@ -454,8 +454,8 @@ awk '/^;[a-z_0-9]+\.[ch]:[0-9]+:/{s=$0} /^\tcall\t__(div|mod|mul)/{printf "%-22s
| `pop_guard.c` `guard_col_from_x` | `/14` + `%14` **безусловно**, мимо `POP_TILE_DIV` | единственное 16-битное деление, у которого таблица вообще не была подключена |
| `pop_trob.c` `animate_chomper` | `tp / 10` = `__divuchar` на чомпера каждый кадр | таблицы `TP_ROW`/`TP_COL` уже лежали в ЭТОМ ЖЕ файле, но ниже по тексту — чомперы их не видели |
Приём для `×8/7`: **таблица готовых значений** `SCRX[1152]` (int16_t) в
банке 4, индекс `x + 448`. 2 304 байта, резидент не тронут.
Приём для `×8/7`: **таблица** `SCRX7[1152]` (int8_t, хранит `x/7`) в банке 4,
индекс `x + 448`, результат `x + SCRX7[i]`. 1 152 байта, резидент не тронут.
Два решения по дороге, оба проверены, а не угаданы:
@@ -468,22 +468,33 @@ awk '/^;[a-z_0-9]+\.[ch]:[0-9]+:/{s=$0} /^\tcall\t__(div|mod|mul)/{printf "%-22s
`render_dx ∈ {140, 0, +140}` полный диапазон `obj_x` = **416..695**.
Таблица кроет −448..703, деление стало недостижимым (оставлено
страховкой на третьего персонажа / другой `render_dx`).
2. **Хранить готовое значение, а не `x/7`.** Байтовая таблица вдвое меньше,
но со знаковыми значениями к ней добавляются расширение знака и 16-битное
сложение — ровно те же такты, что лишний `add hl,hl` при 2-байтном
индексе. Раз выигрыша нет, берём вариант без арифметики.
2. **Хранить `x/7` байтом, а не готовое `x*8/7` словом** — по тождеству
`8x/7 == x + x/7`. Первая версия хранила готовое, «раз всё равно
индексация двухбайтная». Собраны ОБА варианта, такты посчитаны по
сгенерированному asm:
| вариант | хвост после проверки границ | тактов | байт |
|---|---|---|---|
| `int16_t` готовое | `add hl,hl / add hl,de / ld e,(hl) / inc hl / ld d,(hl)` | **132** | 2 304 |
| `int8_t` `x/7` | `add hl,bc / ld a,(hl) / ld l,a / rlca / sbc a,a / ld h,a / add hl,de` | **131** | 1 152 |
Расширение знака и 16-битное сложение стоят ровно столько же, сколько
лишний `add hl,hl` при двухбайтном индексе, а обращений к памяти на одно
меньше — под wait-state'ами Sprinter (такт ≈ 2,4× номинала именно на
обращениях к ОЗУ) байтовый вариант ещё чуть выгоднее номинала. Итог:
байтовая таблица не хуже по скорости и на килобайт меньше. **Урок:
«двухбайтный индекс съест выигрыш» — гипотеза; она не подтвердилась.**
Кодоген проверен глазами (`bank4_pop_cdraw.asm`): `ld hl,#0x01C0; add hl,de`,
16-битное беззнаковое сравнение, `add hl,hl; add hl,de; ld e,(hl); inc hl;
ld d,(hl)` — индекс полный, старший байт не теряется (грабли
`sdcc_z80_const_ptr_index_bug` обойдены отдельной `uint16_t`-переменной).
≈130 тактов номинала против ~1 000 у `__divsint`.
16-битное беззнаковое сравнение — индекс полный, старший байт не теряется
(грабли `sdcc_z80_const_ptr_index_bug` обойдены отдельной
`uint16_t`-переменной). 131 такт номинала против ~1 000 у `__divsint`.
Проверка таблицы: значения сверены питоном обратно из `.c` со всеми 1 152
записями, а ПРАВИЛО генерации («усечение к нулю») — тестом
`geom_mul8div7_trunc_to_zero` на целевом компиляторе: округляй SDCC к минус
бесконечности, вся отрицательная половина уехала бы на пиксель, и поймалось
бы это только глазами на левой кромке.
Проверка таблицы: все 1 152 записи сверены питоном обратно из `.c`, а ПРАВИЛА
генерации (тождество + усечение к нулю) — тестом `geom_mul8div7_table_rules`
на целевом компиляторе: округляй SDCC к минус бесконечности, вся
отрицательная половина уехала бы на пиксель, и поймалось бы это только
глазами на левой кромке.
Оставлено сознательно (НЕ трогать, это не забытые места):