7fe4c25ad1
Первый шаг 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>
152 lines
8.0 KiB
C
152 lines
8.0 KiB
C
/*
|
||
* _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;
|
||
}
|