Files
Sprinter-SDCC/libbgi/bgi256/_bgi_scroll_rows_raw.c
T
snark13 95c22be9bd libbgi: скролл-примитивы + --w3 + отчёт раскладки памяти
Скролл региона video->video (неактивная страница -> активная, банк 0x50:
копия = скролл + heal цели):
- gfx_scroll_h / _bgi_scroll_rows_raw — горизонтальный, построчно без
  страйдов (~54Т/строку), DI/EI бандами по 16 строк, h=0=>256;
- gfx_scroll_v / _bgi_scroll_cols_raw — верт. И/ИЛИ гориз. за один проход
  без буфера (колонка = accel-burst LD A,A, STOP между read/write делает
  промежуточный OUT Port_Y безопасным), банды по 16 колонок;
- _gfx_addr_shadow_base (адрес неактивной страницы) + gfx_rect_t.
Пример examples/scroll.

check_banks.py + sprinter-cc: отчёт раскладки памяти для ЛЮБОЙ модели
(W1/W2 код/данные, остаток кучи/стека, W3 при --w3, банки при --bank),
не только при --bank.  Док docs/memory-management.md §10.

--w3 (резидентный код окна W3) + сопутствующее: crt0_banked W3_RESIDENT,
mkexe -W, tests/w3probe.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 19:38:00 +03:00

130 lines
6.9 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* _bgi_scroll_rows_raw — БЫСТРАЯ построчная копия video->video со сдвигом
* ТОЛЬКО по X для gfx_scroll_h (mode 0x81). Специализация _bgi_copy_rows_raw
* для скролла: обе стороны — видео, ОБА страйда НУЛЕВЫЕ (CPU-адрес строки
* фиксирован, строку выбирает Port_Y), поэтому адресной арифметики в цикле
* НЕТ ВООБЩЕ — ни страйдовых add (как у copy_rows), ни даже inc адресов
* (как у heal). От _bgi_heal_rows_raw отличается тем, что src != dst
* (HL — колонка-старт src-страницы, DE — dst-страницы со сдвигом dx):
* горизонтальный сдвиг целиком в разнице адресов, задаётся вызывающим.
*
* Вертикального сдвига НЕТ (read и write одной строки на одном Port_Y) —
* для сдвига по Y есть _bgi_scroll_cols_raw (колонки, вертикальный режим).
* Зато rows на ~5-7% быстрее cols: на строку один OUT Port_Y (54Т/строку),
* а cols нужен OUT на чтение И на запись колонки.
*
* ПОРЯДОК на строку: {OUT Port_Y · arm(LD L,L) · read(LD A,(HL)) ·
* write(LD (DE),A) · STOP(LD B,B)}. Между read- и write-триггером —
* НИЧЕГО: на Sprinter accel перехватывает любое чтение программной памяти,
* а fetch операнда любой инструкции там (immediate у OUT/ADD) спурьезно
* перезапустил бы read-burst и набил буфер кодом. Port_Y ставится ДО
* arm'а (accel разоружён STOP'ом прошлой строки — операнд OUT безопасен).
* (memory/accel_operand_fetch_retrigger.)
*
* DI/EI — БАНДАМИ по 16 строк ВНУТРИ (как _gfx_recthfill256): накрывать DI
* весь сдвиг нельзя (порвёт CBL-звук). Армирование размера блока — в
* КАЖДОЙ скобке (в окне EI CBL-ISR армирует accel своим размером). Число
* полных 16-банд и хвост считаем ОДИН раз на входе (precompute) — в петле
* банд остаётся дешёвый dec (без per-band `sub 16` через IX-слот).
* Регистры упёрты (HL src, DE dst, B djnz, C бегущий Port_Y) — быстрее уже
* некуда: out(c) без стопов потребовал бы Port_Y-регистра, которого нет.
*
* ВХОД (__sdcccall(1)): src->HL, dst->DE; стек: w 4(ix) — байт в строке
* (размер accel-блока, 1..256, 0=256), h 5(ix) — строк 1..256 (0 = 256,
* КОНВЕНЦИЯ accel — как у w), y0 6(ix) — стартовый Port_Y (инкремент на
* строку). Callee-pop 3 байта.
*
* Raw: клиппинга нет (вызывающий), W3 замаплен (_bgi_begin/end снаружи),
* банк 0x50 (запись бьёт VRAM+тень — скролл И heal цели). Перекрытия
* src/dst нет by design — стороны в РАЗНЫХ видеостраницах. Строку >256
* байт режет вызывающий. Клоббер: AF/BC; HL/DE/IX сохраняются
* (адреса-константы не трогаются — LD L,L/LD D,D это accel-опкоды-no-op'ы).
*/
#include "../_bgi.h"
void _bgi_scroll_rows_raw(uint16_t src, uint16_t dst,
uint8_t w, uint8_t h, uint8_t y0) __naked {
(void)src; (void)dst; (void)w; (void)h; (void)y0;
__asm
push ix
ld ix, #0
add ix, sp
;; HL = src, DE = dst (адреса-константы; строку задаёт Port_Y)
ld a, 4 (ix)
ld (sc_row_len), a ; SMC: размер блока <- w (0 = 256)
ld (sc_row_len2), a ; ...и в хвостовую скобку
ld c, 6 (ix) ; C = бегущий Port_Y (y0)
;; --- precompute: полных 16-банд (ix+5) + хвост (SMC); h=0 => 256 ---
ld a, 5 (ix)
and #0x0F
ld (sc_row_tail), a ; хвост = h & 15 (0 и при h=0, и при h=256)
ld a, 5 (ix)
or a, a
jr NZ, sc_row_shr
ld a, #16 ; h=0 => 256 => ровно 16 полных банд
jr sc_row_setfull
sc_row_shr:
rrca
rrca
rrca
rrca
and #0x0F ; full = h >> 4
sc_row_setfull:
ld 5 (ix), a ; ix+5 = число полных 16-строчных банд
sc_row_band:
ld a, 5 (ix)
or a, a
jr Z, sc_row_tailband ; полные банды кончились
dec 5 (ix) ; full -= 1
ld b, #16 ; 16 строк в банде
di
ld d, d ; 0x52 режим размера блока (no-op для D)
ld a, #0
sc_row_len = . - 1
ld b, b ; выключить размер сохранён (B=16 цел)
sc_row_next:
ld a, c
out (#0x89), a ; Port_Y = y (accel ВЫКЛЮЧЕН операнд безопасен)
inc c ; y++
ld l, l ; 0x6D арм гориз. копии (no-op для L)
ld a, (hl) ; read-burst: src-строка -> буфер
ld (de), a ; write-burst: буфер -> dst-строка (вплотную!)
ld b, b ; 0x40 стоп
djnz sc_row_next
ei ; окно прерываний между бандами
jr sc_row_band
;; --- хвост h&15 строк: отдельная скобка со СВОИМ армированием ---
sc_row_tailband:
ld a, #0
sc_row_tail = . - 1
or a, a
jr Z, sc_row_done ; хвоста нет
ld b, a ; B = остаток строк (1..15)
di
ld d, d
ld a, #0
sc_row_len2 = . - 1
ld b, b
sc_row_trow:
ld a, c
out (#0x89), a
inc c
ld l, l
ld a, (hl)
ld (de), a
ld b, b
djnz sc_row_trow
ei
sc_row_done:
pop ix
;; callee-pop 3 байта (w, h, y0)
pop hl ; ret-адрес
pop af
inc sp
jp (hl)
__endasm;
}