Files
Sprinter-SDCC/libbgi/bgi256/_bgi_flip_rows_raw.c
T
snark13 7fe4c25ad1 libbgi: переворот экрана акселератором (gfx_copy_page + _bgi_flip_rows_raw)
Первый шаг L9-INVERT.  Разведка железа закрыта экспериментом: Port_Y можно
менять между read- и write-триггером, если перед вторым OUT стоит STOP —
приём уже был в проде в _bgi_scroll_cols_raw (указал пользователь), так что
обходной путь через ОЗУ-буфер не понадобился.

- _bgi_flip_rows_raw: построчная копия video->video с реверсом Port_Y
  приёмника (DI/EI бандами по 16 строк, размер блока армируется в каждой
  скобке, убывающий y1 держится SMC-ячейкой);
- gfx_copy_page(area, GFX_COPY_DIRECT|GFX_COPY_VFLIP): копия области из
  неактивной страницы в активную; банк 0x50, то есть приёмник обновляется и
  в VRAM, и в ОЗУ-копии — иначе heal восстанавливал бы старый фон;
- tests/pageflip: 5/5 PASS в MAME (прямая, vflip, рамка, широкая 320 двумя
  проходами, источник цел).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 16:38:10 +03:00

152 lines
8.0 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_flip_rows_raw — построчная копия video->video с ПЕРЕВОРОТОМ ПО
* ВЕРТИКАЛИ (mode 0x81). Строка src с Port_Y = y0 уходит в строку dst с
* Port_Y = y1, дальше y0 растёт, а y1 УБЫВАЕТ: за h строк область
* отражается сверху вниз. Ядро gfx_copy_page(GFX_COPY_VFLIP) — переворот
* экрана для зелья инверсии (applications/PoP/docs/l9_invert_plan.md).
*
* Отличие от _bgi_scroll_rows_raw: там read и write идут на ОДНОМ Port_Y
* (одна строка), поэтому OUT на строку один. Здесь строки разные, значит
* Port_Y надо сменить МЕЖДУ read- и write-триггером — а это ровно тот
* случай, где наивная схема ломается: accel перехватывает любое чтение
* программной памяти, и fetch immediate-операнда OUT спурьезно
* перезапустил бы read-burst, набив буфер кодом
* (memory/accel_operand_fetch_retrigger).
*
* РЕШЕНИЕ — то же, что в _bgi_scroll_cols_raw (там оно уже в проде,
* gfx_scroll_v): STOP (LD B,B) ПЕРЕД вторым OUT. Он разоружает accel
* (m_acc_dir = 0), после чего операнд OUT безопасен; затем LD L,L снова
* взводит горизонтальный режим — уже для записи. Порядок на строку:
* {OUT y0 · arm · read · STOP · OUT y1 · arm · write · STOP}
* Оба OUT выполняются только пока accel РАЗОРУЖЁН — это инвариант, на
* котором всё держится; менять порядок нельзя.
*
* Убывающий y1 держим SMC-ячейкой, а не регистром: HL/DE заняты адресами,
* B — счётчиком банды, C — растущим y0, и свободного 8-битного не
* остаётся. Обновление ячейки (`dec a` + `ld (…),a`) стоит на строке
* 17Т и идёт ПОСЛЕ STOP'а, то есть при разоружённом accel — безопасно.
*
* DI/EI — БАНДАМИ по 16 строк ВНУТРИ (как у scroll-ядер): держать DI на
* весь проход нельзя (порвёт CBL-звук). Размер блока армируется в КАЖДОЙ
* скобке — в окне EI между бандами CBL-ISR армирует accel своим размером.
*
* ВХОД (__sdcccall(1)): src->HL, dst->DE; стек: w 4(ix) — байт в строке
* (размер accel-блока, 1..256, 0 = 256), h 5(ix) — строк 1..256 (0 = 256,
* конвенция accel), y0 6(ix) — Port_Y ПЕРВОЙ строки src (инкремент),
* y1 7(ix) — Port_Y ПЕРВОЙ строки dst (ДЕКРЕМЕНТ). Callee-pop 4 байта.
*
* Raw: клиппинга нет (вызывающий), W3 замаплен (_bgi_begin/_bgi_end
* снаружи), банк 0x50 (запись бьёт VRAM + ОЗУ-копию — иначе перевёрнутый
* фон не попадёт в копию и heal начнёт восстанавливать старую картинку).
* Перекрытия src/dst нет by design — стороны в РАЗНЫХ видеостраницах.
* Строку > 256 байт режет вызывающий. Клоббер: AF/BC; HL/DE/IX
* сохраняются (LD L,L / LD D,D / LD B,B — accel-опкоды-no-op'ы).
*/
#include "../_bgi.h"
void _bgi_flip_rows_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, 4 (ix)
ld (fl_row_len), a ; SMC: размер блока <- w (0 = 256)
ld (fl_row_len2), a ; ...и в хвостовую скобку
ld a, 7 (ix)
ld (fl_row_ydst), a ; SMC: Port_Y записи <- y1 (убывает)
ld c, 6 (ix) ; C = бегущий Port_Y чтения (y0)
;; --- precompute: полных 16-банд (ix+5) + хвост (SMC); h=0 => 256 ---
ld a, 5 (ix)
and #0x0F
ld (fl_row_tail), a ; хвост = h & 15 (0 и при h=0, и при h=256)
ld a, 5 (ix)
or a, a
jr NZ, fl_row_shr
ld a, #16 ; h=0 => 256 => ровно 16 полных банд
jr fl_row_setfull
fl_row_shr:
rrca
rrca
rrca
rrca
and #0x0F ; full = h >> 4
fl_row_setfull:
ld 5 (ix), a ; ix+5 = число полных 16-строчных банд
fl_row_band:
ld a, 5 (ix)
or a, a
jr Z, fl_row_tailband ; полные банды кончились
dec 5 (ix) ; full -= 1
ld b, #16 ; 16 строк в банде
di
ld d, d ; 0x52 режим размера блока (no-op для D)
ld a, #0
fl_row_len = . - 1
ld b, b ; выключить размер сохранён (B=16 цел)
fl_row_next:
ld a, c
out (#0x89), a ; Port_Y = y0 (accel РАЗОРУЖЁН операнд безопасен)
inc c ; y0++
ld l, l ; 0x6D арм гориз. копии
ld a, (hl) ; read-burst: src-строка -> буфер
ld b, b ; 0x40 STOP (разоружить перед OUT y1)
ld a, #0
fl_row_ydst = . - 1
out (#0x89), a ; Port_Y = y1 accel разоружён
dec a
ld (fl_row_ydst), a ; y1-- (запись при разоружённом accel)
ld l, l ; 0x6D арм гориз. копии для записи
ld (de), a ; write-burst: буфер -> dst-строка
ld b, b ; 0x40 стоп
djnz fl_row_next
ei ; окно прерываний между бандами
jr fl_row_band
;; --- хвост h&15 строк: отдельная скобка со СВОИМ армированием ---
fl_row_tailband:
;; y1 у хвоста ПРОДОЛЖЕНИЕ, а не исходное значение: переносим
;; текущее из основной скобки (у неё своя immediate-ячейка).
ld a, (fl_row_ydst)
ld (fl_row_ydst2), a
ld a, #0
fl_row_tail = . - 1
or a, a
jr Z, fl_row_done ; хвоста нет
ld b, a ; B = остаток строк (1..15)
di
ld d, d
ld a, #0
fl_row_len2 = . - 1
ld b, b
fl_row_trow:
ld a, c
out (#0x89), a
inc c
ld l, l
ld a, (hl)
ld b, b
ld a, #0
fl_row_ydst2 = . - 1
out (#0x89), a
dec a
ld (fl_row_ydst2), a
ld l, l
ld (de), a
ld b, b
djnz fl_row_trow
ei
fl_row_done:
pop ix
;; callee-pop 4 байта (w, h, y0, y1)
pop hl ; ret-адрес
pop af
pop af
jp (hl)
__endasm;
}