e2730f78e0
Вертикальный скролл перестал ходить через строку-буфер на стеке: колонку читаем с Port_Y=ys, пишем с Port_Y=yd, а STOP между read- и write-триггером делает промежуточный OUT Port_Y безопасным. Один проход вместо grab→blit-через-буфер. Справочник libc приведён в соответствие (там же строка про новую точку входа cbl_open_silence). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
131 lines
5.9 KiB
C
131 lines
5.9 KiB
C
/*
|
||
* 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) {
|
||
#ifdef GFX_NOCHECK
|
||
uint8_t saved = gfx_get_bank();
|
||
uint16_t adx = (dx < 0) ? (uint16_t)(-dx) : (uint16_t)dx;
|
||
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;
|
||
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();
|
||
while (w) {
|
||
uint8_t cw = (w > 255) ? 255 : (uint8_t)w;
|
||
_bgi_scroll_rows_raw(src, dst, cw, h, (uint8_t)area->y);
|
||
src += cw; dst += cw; w -= cw;
|
||
}
|
||
_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;
|
||
}
|
||
#else
|
||
int16_t x, y;
|
||
uint16_t aw, ah, cut, adx;
|
||
uint8_t saved, h;
|
||
uint16_t src_x, dst_x, w, src, dst;
|
||
|
||
if (!area) return;
|
||
x = area->x; y = area->y; aw = area->w; ah = area->h;
|
||
if (!aw || !ah) goto empty;
|
||
|
||
/* Raw-leaf не клипит. Нормализуем область до экрана и не допускаем
|
||
* 16-битного underflow в area->w-|dx|. */
|
||
if (x < 0) {
|
||
cut = (uint16_t)(-x);
|
||
if (cut >= aw) goto empty;
|
||
aw -= cut; x = 0;
|
||
}
|
||
if (y < 0) {
|
||
cut = (uint16_t)(-y);
|
||
if (cut >= ah) goto empty;
|
||
ah -= cut; y = 0;
|
||
}
|
||
if (x >= GFX_WIDTH || y >= GFX_HEIGHT) goto empty;
|
||
if (aw > (uint16_t)(GFX_WIDTH - x)) aw = (uint16_t)(GFX_WIDTH - x);
|
||
if (ah > (uint16_t)(GFX_HEIGHT - y)) ah = (uint16_t)(GFX_HEIGHT - y);
|
||
|
||
/* Сначала сравнение со знаком: так не вычисляем -INT16_MIN. */
|
||
if (dx >= (int16_t)aw || dx <= -(int16_t)aw) {
|
||
if (dirty) { dirty->x=x; dirty->y=y; dirty->w=aw; dirty->h=ah; }
|
||
return;
|
||
}
|
||
|
||
adx = (dx < 0) ? (uint16_t)(-dx) : (uint16_t)dx;
|
||
saved = gfx_get_bank();
|
||
/* src-строка стартует правее на |dx| при сдвиге влево, dst — правее
|
||
* на dx при сдвиге вправо; так оба края остаются внутри area. */
|
||
src_x = (uint16_t)x + ((dx < 0) ? adx : 0);
|
||
dst_x = (uint16_t)x + ((dx > 0) ? adx : 0);
|
||
w = aw - adx;
|
||
h = (uint8_t)ah; /* 256 => 0 у leaf */
|
||
src = _gfx_addr_shadow_base + src_x;
|
||
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) {
|
||
// Закомментированный блок должен работать правильно но не работает
|
||
// почему-то при w > 256 второй проход мусорит
|
||
// TODO: разобраться
|
||
// 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;
|
||
uint8_t cw = (w > 255) ? 255 : (uint8_t)w; /* 0 => 256 */
|
||
_bgi_scroll_rows_raw(src, dst, cw, h, (uint8_t)y);
|
||
src += cw; dst += cw; w -= cw;
|
||
}
|
||
|
||
_bgi_end();
|
||
gfx_set_bank(saved);
|
||
|
||
if (dirty) {
|
||
dirty->x = (dx >= 0) ? x : (int16_t)(x + aw - adx);
|
||
dirty->y = y;
|
||
dirty->w = adx;
|
||
dirty->h = ah;
|
||
}
|
||
return;
|
||
|
||
empty:
|
||
if (dirty) { dirty->x=0; dirty->y=0; dirty->w=0; dirty->h=0; }
|
||
#endif
|
||
}
|