PoP roomtest (план v2, шаг 1): таблицы анимации Kid -> EMM-страница

kid_frames (241*5) и kid_seqtbl (2310) занимали 3.5 КБ в _CODE окна W1/W2
— самого дефицитного ресурса.  Теперь они лежат в kid_data.bin (отдельная
EMM-страница), которая маппится в W0 ровно на время play_seq — один
map/unmap за тик, в фазе тика, без конфликта с атласом в W0.

Ключ к переносу — порт load_frame/cur_frame (seg006): оригинал раз за тик
копирует кадр в структуру, и вся коллизия/отрисовка читает ЕЁ, а не
таблицу.  У нас так же: kid_cur_dx/kid_cur_flags (их дёргают несколько раз
за кадр из pop_map) и kid_draw читают cur_frame — 5 байт в _DATA.
kid_seq_off (230 Б) оставлен резидентным: его читает kid_set_seq из
pop_ctrl/pop_map, вне страницы.

pop_extract_kid_data.py теперь пишет и kid_data.bin, и урезанный
kid_data.h (тип kframe, размеры, смещения в бинаре, kid_seq_off).

Замер: _CODE 24881 -> 21634 (-3247 Б), куча W2 2076 -> 5315 Б, W3 без
изменений.  Проверено в MAME скриптом: стойка -> бег (кадр 8) -> стоп,
перемещение и коллизия у кромки работают, спрайт рисуется.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-29 19:15:16 +03:00
parent a5773ab654
commit 8e33cd07bc
7 changed files with 134 additions and 462 deletions
@@ -15,6 +15,8 @@ import os
import re
SRC = "/Users/alex/Projects/DIY/Z80/Sprinter/C-Compiler/applications/PoP/SDLPoP/src"
OUT_BIN_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "poc", "res", "kid")
OUT = os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "roomtest", "kid_data.h")
SEQTBL_BASE = 0x196E
@@ -74,22 +76,21 @@ def main():
f.write("#ifndef KID_DATA_H\n#define KID_DATA_H\n#include <stdint.h>\n\n")
f.write("#define SEQTBL_BASE 0x%04Xu\n\n" % SEQTBL_BASE)
# frame table
f.write("/* {image, dx, dy, flags, sword}; chtab = sword>>6 (0=kid). */\n")
# frame table и seqtbl В .h НЕ ПОПАДАЮТ — они уезжают в kid_data.bin
# (EMM-страница): 3.5 КБ в _CODE окна W1/W2 нам дороже, чем один
# gfx_w0_map за тик. См. applications/PoP/docs/layout_plan_v2.md §5.1.
f.write("/* {image, dx, dy, flags, sword}; chtab = sword>>6 (0=kid).\n"
" * САМА таблица кадров и seqtbl лежат в kid_data.bin (EMM-\n"
" * страница); здесь только тип и размеры. */\n")
f.write("typedef struct { uint8_t image; int8_t dx, dy; "
"uint8_t flags, sword; } kframe;\n")
f.write("#define KID_NFRAMES %d\n" % len(frames))
f.write("static const kframe kid_frames[KID_NFRAMES] = {\n")
for (img, dx, dy, fl, sw) in frames:
dxs = dx - 256 if dx > 127 else dx
dys = dy - 256 if dy > 127 else dy
f.write(" {%3d,%4d,%4d,0x%02X,0x%02X},\n" % (img, dxs, dys, fl, sw))
f.write("};\n\n")
f.write("#define KID_SEQTBL_LEN %d\n\n" % len(seqtbl))
# seqtbl bytes
f.write("#define KID_SEQTBL_LEN %d\n" % len(seqtbl))
f.write("static const uint8_t kid_seqtbl[KID_SEQTBL_LEN] = {\n")
f.write(emit_c(seqtbl) + "\n};\n\n")
# раскладка kid_data.bin (смещения ОТ НАЧАЛА ДАННЫХ страницы)
f.write("/* Раскладка kid_data.bin: кадры (KID_NFRAMES*5), затем seqtbl. */\n")
f.write("#define KID_BIN_FRAMES_OFF 0\n")
f.write("#define KID_BIN_SEQTBL_OFF %d\n\n" % (len(frames) * 5))
# seq offsets
f.write("#define KID_NSEQ %d\n" % len(offs))
@@ -97,6 +98,15 @@ def main():
f.write(emit_c(offs, 8, "0x%04X") + "\n};\n\n")
f.write("#endif\n")
# kid_data.bin: кадры (по 5 байт) + seqtbl подряд
os.makedirs(OUT_BIN_DIR, exist_ok=True)
binpath = os.path.join(OUT_BIN_DIR, "kid_data.bin")
with open(binpath, "wb") as b:
for (img, dx, dy, fl, sw) in frames:
b.write(bytes((img & 0xFF, dx & 0xFF, dy & 0xFF, fl & 0xFF, sw & 0xFF)))
b.write(bytes(seqtbl))
print(f"kid_data.bin: {len(frames)*5 + len(seqtbl)} Б -> {binpath}")
print(f"kid_data.h: {len(frames)} кадров, seqtbl {len(seqtbl)} Б, "
f"{len(offs)} seq-оффсетов -> {OUT}")