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:
@@ -37,6 +37,7 @@ extern uint8_t _bgi_prevmode; /* видеорежим до initgraph */
|
||||
/* CPU-адрес колонки 0 текущей draw-страницы: 0xC000 (page 0) или
|
||||
* 0xC140 (page 1); каждый примитив использует его вместо константы. */
|
||||
extern uint16_t _gfx_addr_base;
|
||||
extern uint16_t _gfx_addr_shadow_base;
|
||||
|
||||
/* ---- Низкоуровневый видеорежим (libc/video) ---------------------- */
|
||||
uint8_t _videomode_raw_get(void);
|
||||
@@ -108,6 +109,30 @@ void _bgi_heal_rows_raw(uint8_t *scr, int y0, uint8_t w, uint8_t h);
|
||||
void _bgi_hspan (int x, int y, int len, uint8_t color);
|
||||
void _bgi_clearall(uint8_t color);
|
||||
|
||||
|
||||
/* Скролл-ядра video->video (банк 0x50: копия = скролл + heal цели; без
|
||||
* скобки/клиппинга; DI/EI бандами по 16 ВНУТРИ; перекрытия нет — стороны
|
||||
* в разных страницах). Контракты — в шапках bgi256/_bgi_scroll_*_raw.c.
|
||||
*
|
||||
* _bgi_scroll_rows_raw — ядро gfx_scroll_h: сдвиг ТОЛЬКО по X (разница
|
||||
* адресов src/dst), построчно, БЕЗ страйдов — один OUT Port_Y на строку
|
||||
* (~54Т/строку, на 5-7% быстрее cols). w — байт/строка (0=256), h —
|
||||
* строк 1..256 (0=256, конвенция accel), y0 — стартовый Port_Y (++ на строку). */
|
||||
void _bgi_scroll_rows_raw(uint16_t src, uint16_t dst,
|
||||
uint8_t w, uint8_t h, uint8_t y0);
|
||||
|
||||
/* _bgi_scroll_cols_raw — ядро gfx_scroll_v: сдвиг по X И/ИЛИ Y за один
|
||||
* проход БЕЗ буфера. Колонка = вертикальный accel-burst (LD A,A): читаем
|
||||
* с Port_Y=y0, пишем с Port_Y=y1 (сдвиг dy = y1−y0; dx — в адресах). STOP
|
||||
* между read и write делает промежуточный OUT Port_Y безопасным. w —
|
||||
* число КОЛОНОК 1..255 (0=выход), h — высота колонки = размер accel-блока
|
||||
* (0=256), y0/y1 — Port_Y чтения/записи. */
|
||||
void _bgi_scroll_cols_raw(uint16_t src, uint16_t dst,
|
||||
uint8_t w, uint8_t h, uint8_t y0, uint8_t y1);
|
||||
|
||||
|
||||
|
||||
|
||||
/* Ядро блиттинга gfx_blit_part/gfx_blit/gfx_heal (Фаза B) — публичные,
|
||||
* прототипы в <gfx.h>; спрайтовые обёртки putsprite/movesprite — в
|
||||
* <graphics.h>. Реализации: common/gfx_blit_part.c и соседи. */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* _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;
|
||||
}
|
||||
@@ -18,6 +18,7 @@ uint8_t _gfx_bank = 0x50;
|
||||
* (CPU 0xC000+), page 1 — 320..639 (CPU 0xC140+), остаток — дескрипторы
|
||||
* режима/палитра, их не трогаем. */
|
||||
uint16_t _gfx_addr_base = 0xC000;
|
||||
uint16_t _gfx_addr_shadow_base = 0xC140;
|
||||
|
||||
/* Клип спрайтов по экрану: 1 = включён (безопасно, дефолт), 0 = выключен
|
||||
* (приложение само гарантирует, что спрайты не выходят за края — даёт
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* gfx_scroll_h — ГОРИЗОНТАЛЬНЫЙ скролл региона (mode-agnostic обёртка).
|
||||
*
|
||||
* Берёт чистый фон из НЕактивной (теневой) страницы и копирует его со
|
||||
* сдвигом dx по X в активную (draw) страницу. Обе стороны — банк 0x50
|
||||
* (GFX_BANK_NORMAL): запись бьёт VRAM + ОЗУ-копию, т.е. копия
|
||||
* ОДНОВРЕМЕННО скроллит И «лечит» целевую страницу (старые спрайты
|
||||
* затираются сдвинутым фоном; scroll-impl-guide.md §2).
|
||||
*
|
||||
* dx > 0 — сдвиг в сторону УВЕЛИЧЕНИЯ X (изображение едет вправо,
|
||||
* открывается полоса слева); dx < 0 — влево (открывается справа);
|
||||
* dx == 0 — чистая копия фона (полный heal целевой страницы). В обеих
|
||||
* страницах остаёмся в пределах area. Открывшуюся |dx|-полосу НЕ
|
||||
* заполняем — возвращаем её в *dirty (её дозаполняет вызывающий).
|
||||
*
|
||||
* РЕАЛИЗАЦИЯ — построчно, через _bgi_scroll_rows_raw (без страйдов, один
|
||||
* OUT Port_Y на строку — на ~5-7% быстрее колоночного _bgi_scroll_cols_raw,
|
||||
* которому на колонку нужен OUT и на чтение, и на запись). Горизонтальный
|
||||
* сдвиг — чистая разница CPU-адресов src/dst (src_x vs dst_x), Port_Y для
|
||||
* read и write один (одна строка). DI/EI бандами по 16 строк — ВНУТРИ
|
||||
* leaf'а; он же режет высоту на банды и принимает h 1..256 (0=256), так что
|
||||
* здесь делим только ширину >256 (лимит accel-блока). Перекрытий src/dst
|
||||
* нет: стороны в разных страницах.
|
||||
*/
|
||||
|
||||
#include "../include/sprite.h"
|
||||
#include "../_bgi.h"
|
||||
|
||||
void gfx_scroll_h(const gfx_rect_t *area, int16_t dx, gfx_rect_t *dirty) {
|
||||
|
||||
uint8_t saved = gfx_get_bank();
|
||||
uint16_t adx = (dx < 0) ? (uint16_t)(-dx) : (uint16_t)dx;
|
||||
/* src-строка стартует правее на |dx| при сдвиге влево, dst — правее
|
||||
* на dx при сдвиге вправо; так оба края остаются внутри area. */
|
||||
uint16_t src_x = area->x + ((dx < 0) ? adx : 0);
|
||||
uint16_t dst_x = area->x + ((dx > 0) ? adx : 0);
|
||||
uint16_t w = area->w - adx; /* байт (пикселей) в строке */
|
||||
uint8_t h = (uint8_t)area->h; /* строк (256 => 0 = 256 у leaf) */
|
||||
uint16_t src = _gfx_addr_shadow_base + src_x;
|
||||
uint16_t dst = _gfx_addr_base + dst_x;
|
||||
|
||||
gfx_set_bank(GFX_BANK_NORMAL);
|
||||
_bgi_begin();
|
||||
|
||||
/* Ширина >256 байт — банды ≤256 (размер accel-блока, 0=256). Высоту и
|
||||
* DI/EI-банды по 16 строк leaf делает сам (h: 0=256). Между бандами по
|
||||
* ширине CPU-адрес двигаем на 256 (Port_Y выбирает строку). */
|
||||
while (w) {
|
||||
uint8_t cw = (w > 256) ? 256 : (uint8_t)w; /* 0 => 256 */
|
||||
_bgi_scroll_rows_raw(src, dst, cw, h, (uint8_t)area->y);
|
||||
src += 256; dst += 256;
|
||||
w -= (w > 256) ? 256 : w;
|
||||
}
|
||||
|
||||
_bgi_end();
|
||||
gfx_set_bank(saved);
|
||||
|
||||
if (dirty) {
|
||||
dirty->x = (dx >= 0) ? area->x : (int16_t)(area->x + area->w - adx);
|
||||
dirty->y = area->y;
|
||||
dirty->w = adx;
|
||||
dirty->h = area->h;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* gfx_scroll_v — ВЕРТИКАЛЬНЫЙ скролл региона (mode-agnostic обёртка).
|
||||
*
|
||||
* Симметрична gfx_scroll_h, но сдвиг по Y. Берёт чистый фон из НЕактивной
|
||||
* (теневой) страницы и копирует его со сдвигом dy по Y в активную (draw)
|
||||
* страницу. Банк 0x50: копия = скролл + heal целевой страницы (старые
|
||||
* спрайты затираются сдвинутым фоном; см. gfx_scroll_h / guide §2).
|
||||
*
|
||||
* dy > 0 — сдвиг в сторону УВЕЛИЧЕНИЯ Y (изображение едет вниз,
|
||||
* открывается полоса сверху); dy < 0 — вверх (открывается снизу);
|
||||
* dy == 0 — чистая копия (полный heal). Открывшуюся |dy|-полосу НЕ
|
||||
* заполняем — возвращаем в *dirty.
|
||||
*
|
||||
* РЕАЛИЗАЦИЯ — колонками, через _bgi_scroll_cols_raw (вертикальный режим
|
||||
* accel): колонку читаем с Port_Y=ys, пишем с Port_Y=yd (= ys+dy) — сдвиг
|
||||
* по Y без буфера-посредника (STOP между read и write делает промежуточный
|
||||
* OUT Port_Y безопасным; см. шапку leaf'а). Один проход, вдвое быстрее
|
||||
* прежнего grab→blit-через-буфер. Ширину >255 режем на банды колонок
|
||||
* (leaf сам делит их на DI-скобки по 16). Высота колонки h = area->h−|dy|
|
||||
* (≤256, 0=256 у accel). Перекрытия src/dst нет: стороны в разных страницах.
|
||||
*/
|
||||
|
||||
#include "../include/sprite.h"
|
||||
#include "../_bgi.h"
|
||||
|
||||
void gfx_scroll_v(const gfx_rect_t *area, int16_t dy, gfx_rect_t *dirty) {
|
||||
|
||||
uint8_t saved = gfx_get_bank();
|
||||
uint16_t ady = (dy < 0) ? (uint16_t)(-dy) : (uint16_t)dy;
|
||||
/* Строку чтения (ys) сдвигаем вниз на |dy| при сдвиге вверх, строку
|
||||
* записи (yd) — вниз на dy при сдвиге вниз; края в пределах area. */
|
||||
uint8_t ys = (uint8_t)(area->y + ((dy < 0) ? ady : 0));
|
||||
uint8_t yd = (uint8_t)(area->y + ((dy > 0) ? ady : 0));
|
||||
uint16_t hb = area->h - ady; /* строк к переносу (≤256) */
|
||||
|
||||
if (hb) { /* 0 = переносить нечего */
|
||||
uint8_t hblk = (uint8_t)hb; /* размер accel-блока: 256 => 0 */
|
||||
uint16_t w = area->w;
|
||||
uint16_t src = _gfx_addr_shadow_base + area->x;
|
||||
uint16_t dst = _gfx_addr_base + area->x;
|
||||
|
||||
gfx_set_bank(GFX_BANK_NORMAL);
|
||||
_bgi_begin();
|
||||
|
||||
/* >255 колонок — банды (leaf 8-битный по числу колонок; 0=выход,
|
||||
* поэтому ≤255, а не 256). Высота колонки одна на весь проход. */
|
||||
while (w) {
|
||||
uint8_t cw = (w > 255) ? 255 : (uint8_t)w;
|
||||
_bgi_scroll_cols_raw(src, dst, cw, hblk, ys, yd);
|
||||
src += cw; dst += cw; w -= cw;
|
||||
}
|
||||
|
||||
_bgi_end();
|
||||
gfx_set_bank(saved);
|
||||
}
|
||||
|
||||
if (dirty) {
|
||||
dirty->x = area->x;
|
||||
dirty->y = (dy >= 0) ? area->y : (int16_t)(area->y + area->h - ady);
|
||||
dirty->w = area->w;
|
||||
dirty->h = ady;
|
||||
}
|
||||
}
|
||||
@@ -10,5 +10,11 @@ void gfx_set_draw_page(uint8_t page)
|
||||
_gfx_draw_page = page & 1;
|
||||
/* Прямые константы короче (0xC000 + (cond ? 0x140 : 0)) на 3
|
||||
* инструкции Z80 — SDCC не сворачивает сложение констант. */
|
||||
_gfx_addr_base = _gfx_draw_page ? 0xC140 : 0xC000;
|
||||
if(_gfx_draw_page) {
|
||||
_gfx_addr_base = 0xC140;
|
||||
_gfx_addr_shadow_base = 0xC000;
|
||||
} else {
|
||||
_gfx_addr_base = 0xC000;
|
||||
_gfx_addr_shadow_base = 0xC140;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,4 +308,16 @@ void atlas_sprite_init(sprite_t *s, const atlas_t *a, uint8_t idx);
|
||||
void gfx_w0_map(uint8_t page);
|
||||
void gfx_w0_unmap(void);
|
||||
|
||||
typedef struct {
|
||||
int16_t x, y;
|
||||
uint16_t w, h;
|
||||
} gfx_rect_t;
|
||||
|
||||
/* Скролл региона area из НЕактивной страницы в активную со сдвигом на
|
||||
* dx/dy (0 = чистая копия/heal, >0 = в сторону увеличения координат,
|
||||
* <0 = уменьшения). Открывшуюся полосу |d| НЕ заполняют — возвращают
|
||||
* в *dirty (может быть NULL). Банк 0x50: копия = скролл + heal цели. */
|
||||
void gfx_scroll_h(const gfx_rect_t *area, int16_t dx, gfx_rect_t *dirty);
|
||||
void gfx_scroll_v(const gfx_rect_t *area, int16_t dy, gfx_rect_t *dirty);
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user