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>
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* _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;
|
||||
}
|
||||
Reference in New Issue
Block a user