PoP/libbgi: убрана 32-бит арифметика, noclip для column-major, быстрый PRNG
Ревью на 32-бит сделан ПО ASM, а не по коду (искали и безымянные
временные): во всём приложении был ровно ОДИН 32-битный вызов —
__mullong в pop_prandom. Замер в MAME: 8 430 тактов на вызов, два
вызова за кадр. Прочие библиотечные вызовы 16-битные (__divsint 16,
__modsint 12, __moduchar 8, __divuchar 5).
1. pop_prandom. Состояние 32-бит -> две 16-битные половины. Два
генератора, выбор через POP_PRANDOM_EXACT:
- 0 (по умолчанию) — xorshift16 + шаг Вейля, без единого умножения;
- 1 — LCG оригинала бит-в-бит, посчитанный половинами (для сверки
картинки с эталоном).
8-битный RND Apple II (5*x+23 mod 256) НЕ взят: у LCG по модулю 256
вырождены младшие биты (бит 0 просто чередуется), а раскладка кладки
берёт как раз prandom(1) и prandom(4) — вместо шума вышла бы
правильная шахматка. Шаг Вейля ещё и убирает ноль как неподвижную
точку xorshift (сид кладки вполне может быть нулём).
Бит-в-бит эквивалентность half-word версии проверена на хосте:
70 000 сидов x 8 шагов + 7 крайних сидов x 2000 шагов.
Остаток 0..maxv: делитель степень двойки — маска вместо __moduint.
2. libbgi: gfx_blit_cols_part_noclip — column-major блит без клипа
(пара к gfx_blit_cols_part, как gfx_blit_part_noclip к
gfx_blit_part). Клипающий вариант платит ~5 622 такта подготовки на
КАЖДЫЙ вызов независимо от того, вылезает край (замер: подготовка
5 622 против 13 596 на сам accel-проход). Kid, страж и клинок
выбирают путь по pop_onscreen_cols. size-check: роста нет.
Бюджет (175 кадров, комната 3, медиана):
было (после клинка) 416 154 0.968 кадра
стало 397 986 0.926 кадра
Разница между генераторами, замер на одинаковой сборке:
xorshift16 + Вейль 397 986 prandom->torch_draw 7 927
бит-в-бит LCG 403 632 prandom->torch_draw 10 933
то есть точность обходится в 5 646 тактов за кадр (1.3 %).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -354,7 +354,7 @@ static uint8_t wall_modifier(int row, int col)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ---- PRNG (seg009.c:321, 32-бит LCG) — только на ВХОД в комнату ---- */
|
/* ---- PRNG (seg009.c:321, 32-бит LCG) — только на ВХОД в комнату ---- */
|
||||||
static unsigned long rnd_seed;
|
static pop_rnd_t rnd_seed;
|
||||||
|
|
||||||
#define prandom(maxv) pop_prandom(&rnd_seed, (maxv))
|
#define prandom(maxv) pop_prandom(&rnd_seed, (maxv))
|
||||||
|
|
||||||
@@ -417,7 +417,7 @@ static void wall_rnd(int row, int col)
|
|||||||
* соседнего РЯДА и чужую раскладку (тихий баг вместо промаха). */
|
* соседнего РЯДА и чужую раскладку (тихий баг вместо промаха). */
|
||||||
int idx = (col >= 0 && col < 10) ? (row + 1) * 10 + col : -1;
|
int idx = (col >= 0 && col < 10) ? (row + 1) * 10 + col : -1;
|
||||||
uint8_t a, b, bg;
|
uint8_t a, b, bg;
|
||||||
unsigned long saved;
|
pop_rnd_t saved;
|
||||||
if (idx >= 0 && idx < WP_N && wp_ok[idx]) {
|
if (idx >= 0 && idx < WP_N && wp_ok[idx]) {
|
||||||
a = wp_a[idx]; b = wp_b[idx];
|
a = wp_a[idx]; b = wp_b[idx];
|
||||||
wr_mid_div = (uint8_t)(a & 1); wr_mid_off = (uint8_t)((a >> 1) & 7);
|
wr_mid_div = (uint8_t)(a & 1); wr_mid_off = (uint8_t)((a >> 1) & 7);
|
||||||
@@ -430,7 +430,7 @@ static void wall_rnd(int row, int col)
|
|||||||
saved = rnd_seed;
|
saved = rnd_seed;
|
||||||
/* Верхняя полоса (row<0): SDLPoP рисует её как ряд 2 комнаты сверху
|
/* Верхняя полоса (row<0): SDLPoP рисует её как ряд 2 комнаты сверху
|
||||||
* (drawn_row=2 для seed, draw_main_y=-1 для позиции) — seed по row 2. */
|
* (drawn_row=2 для seed, draw_main_y=-1 для позиции) — seed по row 2. */
|
||||||
rnd_seed = (unsigned long)(g_room + TBL_LINE[row < 0 ? 2 : row] + col);
|
pop_prandom_set(rnd_seed, g_room + TBL_LINE[row < 0 ? 2 : row] + col);
|
||||||
prandom(1); /* сброс */
|
prandom(1); /* сброс */
|
||||||
wr_mid_div = (uint8_t)prandom(1);
|
wr_mid_div = (uint8_t)prandom(1);
|
||||||
wr_mid_off = (uint8_t)prandom(4);
|
wr_mid_off = (uint8_t)prandom(4);
|
||||||
|
|||||||
@@ -25,6 +25,19 @@
|
|||||||
/* Экранный y нижней границы поля (ниже — статус-полоса + борт → клип). */
|
/* Экранный y нижней границы поля (ниже — статус-полоса + борт → клип). */
|
||||||
#define POP_CLIP_BOTTOM (POP_YOFF + POP_PLAYFIELD_H)
|
#define POP_CLIP_BOTTOM (POP_YOFF + POP_PLAYFIELD_H)
|
||||||
|
|
||||||
|
/* Спрайт целиком на экране И укладывается в 8-битные параметры noclip-
|
||||||
|
* примитивов libbgi? Условие входа в gfx_blit_cols_part_noclip: клипающий
|
||||||
|
* вариант платит ~5.6 К тактов подготовки на КАЖДЫЙ вызов, независимо от
|
||||||
|
* того, вылезает край или нет.
|
||||||
|
* static inline, а не чистый inline: SDCC оставляет мёртвое тело в каждом TU
|
||||||
|
* (memory sdcc_inline_codegen_findings), но здесь это ~30 байт на два TU —
|
||||||
|
* дешевле риска неразрешённой ссылки. */
|
||||||
|
static inline uint8_t pop_onscreen_cols(int x, int y, uint16_t w, uint16_t h)
|
||||||
|
{
|
||||||
|
return (uint8_t)(x >= 0 && y >= 0 && w < 256 && h < 256 &&
|
||||||
|
x + (int)w <= 320 && y + (int)h <= 256);
|
||||||
|
}
|
||||||
|
|
||||||
/* Загрузить 7 атласов фона. Звать ДО initgraph (как poc.c — atlas_load
|
/* Загрузить 7 атласов фона. Звать ДО initgraph (как poc.c — atlas_load
|
||||||
* трогает W3; проверенный порядок). Палитру pop_bg.pal грузит ПРИЛОЖЕНИЕ
|
* трогает W3; проверенный порядок). Палитру pop_bg.pal грузит ПРИЛОЖЕНИЕ
|
||||||
* после initgraph (gfx_pal_fload). 0 — OK, -1 — ошибка. */
|
* после initgraph (gfx_pal_fload). 0 — OK, -1 — ошибка. */
|
||||||
|
|||||||
@@ -100,6 +100,9 @@ void pop_guard_draw(void) __banked
|
|||||||
top = obj_y - (int)h + 1;
|
top = obj_y - (int)h + 1;
|
||||||
p = gfx_get_draw_page() & 1;
|
p = gfx_get_draw_page() & 1;
|
||||||
gfx_set_bank(GFX_BANK_SPRITE);
|
gfx_set_bank(GFX_BANK_SPRITE);
|
||||||
|
if (pop_onscreen_cols(bx, top + POP_YOFF, w, h))
|
||||||
|
gfx_blit_cols_part_noclip(bx, top + POP_YOFF, img, flip, 0, 0);
|
||||||
|
else
|
||||||
gfx_blit_cols_part(bx, top + POP_YOFF, img, flip, 0, 0);
|
gfx_blit_cols_part(bx, top + POP_YOFF, img, flip, 0, 0);
|
||||||
gfx_set_bank(GFX_BANK_NORMAL);
|
gfx_set_bank(GFX_BANK_NORMAL);
|
||||||
gx_l[p] = bx; gy_l[p] = top; gw_l[p] = w; gh_l[p] = h; gvalid[p] = 1;
|
gx_l[p] = bx; gy_l[p] = top; gw_l[p] = w; gh_l[p] = h; gvalid[p] = 1;
|
||||||
|
|||||||
@@ -15,9 +15,95 @@ int8_t pop_y_to_row(int16_t y)
|
|||||||
return (int8_t)((y + 60) / TILE_SIZEY % 4 - 1);
|
return (int8_t)((y + 60) / TILE_SIZEY % 4 - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t pop_prandom(unsigned long *seed, uint16_t maxv)
|
/* Уложить значение в диапазон 0..maxv. Вызовы оригинала — prandom(1),
|
||||||
|
* prandom(255), prandom(0xFF): делитель степень двойки, то есть маска, а не
|
||||||
|
* деление. __moduint на Z80 стоит заметно дороже проверки n & (n-1). */
|
||||||
|
static uint16_t pop_rnd_fit(uint16_t v, uint16_t maxv)
|
||||||
{
|
{
|
||||||
unsigned long s = *seed * 214013UL + 2531011UL;
|
uint16_t n = (uint16_t)(maxv + 1);
|
||||||
*seed = s;
|
if ((uint16_t)(n & (uint16_t)(n - 1)) == 0)
|
||||||
return (uint16_t)((uint16_t)(s >> 16) % (uint16_t)(maxv + 1));
|
return (uint16_t)(v & (uint16_t)(n - 1));
|
||||||
|
return (uint16_t)(v % n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if POP_PRANDOM_EXACT
|
||||||
|
/* s = s * 214013 + 2531011, 16-битными половинами.
|
||||||
|
*
|
||||||
|
* 214013 = 0x0003_43FD. Раскладываем произведение так, чтобы обошлось без
|
||||||
|
* __mullong: старшее слово результата не зависит от старших бит множителя
|
||||||
|
* выше 32-го, поэтому
|
||||||
|
* s*M = SL*ML (полные 32 бита)
|
||||||
|
* + ((SL*MH + SH*ML) mod 2^16) << 16 (только младшие 16 бит)
|
||||||
|
* где ML = 0x43FD, MH = 3. Единственное «широкое» умножение — SL*ML,
|
||||||
|
* 16x16 -> 32; собираем его из четырёх байтовых произведений (каждое
|
||||||
|
* помещается в 16 бит, поэтому это обычный 16-битный __mulint).
|
||||||
|
*
|
||||||
|
* Проверка эквивалентности — на хосте: те же значения, что давал
|
||||||
|
* unsigned long (см. комментарий к pop_rnd_t в pop_geom.h). */
|
||||||
|
uint16_t pop_prandom(pop_rnd_t *seed, uint16_t maxv)
|
||||||
|
{
|
||||||
|
uint16_t sl = seed->lo, sh = seed->hi;
|
||||||
|
uint8_t al = (uint8_t)sl, ah = (uint8_t)(sl >> 8);
|
||||||
|
uint16_t t0 = (uint16_t)((uint16_t)al * 0xFDu); /* al * ML_lo */
|
||||||
|
uint16_t t1 = (uint16_t)((uint16_t)al * 0x43u); /* al * ML_hi */
|
||||||
|
uint16_t t2 = (uint16_t)((uint16_t)ah * 0xFDu); /* ah * ML_lo */
|
||||||
|
uint16_t t3 = (uint16_t)((uint16_t)ah * 0x43u); /* ah * ML_hi */
|
||||||
|
uint16_t m = (uint16_t)(t1 + t2); /* средние байты */
|
||||||
|
uint16_t pl, ph;
|
||||||
|
|
||||||
|
ph = (uint16_t)(t3 + (m >> 8));
|
||||||
|
if (m < t1) ph = (uint16_t)(ph + 0x100u); /* перенос из t1+t2 */
|
||||||
|
pl = (uint16_t)(t0 + (uint16_t)(m << 8));
|
||||||
|
if (pl < t0) ph++; /* перенос в старшее слово */
|
||||||
|
|
||||||
|
/* (SL*MH + SH*ML) << 16 — только младшие 16 бит слагаемого. */
|
||||||
|
ph = (uint16_t)(ph + (uint16_t)(sl * 3u) + (uint16_t)(sh * 0x43FDu));
|
||||||
|
|
||||||
|
/* + 2531011 = 0x0026_9EC3 */
|
||||||
|
{
|
||||||
|
uint16_t nl = (uint16_t)(pl + 0x9EC3u);
|
||||||
|
ph = (uint16_t)(ph + 0x0026u);
|
||||||
|
if (nl < pl) ph++;
|
||||||
|
seed->lo = nl;
|
||||||
|
seed->hi = ph;
|
||||||
|
}
|
||||||
|
return pop_rnd_fit(ph, maxv);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* !POP_PRANDOM_EXACT — быстрый 16-битный генератор (по умолчанию) */
|
||||||
|
|
||||||
|
/* xorshift16 + шаг Вейля. Зачем не LCG оригинала: тот 32-битный, и даже
|
||||||
|
* без __mullong (см. ветку выше) стоит ~7 000 тактов на вызов — при двух
|
||||||
|
* вызовах за кадр это 3 % бюджета на генератор случайных чисел. Здесь
|
||||||
|
* умножений нет вовсе, только сдвиги и xor.
|
||||||
|
*
|
||||||
|
* Почему НЕ 8-битный RND Apple II (RNDseed := 5*RNDseed + 23 mod 256,
|
||||||
|
* Prince-of-Persia-Apple-II): он ещё быстрее, но у LCG по модулю 256
|
||||||
|
* младшие биты вырождены — бит 0 просто чередуется. Наши вызовы это
|
||||||
|
* видят: раскладка кладки берёт prandom(1) (один бит) и prandom(4), и
|
||||||
|
* вместо шума получилась бы правильная шахматка. У xorshift такой
|
||||||
|
* структуры нет, а стоит он примерно столько же.
|
||||||
|
*
|
||||||
|
* Шаг Вейля (+0x9E37, нечётный) нужен ещё и потому, что у чистого
|
||||||
|
* xorshift ноль — неподвижная точка, а сид кладки (номер комнаты + ряд +
|
||||||
|
* колонка) вполне может оказаться нулём.
|
||||||
|
*
|
||||||
|
* ПОСЛЕДСТВИЕ: последовательность отличается от SDLPoP. Значит другая
|
||||||
|
* (но статистически такая же) раскладка кладки и другие броски в боёвке.
|
||||||
|
* Если понадобится сверять картинку с оригиналом — собрать с
|
||||||
|
* -DPOP_PRANDOM_EXACT=1, там бит-в-бит тот же LCG. */
|
||||||
|
uint16_t pop_prandom(pop_rnd_t *seed, uint16_t maxv)
|
||||||
|
{
|
||||||
|
uint16_t s = seed->lo;
|
||||||
|
s ^= (uint16_t)(s << 7);
|
||||||
|
s ^= (uint16_t)(s >> 9);
|
||||||
|
s ^= (uint16_t)(s << 8);
|
||||||
|
s = (uint16_t)(s + 0x9E37u);
|
||||||
|
seed->lo = s;
|
||||||
|
seed->hi = 0; /* старшая половина не используется */
|
||||||
|
/* Отдаём СТАРШИЙ байт: у него нет короткопериодической структуры
|
||||||
|
* младших бит, а вызывающие берут от результата именно младшие. */
|
||||||
|
return pop_rnd_fit((uint16_t)(s >> 8), maxv);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -26,9 +26,31 @@ extern const int16_t pop_y_land[5];
|
|||||||
* над комнатой, 3 = под комнатой). */
|
* над комнатой, 3 = под комнатой). */
|
||||||
int8_t pop_y_to_row(int16_t y);
|
int8_t pop_y_to_row(int16_t y);
|
||||||
|
|
||||||
/* prandom (seg009:321) — 32-битный LCG оригинала. Сид ВНЕШНИЙ: у стен
|
/* prandom (порт seg009:321). Сид ВНЕШНИЙ: у стен (раскладка кладки) и у
|
||||||
* (раскладка кладки) и у анимаций тайлов (фаза факелов) свои
|
* анимаций тайлов (фаза факелов) свои последовательности, смешивать их
|
||||||
* последовательности, смешивать их нельзя. */
|
* нельзя.
|
||||||
uint16_t pop_prandom(unsigned long *seed, uint16_t maxv);
|
*
|
||||||
|
* ДВА генератора, выбор через POP_PRANDOM_EXACT (см. pop_geom.c):
|
||||||
|
* 0 (по умолчанию) — xorshift16 + шаг Вейля. Быстрый: ни одного
|
||||||
|
* умножения. Последовательность ОТЛИЧАЕТСЯ от SDLPoP, то есть другая
|
||||||
|
* раскладка кладки и другие броски в боёвке — статистически
|
||||||
|
* эквивалентные, но не те же самые;
|
||||||
|
* 1 — LCG оригинала (s*214013+2531011) бит-в-бит. Для сверки картинки
|
||||||
|
* с эталоном. Дороже: даже посчитанный 16-битными половинами (без
|
||||||
|
* __mullong, который один стоил 8 430 тактов — замер в MAME) он
|
||||||
|
* обходится в ~7 000 тактов на вызов.
|
||||||
|
*
|
||||||
|
* Состояние — две 16-битные половины, а не unsigned long: иначе SDCC на
|
||||||
|
* умножении зовёт __mullong. */
|
||||||
|
typedef struct { uint16_t lo, hi; } pop_rnd_t;
|
||||||
|
|
||||||
|
#ifndef POP_PRANDOM_EXACT
|
||||||
|
#define POP_PRANDOM_EXACT 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uint16_t pop_prandom(pop_rnd_t *seed, uint16_t maxv);
|
||||||
|
|
||||||
|
/* Сид из 16-битного числа (старшая половина = 0). */
|
||||||
|
#define pop_prandom_set(s, v) do { (s).lo = (uint16_t)(v); (s).hi = 0; } while (0)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -81,6 +81,11 @@ uint8_t pop_sword_draw(const pop_char_t *ch, const kframe *fr,
|
|||||||
top = obj_y + (int)SWORD_DY[sf] - (int)h + 1;
|
top = obj_y + (int)SWORD_DY[sf] - (int)h + 1;
|
||||||
if (flip) bx -= (int)w;
|
if (flip) bx -= (int)w;
|
||||||
gfx_set_bank(GFX_BANK_SPRITE);
|
gfx_set_bank(GFX_BANK_SPRITE);
|
||||||
|
/* Клинок почти всегда целиком на экране — noclip-путь (см. gfx.h:
|
||||||
|
* подготовка клипающего варианта стоит ~5.6 К тактов на вызов). */
|
||||||
|
if (pop_onscreen_cols(bx, top + POP_YOFF, w, h))
|
||||||
|
gfx_blit_cols_part_noclip(bx, top + POP_YOFF, simg, flip, 0, 0);
|
||||||
|
else
|
||||||
gfx_blit_cols_part(bx, top + POP_YOFF, simg, flip, 0, 0);
|
gfx_blit_cols_part(bx, top + POP_YOFF, simg, flip, 0, 0);
|
||||||
gfx_set_bank(GFX_BANK_NORMAL);
|
gfx_set_bank(GFX_BANK_NORMAL);
|
||||||
*rx = bx; *ry = top; *rw = w; *rh = h;
|
*rx = bx; *ry = top; *rw = w; *rh = h;
|
||||||
@@ -512,6 +517,9 @@ void kid_draw_splash(void)
|
|||||||
int top = obj_y - (int)h + 1;
|
int top = obj_y - (int)h + 1;
|
||||||
if (flip) bx -= (int)w;
|
if (flip) bx -= (int)w;
|
||||||
gfx_set_bank(GFX_BANK_SPRITE);
|
gfx_set_bank(GFX_BANK_SPRITE);
|
||||||
|
if (pop_onscreen_cols(bx, top + POP_YOFF, w, h))
|
||||||
|
gfx_blit_cols_part_noclip(bx, top + POP_YOFF, img, flip, 0, 0);
|
||||||
|
else
|
||||||
gfx_blit_cols(bx, top + POP_YOFF, img, flip);
|
gfx_blit_cols(bx, top + POP_YOFF, img, flip);
|
||||||
gfx_set_bank(GFX_BANK_NORMAL);
|
gfx_set_bank(GFX_BANK_NORMAL);
|
||||||
pop_clip_sprite(bx, w, top + POP_YOFF, (int)h);
|
pop_clip_sprite(bx, w, top + POP_YOFF, (int)h);
|
||||||
@@ -593,8 +601,14 @@ void kid_draw(void)
|
|||||||
if (skip >= (int)h) skip = (int)h; /* обрезан целиком */
|
if (skip >= (int)h) skip = (int)h; /* обрезан целиком */
|
||||||
}
|
}
|
||||||
gfx_set_bank(GFX_BANK_SPRITE); /* видео-ОЗУ, без теневой копии */
|
gfx_set_bank(GFX_BANK_SPRITE); /* видео-ОЗУ, без теневой копии */
|
||||||
if (skip < (int)h) /* +YOFF: центрирование */
|
if (skip < (int)h) { /* +YOFF: центрирование */
|
||||||
|
if (pop_onscreen_cols(bx, top + skip + POP_YOFF, w,
|
||||||
|
(uint16_t)((int)h - skip)))
|
||||||
|
gfx_blit_cols_part_noclip(bx, top + skip + POP_YOFF, img, flip,
|
||||||
|
(uint8_t)skip, 0);
|
||||||
|
else
|
||||||
gfx_blit_cols_part(bx, top + skip + POP_YOFF, img, flip, skip, 0);
|
gfx_blit_cols_part(bx, top + skip + POP_YOFF, img, flip, skip, 0);
|
||||||
|
}
|
||||||
gfx_set_bank(GFX_BANK_NORMAL);
|
gfx_set_bank(GFX_BANK_NORMAL);
|
||||||
/* клип СВОЕГО выхода за поле (падение ниже поля / верхний борт) —
|
/* клип СВОЕГО выхода за поле (падение ниже поля / верхний борт) —
|
||||||
* узко, только по прямоугольнику Кида и только при выходе. */
|
* узко, только по прямоугольнику Кида и только при выходе. */
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ static uint8_t trob_drawn[POP_ROOMTILES];
|
|||||||
|
|
||||||
/* prandom (seg009.c:321, 32-бит LCG) — нужен для стартовой фазы факела и
|
/* prandom (seg009.c:321, 32-бит LCG) — нужен для стартовой фазы факела и
|
||||||
* выбора следующего кадра пламени. */
|
* выбора следующего кадра пламени. */
|
||||||
static unsigned long trob_seed;
|
static pop_rnd_t trob_seed;
|
||||||
|
|
||||||
#define trob_prandom(maxv) pop_prandom(&trob_seed, (maxv))
|
#define trob_prandom(maxv) pop_prandom(&trob_seed, (maxv))
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* gfx_blit_cols_part_noclip — блит спрайта column-major БЕЗ клипа по экрану,
|
||||||
|
* с обрезкой сверху (как gfx_blit_cols_part). Пара к gfx_blit_cols_part
|
||||||
|
* ровно так же, как gfx_blit_part_noclip — к gfx_blit_part.
|
||||||
|
*
|
||||||
|
* Зачем: у персонажей PoP спрайт почти всегда целиком на экране, а общий
|
||||||
|
* gfx_blit_cols_part платит за проверку четырёх границ, пересчёт w/h/cx/sy и
|
||||||
|
* 16-битную арифметику независимо от того, вылезает край или нет — замер в
|
||||||
|
* MAME (roomtest, страж + клинок): 5 622 такта подготовки против 13 596 на
|
||||||
|
* сам accel-проход. Здесь та же колонка уходит в _bgi_blit_cols_raw без
|
||||||
|
* подготовки.
|
||||||
|
*
|
||||||
|
* Требования (проверяет ВЫЗЫВАЮЩИЙ, кода проверок здесь нет):
|
||||||
|
* - прямоугольник назначения целиком на экране;
|
||||||
|
* - ширина и высота кадра <= 255, y <= 255;
|
||||||
|
* - skip < высоты кадра.
|
||||||
|
* Не выполняется — зовите gfx_blit_cols_part.
|
||||||
|
*
|
||||||
|
* Флип, как и в gfx_blit_cols_part, бесплатный: подаём src с последней
|
||||||
|
* колонки и отрицательный страйд — accel сам кладёт её в левый x.
|
||||||
|
*
|
||||||
|
* W3-скобка ставится ЗДЕСЬ (код libbgi живёт в W1): из приложения,
|
||||||
|
* собранного с --w3, её вызывать нельзя — после _bgi_begin окно W3 занято
|
||||||
|
* видеобанком и код вызывающего исчезает из адресного пространства.
|
||||||
|
*/
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "../_bgi.h"
|
||||||
|
|
||||||
|
void gfx_blit_cols_part_noclip(int x, int y, const void *img, uint8_t flip,
|
||||||
|
uint8_t skip, uint8_t rows)
|
||||||
|
{
|
||||||
|
const uint8_t *p = (const uint8_t *)img;
|
||||||
|
uint8_t w = p[0]; /* ширина кадра (< 256) */
|
||||||
|
uint8_t fh = p[2]; /* высота кадра (< 256) */
|
||||||
|
const uint8_t *src = p + 4;
|
||||||
|
uint8_t h;
|
||||||
|
|
||||||
|
if (w == 0 || fh == 0 || skip >= fh) return;
|
||||||
|
h = (uint8_t)(fh - skip);
|
||||||
|
if (rows && rows < h) h = rows;
|
||||||
|
|
||||||
|
if (flip) src += (uint16_t)(w - 1) * fh; /* последняя колонка -> левый x */
|
||||||
|
src += skip;
|
||||||
|
|
||||||
|
_bgi_begin();
|
||||||
|
_bgi_blit_cols_raw(src, (uint8_t *)(_gfx_addr_base + (uint16_t)x),
|
||||||
|
w, h, flip ? -(int)fh : (int)fh, (uint8_t)y);
|
||||||
|
_bgi_end();
|
||||||
|
}
|
||||||
@@ -128,6 +128,16 @@ void gfx_blit_cols(int x, int y, const void *img, uint8_t flip);
|
|||||||
void gfx_blit_cols_part(int x, int y, const void *img, uint8_t flip,
|
void gfx_blit_cols_part(int x, int y, const void *img, uint8_t flip,
|
||||||
int skip, int rows);
|
int skip, int rows);
|
||||||
|
|
||||||
|
/* То же БЕЗ клипа по экрану — пара к gfx_blit_cols_part так же, как
|
||||||
|
* gfx_blit_part_noclip к gfx_blit_part. Вызывающий гарантирует: спрайт
|
||||||
|
* целиком на экране, ширина/высота кадра и y <= 255, skip < высоты.
|
||||||
|
* rows = 0 — «до низа кадра». Экономит ~5.6 К тактов подготовки на вызов
|
||||||
|
* (замер PoP roomtest: подготовка 5 622 против 13 596 на сам accel-проход),
|
||||||
|
* что для персонажа со спрайтом-компаньоном (клинок) даёт заметную долю
|
||||||
|
* кадра. */
|
||||||
|
void gfx_blit_cols_part_noclip(int x, int y, const void *img, uint8_t flip,
|
||||||
|
uint8_t skip, uint8_t rows);
|
||||||
|
|
||||||
/* Восстановить прямоугольник экрана из ОЗУ-копии (стирание спрайта/
|
/* Восстановить прямоугольник экрана из ОЗУ-копии (стирание спрайта/
|
||||||
* оверлея, нарисованного банком с битом 2: 0x54/0x5C). Всегда
|
* оверлея, нарисованного банком с битом 2: 0x54/0x5C). Всегда
|
||||||
* работает банком GFX_BANK_NORMAL независимо от gfx_set_bank;
|
* работает банком GFX_BANK_NORMAL независимо от gfx_set_bank;
|
||||||
|
|||||||
Reference in New Issue
Block a user