/* * _gfx_rectvfill256 — заливка прямоугольника (x,y,w,h) ВЕРТИКАЛЬНЫМИ * колонками (w выстрелов высотой h), mode 0x81. Chunked-схема: скобки * DI/EI по ≤16 колонок, армирование размера один раз на скобку, на * колонку — re-OUT Port_Y + LD E,E + выстрел (семантика мульти-триггера: * memory/accel_multitrigger_fill, проверено в MAME tests/accfill). * * Счётчик чанков — precompute (v3): один раз на входе E = w>>4 (полных * чанков, ≤20 при w<=320) и w&15 — хвост (SMC в ld b,#n хвостовой * скобки). Заголовок чанка = dec e/jr nz (~46Т на чанк ≈ 2.9Т/линию * против ~173Т/чанк у 16-бит арифметики через IX-слоты); precompute * ~170Т однократно. Итог ~56Т/линию. Хвост — отдельная скобка со * СВОИМ армированием (второй SMC-слот размера): армирование обязано * быть в каждой скобке — в окне EI CBL-ISR армирует акселератор своим * размером блока. * * Прямой вызов оправдан, когда форма известна заранее (высокие/узкие * прямоугольники, w <= h) — экономит ~160-245Т проверки диспетчера * _gfx_rectfill256 (см. там же расчёт break-even). * * ---- КОНТРАКТ (ответственность вызывающего) ------------------------ * - 1 <= h <= 256 (h == 256 кодируется младшим байтом 0 — конвенция * акселератора; h == 0 даст 256!). 0 <= w <= 320 (w == 0 — тихий * выход; старший байт w интерпретируется как «+16 чанков», т.е. * w > 511 — UB). Клиппинга НЕТ, координаты обязаны быть валидны. * - W3 замаплен (_bgi_begin у вызывающего). * * __sdcccall(1): x→HL, y→DE(E=y); w→4/5(ix), h→6/7(ix), color→8(ix); * __naked → callee-pops 5 байт. Инварианты в регистрах: HL=бегущий addr, * C=цвет, D=y0 (константа; vertical Fill двигает Port_Y — re-OUT * обязателен), E=счётчик чанков, B=djnz внутри скобки. */ #include "../_bgi.h" void _gfx_rectvfill256(int x, int y, int w, int h, uint8_t color) __naked { __asm .globl __gfx_addr_base push ix ld ix, #0 add ix, sp ld bc, (__gfx_addr_base) add hl, bc ; HL = base + x (бегущий) ld d, e ; D = y0 (константа) ld a, 8 (ix) ld c, a ; C = color ld a, 6 (ix) ld (rv_len), a ; SMC: размер блока <- h (0 = 256) ld (rv_len2), a ; ...и в хвостовую скобку ;; --- precompute (один раз): E = w>>4, хвост w&15 -> SMC --- ld a, 4 (ix) and #0x0F ld (rv_tail), a ; SMC: хвост <- w & 15 ld a, 4 (ix) rrca rrca rrca rrca and #0x0F ld e, a ; E = (w низкий) >> 4 ld a, 5 (ix) or a, a jr Z, rv_go ; w < 256 ld a, e add a, #16 ; старший байт: w<=320 => ровно +16 чанков ld e, a rv_go: ld a, e or a, a jr Z, rv_tail_go ; полных чанков нет ;; --- E полных чанков по 16 колонок --- ;; (армирование НЕ выносить из цикла: в окне EI CBL-ISR ;; армирует акселератор своим размером блока) rv_chunk: ld b, #16 ; колонок в чанке di ld d, d ; 0x52 — режим размера блока (no-op для D=y0) ld a, #0 rv_len = . - 1 ld b, b ; выключить — размер сохранён rv_col: ld a, d out (#0x89), a ; Port_Y = y0 (vertical Fill его сдвинул) ld e, e ; 0x5B — вертикальный Fill (no-op для E-счётчика) ld a, c ld (hl), a ; выстрел: колонка h строк ld b, b ; выключить (перед djnz — fetch смещения!) inc hl ; следующая колонка djnz rv_col ei ; окно для прерываний между чанками dec e jr NZ, rv_chunk ;; --- хвост w&15 колонок: отдельная скобка со СВОИМ армированием --- rv_tail_go: ld a, #0 rv_tail = . - 1 or a, a jr Z, rv_done ; хвоста нет ld b, a di ld d, d ld a, #0 rv_len2 = . - 1 ld b, b rv_tcol: ld a, d out (#0x89), a ld e, e ld a, c ld (hl), a ld b, b inc hl djnz rv_tcol ei rv_done: pop ix pop hl pop af pop af inc sp jp (hl) __endasm; } #if 0 /* ================================================================ * Предыдущий вариант (v2, 2026-07-10, до precompute): решение * «16 или хвост» и вычитание w -= n на КАЖДЫЙ чанк через 16-бит * арифметику в IX-слотах (~141-189Т на чанк ≈ 11Т/линию). * Оставлен закомментированным для отката/сравнения. * ================================================================ */ void _gfx_rectvfill256(int x, int y, int w, int h, uint8_t color) __naked { __asm .globl __gfx_addr_base push ix ld ix, #0 add ix, sp ld bc, (__gfx_addr_base) add hl, bc ; HL = base + x (бегущий) ld d, e ; D = y0 (константа) ld a, 8 (ix) ld c, a ; C = color ld a, 6 (ix) ld (rv_len), a ; SMC: размер блока <- h (0 = 256) rv_chunk: ld a, 5 (ix) or a, a jr NZ, rv_full ; w >= 256 -> чанк 16 ld a, 4 (ix) or a, a jr Z, rv_done ; w == 0 -> готово cp a, #16 jr NC, rv_full ld b, a ; хвост: n = w jr rv_go rv_full: ld b, #16 ; полный чанк rv_go: ld a, 4 (ix) ; w -= n sub a, b ld 4 (ix), a ld a, 5 (ix) sbc a, #0 ld 5 (ix), a ;; --- скобка: n колонок одним армированием размера --- ;; (армирование НЕ выносить из цикла: в окне EI CBL-ISR ;; армирует акселератор своим размером блока) di ld d, d ; 0x52 — режим размера блока (no-op для D=y0) ld a, #0 rv_len = . - 1 ld b, b ; выключить — размер сохранён rv_col: ld a, d out (#0x89), a ; Port_Y = y0 (vertical Fill его сдвинул) ld e, e ; 0x5B — вертикальный Fill (no-op для E) ld a, c ld (hl), a ; выстрел: колонка h строк ld b, b ; выключить (перед djnz — fetch смещения!) inc hl ; следующая колонка djnz rv_col ei ; окно для прерываний между чанками jr rv_chunk rv_done: pop ix pop hl pop af pop af inc sp jp (hl) __endasm; } #endif