scr_x: таблица готовых значений на весь диапазон, включая отрицательные

Первая версия крыла только 0..255 байтовой таблицей x/7 по тождеству
8x/7 == x + x/7.  Это было мимо: obj_x = 2*fwd - 116 уходит в минус, как
только fwd < 58 (левее x_bump[5]) — то есть у ЛЕВОЙ КРОМКИ комнаты, и там
мы продолжали звать __divsint.

Границы взяты из данных, а не на глаз: kid_data.bin даёт dx кадров Кида
-5..+10, стража -2..+10; при Char.x типа uint8_t и render_dx из
{-140,0,+140} полный диапазон obj_x = -416..695.  SCRX[1152] кроет
-448..703 — деление стало недостижимым, оставлено страховкой.

Хранится ГОТОВОЕ значение (int16_t), а не x/7: байтовая таблица вдвое
меньше, но со знаковыми значениями требует расширения знака плюс
16-битного сложения — те же такты, что лишний add hl,hl при 2-байтном
индексе.  Кодоген проверен: индекс полный 16-битный (грабли
sdcc_z80_const_ptr_index_bug обойдены отдельной uint16_t-переменной),
~130 тактов номинала против ~1 000 у __divsint.

Банк 4: 7 336 -> 9 394 из 16 384 (свободно 6 990).  Таблица сверена
питоном обратно из .c (1 152 записи), правило генерации «усечение к нулю»
закреплено тестом geom_mul8div7_trunc_to_zero на целевом компиляторе.
tests-host: [geom] 1961 -> 1992, все 5 наборов прошли.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-10 20:35:01 +03:00
parent 72797e1be8
commit 8175121d25
3 changed files with 169 additions and 42 deletions
+30 -7
View File
@@ -454,13 +454,36 @@ 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`: тождество **`8x/7 == x + x/7`** (7x делится нацело, поэтому
усечение обеих частей совпадает — и для отрицательных с трункейтом к нулю
тоже). Дальше `x/7` — таблица `DIV7[256]` в банке 4 (резидент не тронут).
Диапазон покрыт не «частый», а весь обычный: `x_bump[5..14] = 58..184`, то
есть `obj_x = 2*fwd 116` у персонажа в отрисованной комнате лежит в 0..252;
хвост нужен только на шве (`render_dx = ∓140`). Тождество проверено тестом
`geom_mul8div7_identity`, сама таблица — сверкой с `x//7` питоном.
Приём для `×8/7`: **таблица готовых значений** `SCRX[1152]` (int16_t) в
банке 4, индекс `x + 448`. 2 304 байта, резидент не тронут.
Два решения по дороге, оба проверены, а не угаданы:
1. **Диапазон — весь, включая отрицательные.** Первая версия крыла 0..255 по
тождеству `8x/7 == x + x/7` с байтовой таблицей `x/7`. Ошибка: `obj_x =
2*fwd 116` уходит в минус, как только `fwd < 58` (левее `x_bump[5]`) —
то есть у ЛЕВОЙ КРОМКИ комнаты, а это не экзотика, и там мы продолжали
делить. Границы взяты из фактических данных: `kid_data.bin` даёт `dx`
кадров Кида −5..+10, стража −2..+10; при `Char.x` типа uint8_t и
`render_dx ∈ {140, 0, +140}` полный диапазон `obj_x` = **416..695**.
Таблица кроет −448..703, деление стало недостижимым (оставлено
страховкой на третьего персонажа / другой `render_dx`).
2. **Хранить готовое значение, а не `x/7`.** Байтовая таблица вдвое меньше,
но со знаковыми значениями к ней добавляются расширение знака и 16-битное
сложение — ровно те же такты, что лишний `add hl,hl` при 2-байтном
индексе. Раз выигрыша нет, берём вариант без арифметики.
Кодоген проверен глазами (`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`.
Проверка таблицы: значения сверены питоном обратно из `.c` со всеми 1 152
записями, а ПРАВИЛО генерации («усечение к нулю») — тестом
`geom_mul8div7_trunc_to_zero` на целевом компиляторе: округляй SDCC к минус
бесконечности, вся отрицательная половина уехала бы на пиксель, и поймалось
бы это только глазами на левой кромке.
Оставлено сознательно (НЕ трогать, это не забытые места):