f2093e0d89
Жалоба: стойка неподвижных Кида и стража занимала больше полутора
кадровых периодов. Гипотеза «виноват __banked» ЗАМЕРОМ НЕ
ПОДТВЕРДИЛАСЬ: весь банковый вызов (трамплин + смена страницы + тело
pop_guard_tick + возврат) стоит 654 такта при бюджете кадра 430 000.
Как мерил (выборка PC бесполезна — мост MAME отвечает из фреймового
колбэка, все сэмплы падают в обработчик прерывания): breakpoint'ы MAME с
действием {printf totalcycles; g} на входах фаз главного цикла, разности
соседних меток = стоимость фазы. Плюс профилирование полосами бордюра
(make PROF=1, макрос PROF() в roomtest.c) для быстрого взгляда.
Замер комнаты 3 (Kid + страж), такты, кадр = 430 000:
fore поверх стража 431 964
fore поверх Kid 402 816 -> 78 % всей работы кадра
остальное 241 956
ИТОГО 1 076 736 = 2.5 кадра
Две причины, обе устранены:
1. Fore-слой рисовал ЦЕЛЫЕ тайлы, хотя существует ровно для того, чтобы
вернуть куски поверх спрайта — за его прямоугольником в видеопамяти и
так правильный фон. Введено ОКНО клипа (pop_fore_set_clip): спрайт
сообщает свой итоговый габарит (у Kid — с клинком, брызгами и
обрезкой clip_char), fore-проход режет по нему. Отсев трёхступенчатый:
тайл целиком (tile_in_fclip, до обращения к атласу), кусок по грубому
габариту (до gfx_w0_map — w/h лежат в EMM-странице), и точный клип в
blit_b. Для последнего добавлен libbgi-примитив
gfx_blit_part_noclip — пара к gfx_blit_noclip, но под-прямоугольник.
2. Оставшиеся 341 К после клипа оказались НЕ пикселями: 18 вызовов
pop_prandom за проход, ~10 700 тактов каждый (32-битный LCG:
__mullong ~8 000 + __moduint). Раскладка кладки тайла — чистая
функция (комната, ряд, колонка), то есть константа комнаты, а
wall_pattern пересчитывал её каждый кадр. Теперь кэшируются готовые
РЕШЕНИЯ (что рисовать и с каким смещением), 3 байта на тайл, сброс в
pop_room_draw. Порядок вызовов prandom воспроизведён один в один,
включая то, что значение метки берётся только при сработавшем условии.
Итог того же замера: fore поверх стража 37 464, поверх Kid 46 464,
кадр целиком 334 716 = 0.78 периода (было 2.5). Ускорение 3.2x, сами
fore-проходы — 10x.
Проверка отсутствия регрессии: попиксельная разность скриншотов комнат
1/2/3 до и после — отличаются ТОЛЬКО языки пламени факелов (анимация),
кладка и метки совпадают байт в байт.
Побочно: sprinter-cc научился пробрасывать -DNAME в sdcc.
Память: куча W2 1245 -> 996 Б (кэш кладки 120 Б), резидент W3 12 819 ->
14 512 (свободно 1872 Б — становится тесно), банк 1 236/16384.
ВНИМАНИЕ: make size-check показывает рост 7 программ, но эталон
docs/size_baseline.tsv отстал (последний раз принят в 484b18d, libbgi
менялась в 95c22be/c127a4b/64ce633) — к этой правке рост отношения не
имеет: новый модуль библиотеки в чужие программы не линкуется.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
107 lines
4.6 KiB
C
107 lines
4.6 KiB
C
/*
|
||
* pop_gdraw.c — ОТРИСОВКА стража. Резидент окна W3 (--w3 pop_gdraw.c).
|
||
*
|
||
* Почему отдельным файлом, а не внутри pop_guard.c: --w3 забирает МОДУЛЬ
|
||
* целиком (код+rodata в 0xC000), а состояние стража и его логика обязаны
|
||
* остаться в W1/W2 — из банка (guards.c) резидент W3 недостижим ни прямо,
|
||
* ни транзитивно (docs/layout_plan_v2.md §2 R2). Значит граница проходит
|
||
* ровно между «что рисует» (зовёт только главный цикл → резидент) и «что
|
||
* знает» (зовёт и банк → W1/W2).
|
||
*
|
||
* Писучие статики резидента (--dataseg не трогаем) остаются в обычном
|
||
* _DATA, то есть в W2 — сюда переезжает КОД, не данные.
|
||
*/
|
||
#include <graphics.h>
|
||
#include <gfx.h>
|
||
#include <sprite.h>
|
||
#include "pop_guard.h"
|
||
#include "pop_bg.h" /* POP_YOFF, pop_fore_over_char */
|
||
#include "kid_data.h"
|
||
|
||
/* ---- Атласы стража (chtab_5, база 750): g[id>>3], idx id&7 ---------- */
|
||
#define GUARD_PAGES 5
|
||
static atlas_t gp[GUARD_PAGES];
|
||
static uint8_t g_ok;
|
||
|
||
/* Прямоугольник последней отрисовки ПО СТРАНИЦЕ дабл-буфера (как у Kid:
|
||
* heal обязан стирать спрайт именно той страницы, в которую рисуем). */
|
||
static int gx_l[2], gy_l[2];
|
||
static uint16_t gw_l[2], gh_l[2];
|
||
static uint8_t gvalid[2];
|
||
|
||
int pop_guard_load(void)
|
||
{
|
||
static const char *const names[GUARD_PAGES] = {
|
||
"GUARD\\g0.atl", "GUARD\\g1.atl", "GUARD\\g2.atl",
|
||
"GUARD\\g3.atl", "GUARD\\g4.atl"
|
||
};
|
||
uint8_t i;
|
||
for (i = 0; i < GUARD_PAGES; i++)
|
||
if (atlas_load(&gp[i], names[i]) != 0) return -1;
|
||
g_ok = 1;
|
||
return 0;
|
||
}
|
||
|
||
void pop_guard_free(void)
|
||
{
|
||
uint8_t i;
|
||
if (!g_ok) return;
|
||
for (i = 0; i < GUARD_PAGES; i++) atlas_free(&gp[i]);
|
||
g_ok = 0;
|
||
}
|
||
|
||
void pop_guard_heal(void)
|
||
{
|
||
uint8_t p = gfx_get_draw_page() & 1;
|
||
if (gvalid[p]) gfx_heal(gx_l[p], gy_l[p] + POP_YOFF, gw_l[p], gh_l[p]);
|
||
gvalid[p] = 0;
|
||
}
|
||
|
||
void pop_guard_draw(void)
|
||
{
|
||
int obj_x, obj_y, top, fwd, fp_x, bx0 = 0, top0 = 0;
|
||
uint8_t page, idx, p, flip;
|
||
const uint8_t *img;
|
||
uint16_t w, h;
|
||
|
||
if (!g_ok || Guard.charid == 0 || pop_gframe.image == 255) return;
|
||
/* load_frame_to_obj (seg008:1728) — та же математика, что у Kid. */
|
||
fwd = (int)Guard.x + ((Guard.direction < 0) ? -pop_gframe.dx : pop_gframe.dx);
|
||
obj_x = (fwd << 1) - 116;
|
||
obj_y = (int)Guard.y + pop_gframe.dy;
|
||
if ((int8_t)(pop_gframe.flags ^ Guard.direction) >= 0) obj_x++;
|
||
fp_x = obj_x; /* ЛОГИЧЕСКАЯ X (до ×8/7) — футпринт */
|
||
obj_x = obj_x * 8 / 7; /* calc_screen_x_coord */
|
||
|
||
page = (uint8_t)(pop_gframe.image >> 3);
|
||
idx = (uint8_t)(pop_gframe.image & 7);
|
||
if (page >= GUARD_PAGES) return;
|
||
img = (const uint8_t *)atlas_image(&gp[page], idx);
|
||
gfx_w0_map(gp[page].page);
|
||
w = (uint16_t)(img[0] | ((uint16_t)img[1] << 8));
|
||
h = (uint16_t)(img[2] | ((uint16_t)img[3] << 8));
|
||
if (w && h) {
|
||
int bx = obj_x;
|
||
flip = (uint8_t)(Guard.direction >= 0); /* спрайты нарисованы влево */
|
||
if (flip) bx -= (int)w;
|
||
top = obj_y - (int)h + 1;
|
||
bx0 = bx; top0 = top;
|
||
p = gfx_get_draw_page() & 1;
|
||
gfx_set_bank(GFX_BANK_SPRITE);
|
||
gfx_blit_cols_part(bx, top + POP_YOFF, img, flip, 0, 0);
|
||
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;
|
||
}
|
||
gfx_w0_unmap();
|
||
/* Окно fore-слоя = прямоугольник стража (см. pop_bg.c, fclip_on):
|
||
* без него проход возвращал ЦЕЛЫЕ тайлы — 432 К тактов на кадр. */
|
||
pop_fore_set_clip(bx0, top0 + POP_YOFF, (int)w, (int)h);
|
||
/* Передние грани тайлов ПОВЕРХ стража: в оригинале персонаж — запись
|
||
* midtable, а foretable рисуется после всех midtable-записей, поэтому
|
||
* колонна/кромка пола перекрывает ЛЮБОГО персонажа, не только Kid
|
||
* (seg008:690 draw_tile_fore). Без этого страж виден сквозь передний
|
||
* столб. */
|
||
if (w && h)
|
||
pop_fore_over_char(fp_x, obj_y, w, h, Guard.direction);
|
||
}
|