Найдены 8791 тактов на вызов блита: uint16_t габарит в pop_blit_b

Точки на цепочку вызовов (libbgi живёт в резиденте W1, адреса однозначны —
пересборка не нужна) разложили постоянные накладные блита:

  pop_blit_b ДО вызова ядра ....... 6 683   <- НАШ код, 77% накладных
  gfx_blit_noclip + _bgi_begin
    + _gfx_blit_sprite_noclip ..... 1 938
  пролог _bgi_blit_rows_raw ....... 457
  строчный цикл ................... 389/строку  (= 198 + 5,96*32, сходится
                                                 с регрессией)
  эпилог + _bgi_end + возврат ..... 1 355
  «вне цикла» ..................... 8 725 при ЛЮБОЙ высоте (h=9..60)

Причина в pop_blit_b, подтверждена чтением .asm: `w`/`h` объявлены
uint16_t, 16-битные значения не влезли в регистры, и SDCC увёл функцию в
14-байтовый стековый кадр (`ld iy,#-14 / add iy,sp / ld sp,iy`), после чего
`w = img[0] | (img[1] << 8)` развернулось в ДВА ДЕСЯТКА IX-относительных
пересылок между ячейками -7..-13 кадра.

Правка: габарит читается БАЙТАМИ.  Корректно по построению — обе ветки и
так требовали w<256 && h<256 (эти проверки теперь убраны как тождественные),
а кадры атласов не крупнее 32x63; формат .atl допускает больше, такой кадр
уходит на общий путь (blit_b_oversize).

Замер A/B на той же детерминированной сцене, те же выборки:
  gfx_blit_noclip  13 679 -> 11 530  (-16%)
  blit_b_clip      18 853 -> 15 340  (-19%)
Стековый кадр 14 -> 6 байт, _CODE -42 Б.  Тайл ~107 000 -> ~95 000.

Все 8 наборов tests-host проходят, комната 23 в MAME рисуется корректно.

Плюс TASKS_OPEN.md: OPT-BLIT — руководство на следующую сессию (где ещё
uint16->uint8, как искать IX-спиллы по asm, что НЕ делать, и грабли с
несколькими экземплярами MAME на один error.log).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-13 23:51:20 +03:00
parent b27b31318a
commit c312e4a043
2 changed files with 116 additions and 5 deletions
+33 -5
View File
@@ -363,10 +363,22 @@ static void blit_b_clip(const uint8_t *img, int x, int top, int w, int h)
if (!pop_t_fclip_on) pop_cd_touch(dx, dy, dw, dh);
}
/* Кадр шире или выше 255 — общий (медленный) путь. Вынесен отдельно, чтобы
* 16-битная арифметика габарита не жила в горячем pop_blit_b: у нас таких
* кадров нет вовсе (максимум 32x63), но контракт формата .atl их допускает. */
static void blit_b_oversize(const uint8_t *img, int x, int ybottom)
{
uint16_t w = (uint16_t)(img[0] | ((uint16_t)img[1] << 8));
uint16_t h = (uint16_t)(img[2] | ((uint16_t)img[3] << 8));
int top = ybottom - (int)h + 1 + POP_YOFF;
if (pop_upside) top = FLIP_TOP(top, h);
if (w && h) blit_b_clip(img, x, top, (int)w, (int)h);
}
void pop_blit_b(atlas_t *a, uint8_t idx, int x, int ybottom)
{
const uint8_t *img;
uint16_t w, h;
uint8_t w, h; /* БАЙТЫ, а не uint16_t — см. ниже */
int top;
if (idx >= a->count)
return;
@@ -384,9 +396,24 @@ void pop_blit_b(atlas_t *a, uint8_t idx, int x, int ybottom)
img = (const uint8_t *)atlas_image(a, idx);
gfx_w0_map(a->page);
pop_dbg_b2(); /* ВРЕМЕННО */
pop_dbg_wh((uint16_t)(((uint16_t)img[0] << 8) | img[2])); /* ВРЕМЕННО: w,h */
w = (uint16_t)(img[0] | ((uint16_t)img[1] << 8));
h = (uint16_t)(img[2] | ((uint16_t)img[3] << 8));
/* Габарит читаем БАЙТАМИ. С `uint16_t w = img[0] | (img[1] << 8)` SDCC
* разворачивал каждую такую строку в два десятка IX-относительных
* пересылок: 16-битные w/h не влезали в регистры, и функция целиком
* уезжала в стековый кадр (пролог `ld iy,#-14 / add iy,sp / ld sp,iy`,
* дальше всё через -N(ix) по 19 тактов). Замер 2026-08-13: до вызова
* ядра блита уходило ~6 700 тактов при том, что работы там — прочитать
* четыре байта заголовка и сравнить границы.
* Байтовый габарит корректен по построению: обе ветки ниже И ТАК требуют
* w < 256 && h < 256 (иначе ядро не примет), а кадры наших атласов не
* крупнее 32x63. Формат .atl допускает больше — такой кадр уходит на
* общий путь выше. */
if (img[1] | img[3]) { /* кадр больше 255 — редкий путь */
blit_b_oversize(img, x, ybottom);
gfx_w0_unmap();
return;
}
w = img[0];
h = img[2];
top = ybottom - (int)h + 1 + POP_YOFF; /* +YOFF: центрирование */
/* Переворот применяем ЗДЕСЬ, до клипа: дальше вся геометрия (окно
* fore-слоя, полоса у потолка, отсев по экрану) считается уже в
@@ -406,7 +433,8 @@ void pop_blit_b(atlas_t *a, uint8_t idx, int x, int ybottom)
gfx_w0_unmap();
return;
}
if (x >= 0 && top >= 0 && w < 256 && h < 256 &&
/* w<256 && h<256 больше не проверяем — гарантировано типом. */
if (x >= 0 && top >= 0 &&
x + (int)w <= 320 && top + (int)h <= 256) {
pop_dbg_b6(); /* ВРЕМЕННО: пометка «шли в noclip» */
if (pop_upside) gfx_blit_noclip_vflip(x, top, img);