Резидент: pop_kid/pop_guard/main расколоты по частоте вызова (куча 6669 Б)
MEM-COLD3, шаги 3-5. Кандидаты выбирались не по размеру, а по тому, КТО зовёт: если единственный вызывающий уже в банке, код едет к нему и трамплин не появляется вовсе. * pop_kdraw.c -> БАНК 4 (к pop_cdraw.c): клинок в руке (pop_sword_draw) и блит спрайта chtab_2 по id (pop_kid_img_blit). Оба звал ровно pop_cdraw, теперь вызовы прямые — контракт и условие корректности в _pop_kdraw.h. * pop_kboot.c -> БАНК 8: загрузка атласов Кида и страницы kid_data.bin (раз за игру). Дескриптор страницы — _pop_kid.h. * pop_guard_cold.c -> БАНК 8: страж идёт за Кидом + вход стража в комнату. Оба зовёт roomtest_cold.c из того же банка — снова без трамплина. * Из main в банк 8 уехали pop_boot (загрузка ресурсов, палитра, первый экран), pop_level_switch (смена уровня) и pop_shutdown. FIRST_LEVEL переехал в pop_tune.h — константа нужна обеим половинам цикла. _CODE 23005 -> 19861, куча 3525 -> 6669 Б. Банк 8 — 6.5 из 16 КБ. tests-host 6/6 (в наборы добавлены eng_pop_kboot и eng_pop_guard_cold).
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
#include "pop_map.h" /* pop_clip_char_top (clip_char) */
|
||||
#include "pop_geom.h"
|
||||
#include "kid_data.h"
|
||||
#include "_pop_kid.h" /* дескриптор страницы данных (её грузит банк 8) */
|
||||
|
||||
pop_char_t Kid;
|
||||
|
||||
@@ -48,130 +49,6 @@ uint8_t kid_npages;
|
||||
* kid_set_seq / pop_char_set_seq. */
|
||||
static const uint16_t kid_seq_off[KID_NSEQ] = KID_SEQ_OFF_INIT;
|
||||
|
||||
/* 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;
|
||||
|
||||
int pop_sword_load(void)
|
||||
{
|
||||
if (atlas_load(&swordp, "KID\\sword.atl") != 0) return -1;
|
||||
sword_ok = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
gfx_w0_map(swordp.page);
|
||||
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;
|
||||
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);
|
||||
/* Клинок почти всегда целиком на экране — 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)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
int pop_kid_load(uint8_t npages)
|
||||
{
|
||||
static const char *const names[KID_MAXPAGES] = {
|
||||
"KID\\kid0.atl","KID\\kid1.atl","KID\\kid2.atl","KID\\kid3.atl","KID\\kid4.atl","KID\\kid5.atl",
|
||||
"KID\\kid6.atl","KID\\kid7.atl","KID\\kid8.atl","KID\\kid9.atl","KID\\kid10.atl","KID\\kid11.atl",
|
||||
"KID\\kid12.atl","KID\\kid13.atl","KID\\kid14.atl","KID\\kid15.atl","KID\\kid16.atl","KID\\kid17.atl",
|
||||
"KID\\kid18.atl","KID\\kid19.atl","KID\\kid20.atl","KID\\kid21.atl","KID\\kid22.atl","KID\\kid23.atl",
|
||||
"KID\\kid24.atl","KID\\kid25.atl","KID\\kid26.atl","KID\\kid27.atl"
|
||||
};
|
||||
uint8_t i;
|
||||
if (npages > KID_MAXPAGES) npages = KID_MAXPAGES;
|
||||
for (i = 0; i < npages; i++)
|
||||
if (atlas_load(&kidp[i], names[i]) != 0)
|
||||
return -1;
|
||||
kid_npages = npages;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pop_kid_free(void)
|
||||
{
|
||||
uint8_t i;
|
||||
for (i = 0; i < kid_npages; i++)
|
||||
atlas_free(&kidp[i]);
|
||||
kid_npages = 0;
|
||||
}
|
||||
|
||||
/* ---- Данные анимации в EMM-странице (kid_data.bin) ------------------ *
|
||||
* Таблица кадров (241*5 Б) и seqtbl (2310 Б) занимали 3.5 КБ в _CODE окна
|
||||
* W1/W2 — самый дефицитный ресурс. Теперь они лежат в отдельной EMM-
|
||||
@@ -182,7 +59,6 @@ void pop_kid_free(void)
|
||||
* Страница адресуется как у pop_level: данные с 0x100, ниже — ISR-стаб. */
|
||||
extern void _gfx_w0_isr(void);
|
||||
|
||||
#define KD_DATA_OFF 0x100u
|
||||
/* Адрес кадра i в замапленной странице. Считаем В uint16_t и кастуем ОДИН
|
||||
* раз: запись `(const uint8_t *)CONST + (uint16_t)i * 5u` SDCC 4.5 собирает
|
||||
* НЕВЕРНО — умножение делает в 16 битах (add hl,hl / add hl,bc), а потом
|
||||
@@ -214,7 +90,9 @@ static const uint8_t *kd_gframe_ptr(uint8_t i)
|
||||
* страницей данных — то есть внутри play_seq. */
|
||||
#define SEQ(cs) KD_SEQ(cs)
|
||||
|
||||
static uint8_t kdat_blk, kdat_page, kdat_ok;
|
||||
/* Страница данных анимации: заполняет ЗАГРУЗЧИК (pop_kboot.c, банк 8),
|
||||
* читает интерпретатор здесь. Не static — контракт в _pop_kid.h. */
|
||||
uint8_t kdat_blk, kdat_page, kdat_ok;
|
||||
|
||||
/* Текущий кадр (порт глобала cur_frame, seg006): 5 байт вместо таблицы.
|
||||
* Заполняется load_frame для АКТИВНОГО персонажа (Char), поэтому на
|
||||
@@ -273,42 +151,6 @@ int16_t pop_char_opp_dist(void)
|
||||
return distance;
|
||||
}
|
||||
|
||||
int pop_kid_data_load(const char *path)
|
||||
{
|
||||
uint8_t *pg = (uint8_t *)0xC000; /* страница мапится в W3 на время чтения */
|
||||
uint8_t saved_w3, blk;
|
||||
uint16_t stub;
|
||||
int fd, n;
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0) return -1;
|
||||
blk = mem_alloc_pages(1);
|
||||
if (!blk) { close(fd); return -1; }
|
||||
kdat_blk = blk;
|
||||
kdat_page = mem_get_page(blk, 0);
|
||||
|
||||
saved_w3 = _io_page_w3;
|
||||
sprinter_page_w3(kdat_page);
|
||||
n = read(fd, pg + KD_DATA_OFF, 16384 - KD_DATA_OFF);
|
||||
if (n >= (int)(KID_BIN_SEQTBL_OFF + KID_SEQTBL_LEN)) {
|
||||
stub = (uint16_t)&_gfx_w0_isr; /* ISR-стаб W0-страницы (как pop_level) */
|
||||
pg[0x38] = 0xC3;
|
||||
pg[0x39] = (uint8_t)(stub & 0xFF);
|
||||
pg[0x3A] = (uint8_t)(stub >> 8);
|
||||
pg[0x66] = 0xED; pg[0x67] = 0x45; /* RETN */
|
||||
}
|
||||
sprinter_page_w3(saved_w3);
|
||||
close(fd);
|
||||
if (n < (int)(KID_BIN_SEQTBL_OFF + KID_SEQTBL_LEN)) { mem_free_block(blk); return -1; }
|
||||
kdat_ok = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pop_kid_data_free(void)
|
||||
{
|
||||
if (kdat_ok) { mem_free_block(kdat_blk); kdat_ok = 0; }
|
||||
}
|
||||
|
||||
/* Прочитать кадр из ЛЮБОЙ таблицы страницы данных (у Kid и у стража они
|
||||
* разные: KID_BIN_FRAMES_OFF / KID_BIN_GFRAMES_OFF). Сама маппит страницу,
|
||||
* поэтому годится для вызова извне (pop_guard) — но НЕ в горячем цикле. */
|
||||
|
||||
Reference in New Issue
Block a user