Инвентаризация делений по .asm: убраны три, остальные разобраны

Обход всех сгенерированных .asm (awk по `call __div/__mod/__mul` с
привязкой к строке исходника) нашёл 31 вызов в 9 модулях.  Три из них
были в горячем пути:

- pop_cdraw.c calc_screen_x_coord: `x * 8 / 7` -> __divsint, 2 400 тактов
  на ПЕРСОНАЖА КАЖДЫЙ КАДР (два вызова при живом сопернике).  Заменено
  тождеством 8x/7 == x + x/7 плюс таблица DIV7[256] в банке 4 — резидент
  не тронут, обычный диапазон (obj_x 0..252 при x_bump 58..184) покрыт
  целиком, деление осталось только хвостом для шва (render_dx = ∓140).
- pop_guard.c guard_col_from_x: /14 и %14 звались БЕЗУСЛОВНО, мимо
  POP_TILE_DIV — единственное 16-битное деление без подключённой таблицы.
- pop_trob.c animate_chomper: `tp / 10` на чомпера каждый кадр, при том
  что TP_ROW/TP_COL лежали в этом же файле, но ниже по тексту.  Таблицы
  подняты выше чомперов.

Остальные 25 оставлены осознанно и расписаны в TASKS_OPEN.md: хвосты за
таблицей (x вне 0..255 = персонаж в соседней комнате), намеренный
медленный хвост pop_y_to_row, недостижимая ветка pop_rnd_fit и холодные
места (вход стража, старт уровня, читы, имя файла, отладочный HUD).

В банках 4, 6, 7 теперь ноль __div*.  Тождество 8x/7 закреплено тестом
geom_mul8div7_identity (перебор −420..700), таблица DIV7 сверена с x//7.
tests-host: [geom] 840 -> 1961, все 5 наборов прошли.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-10 20:23:43 +03:00
parent 8cac51d592
commit 72797e1be8
5 changed files with 129 additions and 17 deletions
+44 -3
View File
@@ -110,6 +110,46 @@ void pop_guard_free(void) __banked
g_ok = 0;
}
/* ---- calc_screen_x_coord (seg008:2602) БЕЗ деления ------------------- *
*
* Логическая X (280 шир = 28px/тайл) -> экранная (320 = 32px/тайл): ×8/7.
* SDLPoP пишет это как `logical_x*320/280`, у нас SDCC разворачивал в
* `__divsint` — 2 400 тактов на вызов, и зовётся оно на КАЖДОГО персонажа
* КАЖДЫЙ кадр (последнее деление, остававшееся в горячем пути).
*
* Тождество: 8x/7 == x + x/7. Оно ТОЧНОЕ в целых, потому что 7x делится
* нацело: floor((7x+x)/7) = x + floor(x/7); для отрицательных с усечением
* к нулю — тоже (проверено перебором в tests-host).
*
* Таблица кроет 0..255 — это ВЕСЬ обычный случай, а не «частый»:
* x_bump[5..14] = 58..184, то есть у персонажа в отрисованной комнате
* obj_x = 2*fwd 116 лежит в 0..252. Медленный хвост остаётся для шва,
* где render_dx уводит X на ∓140. */
static const uint8_t DIV7[256] = {
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2,
2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4,
4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6,
6, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 9,
9, 9, 9, 9, 9, 9,10,10,10,10,10,10,10,11,11,11,
11,11,11,11,12,12,12,12,12,12,12,13,13,13,13,13,
13,13,14,14,14,14,14,14,14,15,15,15,15,15,15,15,
16,16,16,16,16,16,16,17,17,17,17,17,17,17,18,18,
18,18,18,18,18,19,19,19,19,19,19,19,20,20,20,20,
20,20,20,21,21,21,21,21,21,21,22,22,22,22,22,22,
22,23,23,23,23,23,23,23,24,24,24,24,24,24,24,25,
25,25,25,25,25,25,26,26,26,26,26,26,26,27,27,27,
27,27,27,27,28,28,28,28,28,28,28,29,29,29,29,29,
29,29,30,30,30,30,30,30,30,31,31,31,31,31,31,31,
32,32,32,32,32,32,32,33,33,33,33,33,33,33,34,34,
34,34,34,34,34,35,35,35,35,35,35,35,36,36,36,36,
};
static int scr_x(int x)
{
if ((unsigned int)x < 256u) return x + DIV7[x];
return x * 8 / 7;
}
/* Добавить накладной спрайт (клинок / брызги) в overlay-прямоугольник
* страницы. Оба мелкие и в одном кадре встречаются редко — их объединение
* дешевле третьего heal-слота. */
@@ -314,7 +354,7 @@ static void cd_splash(pop_cdraw_t *s, uint8_t who, const atlas_t *pages,
} else {
qy -= (who == POP_CH_KID) ? 15 : 11;
}
qx = (qx + fw) * 8 / 7; /* calc_screen_x_coord */
qx = scr_x(qx + fw); /* calc_screen_x_coord */
img = (const uint8_t *)atlas_image(ap, aidx);
gfx_w0_map(ap->page);
w = (uint16_t)(img[0] | ((uint16_t)img[1] << 8));
@@ -395,8 +435,9 @@ void pop_char_draw(uint8_t who) __banked
s->fpx = fp_x; s->fpy = obj_y;
/* seg008 calc_screen_x_coord: логическая X (280 шир = 28px/тайл) ->
* экран (320 шир = 32px/тайл), ×320/280 = ×8/7. БЕЗ этого персонаж
* уезжает влево от тайлов тем сильнее, чем правее. */
obj_x = obj_x * 8 / 7;
* уезжает влево от тайлов тем сильнее, чем правее. Через scr_x —
* таблицей, без __divsint (см. шапку DIV7). */
obj_x = scr_x(obj_x);
page = (uint8_t)(fr->image >> 3);
idx = (uint8_t)(fr->image & 7);