65797af44c
ТАЙМИНГИ. Пользователь заметил, что заклинание Джафара не совпадает с музыкой. Замер в MAME (breakpoint на вспышке + курсор трека): реплика m53 успевала ДОИГРАТЬ до молнии, хотя по шкале оригинала между входом Джафара и заклинанием ровно 822 тика = 13,7 с. Причина: пейсинг считал НАШИ ожидания vsync, а не прошедшее время. Полноэкранная копия страницы стоит больше кадра луча, и разница копилась. Кадровые прерывания для счёта не годятся (теряются в di-окнах акселератора), поэтому часами стал насос CBL: он идёт от расхода буфера железом, 85,4 Гц, и ему безразлично, чем занят главный цикл. Один тик оригинала = 57/40 тика насоса (0,07 % ошибки, без деления в кадре). Когда звук выключен, работает прежний путь по кадрам луча. После фикса замер даёт 229 блоков остатка m53 против расчётных 233 — расхождение 47 мс. АРХИВЫ. Группы ресурсов сложены в PBA1: kid (58 атласов), pv (78), shadow (32), bg (25), title (20), guard (10), vizier (5), skel (4). Было 232 файла на образе, стало 8 архивов плюс шесть палитр и таблицы уровней. При цене `open` 51,4 мс против 32,6 мс за чтение 16 КБ это возвращает секунды на каждой загрузке. - pop_pack_arc.py печатает индексы элементов константами (ARC_<GRP>_<FILE>), порядок задаёт Makefile, а серии код проверяет статически; - atlas_load разделён: atlas_attach принимает уже прочитанную страницу, поэтому чтение из архива не дублирует проверку магии и подготовку W0; - имена архивов живут в pop_arc.c и адресуются номером. Строковый литерал лежит в rodata своего банка, и указатель на него из другого банка после переключения W3 показывает на чужие данные — ровно так падала загрузка фона (поймано брейкпоинтом на puts). Проверено в MAME: заставка, интро и уровень 1 собираются из архивов, Тень грузит все 32 страницы. Host-тесты: 15 наборов. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
148 lines
8.0 KiB
C
148 lines
8.0 KiB
C
/*
|
||
* pop_kdraw.c — отрисовка ИЗ АТЛАСОВ КИДА, которая нужна только слою
|
||
* персонажей: клинок в руке (add_sword_to_objtable, seg006:1798) и блит
|
||
* спрайта chtab_2 по его id (полоса HP). БАНК 4, вместе с pop_cdraw.c.
|
||
*
|
||
* Почему здесь. Оба вызова приходят РОВНО из pop_cdraw.c и больше ниоткуда
|
||
* (проверено грепом), а лежали они в pop_kid.c — то есть в резиденте W1,
|
||
* съедая ~1.2 КБ дефицитного окна ради кода, который зовёт сосед по банку.
|
||
* Раз модуль в том же банке 4 — вызовы прямые, без трамплина: объявления в
|
||
* _pop_kdraw.h НЕ помечены __banked (см. там же условие корректности).
|
||
*
|
||
* Загрузка атласа меча (pop_sword_load) осталась публичной и __banked: её
|
||
* зовёт инициализация, один раз за игру.
|
||
*/
|
||
#include <graphics.h>
|
||
#include <gfx.h>
|
||
#include <sprite.h>
|
||
#include "pop_kid.h"
|
||
#include "_pop_kdraw.h"
|
||
#include "pop_pace.h" /* pop_beam_sample — счёт кадров по лучу */
|
||
#include "pop_guard.h" /* CHARID_* — клинок есть и у стража */
|
||
#include "pop_bg.h" /* POP_YOFF — вертикальное центрирование */
|
||
#include "_pop_draw.h" /* pop_onscreen_cols */
|
||
#include "pop_map.h" /* pop_upside — зелье инверсии */
|
||
#include "pop_vflip.h" /* зеркальные страницы атласов */
|
||
#include "kid_data.h"
|
||
|
||
/* chtab_0 «меч в руке» (add_sword_to_objtable, seg006:1798): клинок —
|
||
* ОТДЕЛЬНЫЙ спрайт поверх персонажа, один и тот же атлас у Кида и у стража.
|
||
* Таблица sword_tbl (kid_data.h, генерится из seg006) индексируется
|
||
* sword_frame = кадр.sword & 0x3F; id 255 = клинка в этом кадре нет.
|
||
* Смещения — в ЭКРАННЫХ пикселях: оригинал применяет их ПОСЛЕ
|
||
* calc_screen_x_coord, x — «вперёд» по направлению персонажа. */
|
||
static const uint8_t SWORD_ID[KID_NSWORD] = KID_SWORD_ID_INIT;
|
||
static const int8_t SWORD_DX[KID_NSWORD] = KID_SWORD_DX_INIT;
|
||
static const int8_t SWORD_DY[KID_NSWORD] = KID_SWORD_DY_INIT;
|
||
static atlas_t swordp;
|
||
static uint8_t sword_ok;
|
||
|
||
/* Меч приходит УЖЕ ЗАГРУЖЕННЫМ: его читает pop_kid_load из того же архива,
|
||
* что и остальные страницы Кида, — открывать архив второй раз ради одного
|
||
* элемента незачем (`open` дороже самого чтения). */
|
||
int pop_sword_take(const atlas_t *a) __banked
|
||
{
|
||
if (!a) return -1;
|
||
swordp = *a;
|
||
sword_ok = 1;
|
||
return 0;
|
||
}
|
||
|
||
/* Зеркальная страница меча (зелье инверсии) — пара к pop_sword_take. */
|
||
int pop_sword_vflip_take(const atlas_t *m) __banked
|
||
{
|
||
if (!sword_ok) return 0;
|
||
return pop_vflip_take(&swordp, m);
|
||
}
|
||
|
||
/* add_sword_to_objtable (seg006:1798). Клинок рисуется, если персонаж в
|
||
* кадрах подъёма/ножен, ИЛИ держит меч, ИЛИ это ЖИВОЙ страж (у стража меч
|
||
* в руке всегда, даже пока sword ещё sheathed). Страницу W0 оставляем
|
||
* замапленной — вызывающий делает gfx_w0_unmap сам. */
|
||
uint8_t pop_sword_draw(const pop_char_t *ch, const kframe *fr,
|
||
int obj_x, int obj_y,
|
||
int *rx, int *ry, uint16_t *rw, uint16_t *rh)
|
||
{
|
||
uint8_t sf, id, flip;
|
||
const uint8_t *simg;
|
||
uint16_t w, h;
|
||
int bx, top;
|
||
|
||
if (!sword_ok) return 0;
|
||
if (!((ch->frame >= 229 && ch->frame < 238) ||
|
||
ch->sword != 0 ||
|
||
((ch->charid == CHARID_2_GUARD || ch->charid == CHARID_4_SKELETON) &&
|
||
ch->alive < 0)))
|
||
return 0;
|
||
sf = (uint8_t)(fr->sword & 0x3F);
|
||
if (sf == 0 || sf >= KID_NSWORD) return 0;
|
||
id = SWORD_ID[sf];
|
||
if (id == 0xFF) return 0;
|
||
|
||
simg = (const uint8_t *)atlas_image(&swordp, id);
|
||
{ /* ПЕРЕВОРОТ: зеркальная копия страницы меча (см. pop_vflip.h) */
|
||
uint8_t vpg = swordp.page;
|
||
if (pop_upside) {
|
||
uint8_t f = pop_vflip_page(vpg);
|
||
if (f) vpg = f;
|
||
}
|
||
gfx_w0_map(vpg);
|
||
}
|
||
w = (uint16_t)(simg[0] | ((uint16_t)simg[1] << 8));
|
||
h = (uint16_t)(simg[2] | ((uint16_t)simg[3] << 8));
|
||
if (!w || !h) return 0;
|
||
|
||
flip = (uint8_t)(ch->direction >= 0);
|
||
bx = obj_x + ((ch->direction < 0) ? -(int)SWORD_DX[sf] : (int)SWORD_DX[sf]);
|
||
top = obj_y + (int)SWORD_DY[sf] - (int)h + 1;
|
||
/* Та же формула, что у самого персонажа (pop_cdraw.c): верх кадра
|
||
* [top, top+h) уезжает в 192 - top - h. */
|
||
if (pop_upside) top = POP_PLAYFIELD_H - top - (int)h;
|
||
if (flip) bx -= (int)w;
|
||
/* Поле комнаты — 192 строки (reset_obj_clip, seg006:0507): клинок
|
||
* обрезается по нему так же, как сам персонаж. Без этого у падающего
|
||
* в пропасть скелета меч оставался виден НИЖЕ поля, на полосе HP. */
|
||
{
|
||
int skip = 0, rows = (int)h;
|
||
if (top < 0) { skip = -top; rows -= skip; top = 0; }
|
||
if (top + rows > 192) rows = 192 - top;
|
||
if (rows <= 0) return 0;
|
||
gfx_set_bank(GFX_BANK_SPRITE);
|
||
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
|
||
/* Клинок почти всегда целиком на экране — noclip-путь (см. gfx.h:
|
||
* подготовка клипающего варианта стоит ~5.6 К тактов на вызов). */
|
||
if (pop_onscreen_cols(bx, top + POP_YOFF, w, (uint16_t)rows))
|
||
gfx_blit_cols_part_noclip(bx, top + POP_YOFF, simg, flip,
|
||
(uint8_t)skip, (uint8_t)rows);
|
||
else
|
||
gfx_blit_cols_part(bx, top + POP_YOFF, simg, flip, skip, rows);
|
||
gfx_set_bank(GFX_BANK_NORMAL);
|
||
*rx = bx; *ry = top; *rw = w; *rh = (uint16_t)rows;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
/* Блит спрайта chtab_2 (атласы Kid) по ЕГО id, левым верхним углом в (x,y).
|
||
* Нужен полосе HP: она рисует id 216/217 из тех же атласов, что и кадры
|
||
* Кида, а держать вторую копию раскладки в pop_cdraw ни к чему. */
|
||
void pop_kid_img_blit(uint8_t image, int x, int y)
|
||
{
|
||
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
|
||
uint8_t pg = (uint8_t)(image >> 3); /* раскладка атласов: 8 спрайтов/стр. */
|
||
uint8_t idx = (uint8_t)(image & 7);
|
||
const uint8_t *img;
|
||
uint16_t w, h;
|
||
if (pg >= kid_npages) return;
|
||
img = (const uint8_t *)atlas_image(&kidp[pg], idx);
|
||
gfx_w0_map(kidp[pg].page);
|
||
w = (uint16_t)(img[0] | ((uint16_t)img[1] << 8));
|
||
h = (uint16_t)(img[2] | ((uint16_t)img[3] << 8));
|
||
/* Атласы Кида хранятся COLUMN-MAJOR (transpose_cols в pop_pack_kid —
|
||
* ради бесплатного горизонтального флипа), поэтому блит обязан быть
|
||
* колоночным. Row-major gfx_blit_noclip выдал бы марку транспонированной.
|
||
* noclip БЕЗ проверки (CLIP-1): единственный вызывающий — полоса HP
|
||
* (pop_cdraw.c), у неё координаты фиксированные и заведомо на экране. */
|
||
if (w && h) gfx_blit_cols_part_noclip(x, y, img, 0, 0, 0);
|
||
gfx_w0_unmap();
|
||
}
|