Files
Sprinter-SDCC/libbgi/bgi256/_bgi_scroll_cols_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

103 lines
6.4 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_cols_raw — accel-скролл КОЛОНКАМИ video->video (mode 0x81).
* УНИВЕРСАЛЕН: делает и горизонтальный, и ВЕРТИКАЛЬНЫЙ сдвиг за один
* проход, БЕЗ буфера-посредника. Ядро gfx_scroll_v (вертикаль); может
* служить и горизонтали (gfx_scroll_h держит свой rows-вариант — он
* на ~5-7% быстрее, т.к. на строку нужен один OUT Port_Y, а не два).
*
* КАК РАБОТАЕТ. Колонка = один вертикальный accel-burst (LD A,A, 0x7F —
* «копия блока для вертикальных линий»): read набирает h байт колонки src
* с Port_Y=y0 (Port_Y авто-инкрементится внутри burst'а: y0→y0+h), write
* выгружает их в колонку dst с Port_Y=y1 (y1→y1+h). Вертикальный сдвиг =
* разница y0/y1; горизонтальный = разница адресов src/dst (dx). Оба сразу
* = произвольный (dx,dy). dst/src двигаются на +1 (следующий x) за колонку.
*
* ПОЧЕМУ РАБОТАЕТ смена Port_Y между read и write (в отличие от наивного
* rows-варианта, где это ломало accel). Ключ — STOP (LD B,B) ПЕРЕД
* OUT Port_Y=y1: он разоружает accel (m_acc_dir=0), поэтому fetch операнда
* OUT (immediate байт 0x89 из программной памяти) НЕ перезапускает
* read-burst. Затем LD A,A снова взводит вертикальный режим для записи.
* Оба OUT'а (y0 и y1) выполняются только пока accel РАЗОРУЖЁН. Порядок на
* колонку: {OUT y0 · arm · read · STOP · OUT y1 · arm · write · STOP}.
* (Подробно про квирк перезапуска — memory/accel_operand_fetch_retrigger.)
*
* DI/EI — БАНДАМИ по 16 колонок ВНУТРИ (как _gfx_recthfill256): держать DI
* на весь скролл нельзя (порвёт CBL-звук). Размер блока (h) армируется в
* КАЖДОЙ скобке (в окне EI CBL-ISR мог бы переармировать accel). Единая
* тело-петля для полных банд и хвоста (счётчик колонок decrement'ится в
* стек-слоте); y0 держим в C (LD A,C 4Т вместо LD A,#imm 7Т на колонку),
* y1 — SMC-immediate (грузится только пока разоружены — безопасно).
*
* ВХОД (__sdcccall(1)): src->HL, dst->DE; стек: w 4(ix) — число КОЛОНОК
* 1..255 (0 = тихий выход; ровно 256 режет вызывающий), h 5(ix) — высота
* колонки = размер accel-блока 1..256 (0 = 256, конвенция accel),
* y0 6(ix) — Port_Y чтения, y1 7(ix) — Port_Y записи. Callee-pop 4 байта.
*
* Raw: клиппинга нет (вызывающий), W3 замаплен (_bgi_begin/end снаружи),
* банк 0x50 (запись бьёт VRAM+тень — скролл И heal цели). Перекрытия
* src/dst нет by design — стороны в РАЗНЫХ видеостраницах. Колонок >255 и
* колонки >256 строк режет вызывающий. Клоббер: AF/BC; HL/DE/IX
* сохраняются (LD A,A/LD D,D — accel-опкоды-no-op'ы, регистры не трогают).
*/
#include "../_bgi.h"
void _bgi_scroll_cols_raw(uint16_t src, uint16_t dst,
uint8_t w, uint8_t h, uint8_t y0, uint8_t y1) __naked {
(void)src; (void)dst; (void)w; (void)h; (void)y0; (void)y1;
__asm
push ix
ld ix, #0
add ix, sp
;; HL = src, DE = dst (адреса-колонки; строку задаёт Port_Y)
ld a, 5 (ix)
ld (sc_col_len), a ; SMC: размер accel-блока <- h (0 = 256)
ld a, 7 (ix)
ld (sc_col_y1), a ; SMC: Port_Y записи <- y1
ld c, 6 (ix) ; C = Port_Y чтения (y0) LD A,C дешевле imm
sc_col_band:
ld a, 4 (ix) ; колонок осталось
or a, a
jr Z, sc_col_done ; 0 => выход
cp a, #16
jr NC, sc_col_full ; >=16 полная банда
ld b, a ; <16 хвост: B = остаток
jr sc_col_go
sc_col_full:
ld b, #16 ; 16 колонок в банде
sc_col_go:
ld a, 4 (ix)
sub a, b
ld 4 (ix), a ; колонок -= B
di
ld d, d ; 0x52 режим размера блока (no-op для D)
ld a, #0
sc_col_len = . - 1
ld b, b ; выключить размер сохранён (B=счётчик цел)
sc_col_next:
ld a, c ; Port_Y чтения (y0)
out (#0x89), a ; accel РАЗОРУЖЁН операнд OUT безопасен
ld a, a ; 0x7F арм вертикальной копии
ld a, (hl) ; read-burst: колонка src -> буфер (Port_Y y0..)
ld b, b ; 0x40 STOP (разоружить перед OUT y1)
ld a, #0
sc_col_y1 = . - 1
out (#0x89), a ; Port_Y записи (y1) accel разоружён
ld a, a ; 0x7F арм вертикальной копии
ld (de), a ; write-burst: буфер -> колонка dst (Port_Y y1..)
ld b, b ; 0x40 STOP
inc hl ; src += 1 (следующий x)
inc de ; dst += 1
djnz sc_col_next
ei ; окно прерываний между бандами
jr sc_col_band
sc_col_done:
pop ix
;; callee-pop 4 байта (w, h, y0, y1)
pop hl ; ret-адрес
pop af
pop af
jp (hl)
__endasm;
}