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>
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* gfx_copy_page — копия области из НЕактивной страницы в активную:
|
||||
* прямая или с ПЕРЕВОРОТОМ ПО ВЕРТИКАЛИ (mode-agnostic обёртка).
|
||||
*
|
||||
* Зачем. Два сценария, оба про «взять готовую картинку целиком»:
|
||||
* - GFX_COPY_DIRECT — второй странице дабл-буфера отдать то, что уже
|
||||
* нарисовано в первой, вместо повторной отрисовки сцены (у тайловых
|
||||
* движков это в разы дешевле и, главное, невидимо для игрока: он
|
||||
* видит только готовый кадр);
|
||||
* - GFX_COPY_VFLIP — перевернуть уже нарисованное вверх ногами (зелье
|
||||
* инверсии в порте Prince of Persia).
|
||||
*
|
||||
* Направление то же, что у gfx_scroll_*: источник — НЕактивная (теневая)
|
||||
* страница, приёмник — активная (draw). Банк на время копии —
|
||||
* GFX_BANK_NORMAL (0x50): запись бьёт VRAM И ОЗУ-копию. Это не деталь, а
|
||||
* инвариант: accel ЧИТАЕТ ОЗУ-копию, поэтому копируется чистый фон без
|
||||
* спрайтов, а если писать банком без тени, копия страницы-приёмника
|
||||
* останется старой и heal начнёт восстанавливать прошлую картинку.
|
||||
*
|
||||
* VFLIP отражает область ВНУТРИ СЕБЯ: строка area->y + i уходит в строку
|
||||
* area->y + area->h - 1 - i. Горизонтального зеркала здесь нет и не
|
||||
* будет — accel копирует блок только вперёд, побайтовый реверс стоил бы
|
||||
* 320 burst'ов на строку вместо двух (для персонажей зеркало делается
|
||||
* раскладкой спрайта, см. gfx_blit_cols).
|
||||
*
|
||||
* Ширина режется на burst'ы ≤ 255 байт (лимит accel-блока; 256 у нынешних
|
||||
* leaf'ов даёт мусор на втором проходе — тот же TODO, что в gfx_scroll_h).
|
||||
* Высоту и DI/EI-банды по 16 строк leaf делает сам.
|
||||
*/
|
||||
|
||||
#include "../include/gfx.h"
|
||||
#include "../include/sprite.h"
|
||||
#include "../_bgi.h"
|
||||
|
||||
void gfx_copy_page(const gfx_rect_t *area, uint8_t mode) {
|
||||
|
||||
uint8_t saved = gfx_get_bank();
|
||||
uint16_t w = area->w;
|
||||
uint8_t h = (uint8_t)area->h; /* 256 => 0 — конвенция accel */
|
||||
uint16_t src = _gfx_addr_shadow_base + (uint16_t)area->x;
|
||||
uint16_t dst = _gfx_addr_base + (uint16_t)area->x;
|
||||
uint8_t y0 = (uint8_t)area->y;
|
||||
/* Port_Y приёмника: при перевороте начинаем с НИЖНЕЙ строки области и
|
||||
* идём вверх; при прямой копии — та же строка, что у источника. */
|
||||
uint8_t y1 = (mode == GFX_COPY_VFLIP)
|
||||
? (uint8_t)(area->y + area->h - 1)
|
||||
: y0;
|
||||
|
||||
if (!w || !area->h) return;
|
||||
|
||||
gfx_set_bank(GFX_BANK_NORMAL);
|
||||
_bgi_begin();
|
||||
|
||||
while (w) {
|
||||
uint8_t cw = (w > 255) ? 255 : (uint8_t)w;
|
||||
if (mode == GFX_COPY_VFLIP)
|
||||
_bgi_flip_rows_raw(src, dst, cw, h, y0, y1);
|
||||
else
|
||||
_bgi_scroll_rows_raw(src, dst, cw, h, y0);
|
||||
src += cw; dst += cw; w -= cw;
|
||||
}
|
||||
|
||||
_bgi_end();
|
||||
gfx_set_bank(saved);
|
||||
}
|
||||
Reference in New Issue
Block a user