Files
Sprinter-SDCC/applications/PoP/roomtest/CLAUDE.md
T
snark13 d154cb452c NEXT_SESSION.md: точка входа для старта с чистого контекста
Одним файлом: состояние репозитория (11 коммитов дня с хешами), состояние
окружения (образ MAME на уровне 4; как пересобрать SDLPoP с -g без
pkg-config — нужен -std=gnu99, иначе прячется strncasecmp; неприбранный
диагностический fprintf DBGMIRROR), задача на завтра и открытые пункты.

Задача на завтра — XOR/OR-блит через акселератор.  Записано всё, что
установлено замером: тень уровня 4 рисуется ДВУМЯ блитами одного спрайта
Кида (seg008:1602, blitters_2_or + blitters_3_xor со сдвигом на пиксель),
обе сущности идут из chtab=2 — различие только в блиттере.  Механизм на
Sprinter из accelerator_doc.txt: операцию задаёт опкод CPU между
триггерами, цена «байт / 7 МГц», операция ортогональна направлению.
Открытый вопрос честно помечен: акселератор XOR-ит ИНДЕКСЫ, а SDLPoP — RGB;
как это ляжет на нашу перепакованную палитру, надо увидеть.

Отдельным разделом — грабли дня, чтобы не повторять: не оценивать железо по
своей же memory-заметке (ошибся с accel, поправил пользователь); lldb через
FIFO ненадёжен, вместо него fprintf прямо в SDLPoP; make без hdd не
обновляет образ MAME.

Ссылка на файл добавлена в roomtest/CLAUDE.md, который грузится сам.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 23:02:48 +03:00

101 lines
9.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# roomtest — правила подпроекта
Живой прототип порта PoP: комната 1 уровня 1 (композиция тайлов в рантайме)
+ Kid (анимация seqtbl, управление, коллизия, падение, зацеп, fore-окклюзия,
проваливающиеся полы). Действуют правила корневого `CLAUDE.md` и
`applications/PoP/CLAUDE.md`.
**Главное правило (из `../CLAUDE.md`): `../SDLPoP/src/` — источник истины.**
Перед реализацией ЛЮБОЙ функции и по ЛЮБОМУ вопросу «как в оригинале» —
СНАЧАЛА прочитай соответствующий код SDLPoP и портируй по нему; не пиши
по памяти и не угадывай константы/порядок слоёв. Расхождение с SDLPoP =
по умолчанию баг у нас; ОСОЗНАННЫЕ расхождения (платформа/бюджет требуют
иначе) перечислены в `../docs/impl_diff.md` — новое туда же записью, а не
только комментарием в коде.
**НАЧИНАТЬ С `NEXT_SESSION.md`** — состояние окружения, задача на
ближайший заход и грабли последней сессии одним файлом.
**Что в работе сейчас — `TASKS_OPEN.md`** (доска задач: приоритеты, критерии
готовности); сделанное с протоколами замеров — `TASKS_CLOSED.md`. Баги —
`bug_list.md` (только ОТКРЫТЫЕ) и `bug_closed.md` (закрытые + разбор корней:
перед заведением нового бага грепни там по симптому); сырые формулировки
пользователя с прогонов — `bugs_level1.md` / `bugs_level2.md`. План
следующих уровней — `../docs/levels_plan.md`.
Правило разнесения: как только задача/баг закрыт — запись целиком переезжает
в `TASKS_CLOSED.md` / `bug_closed.md`, а в открытом файле остаётся ссылка.
Открытые файлы обязаны читаться целиком за раз.
## Сборка и запуск
```
make # собрать roomtest.exe (упаковав ассеты через toolchain/)
make run # exe + EXTRA_DATA на дискету + запуск MAME (см. корневой док)
make -C tests-host # модульные тесты движка под ucsim_z80 (секунды, без MAME)
```
Логику, которую можно проверить без железа, покрывать в `tests-host/`
(обвязка — `testkit/`, там же почему прогон именно под z80). MAME остаётся
для отрисовки, банков, таймингов и клавиатуры.
`MEMORY=small`, `--gfx 256`. Ассеты (`pop_env0..4.atl`, `pop_wall.atl`,
`pop_fore.atl`, `kid0..27.atl`, `kid.pal`) генерируются python-скриптами
`../toolchain/` — Makefile дёргает их сам при изменении. Данные комнаты —
`room1_data.h` (fg=foretable code, bg=backtable modifier); данные анимации
Kid — `kid_data.h` (генерится `pop_extract_kid_data.py`).
## Проверка в MAME
Только через `toolchain/mame_interactive.py` или MCP-мост `mame-z80`
(см. memory `mame_autotest`, `mame_mcp_bridge`, `mame_hdd_test_disk`).
Эталон комнаты — `../toolchain/1.1-2.png`. Пересобрал HDD-образ → MAME
ОБЯЗАН полный рестарт (`mame_hdd_rebuild_restart`).
Отладочные тумблеры в живой сессии (`roomtest.c`): **SPACE** — вкл/выкл
дабл-буфер (в однобуфере видно баги рисования без мерцания-через-кадр);
**1** — заморозить кадр, **2** — продолжить (разбор позы/окклюзии);
**ESC** — выход. Читы (`pop_cheat.h`): **K** — убить стража, **I**
бессмертие, **S** — выдать меч, **Shift+L** — следующий уровень,
**+/−** — обход комнат (`ROOMNAV`). Уровень грузится по номеру
(`pop_level_load_num`), на диске лежат все 15.
## Модули
| Файл | Роль (порт SDLPoP) |
|------|--------------------|
| `roomtest.c` | Главный цикл: дабл-буфер (2 страницы + флип на vsync), tick→draw порядок слоёв. |
| `pop_tile.c/.h` | Общие ЛИСТЬЯ слоя фона — РЕЗИДЕНТ W1: блит куска атласа, чтение тайла комнаты, `tile_table`, метка «фон трогали вот здесь» (`pop_cd_touch`). Резидент потому, что их зовут обе половины слоя фона из разных банков, а `const`-таблицы банка из чужого банка не видны. |
| `pop_bg.c/.h` | ГОРЯЧАЯ половина слоя фона (банк 2, каждый кадр): fore-окклюзия поверх ЛЮБОГО персонажа (`seg003` redraw_at_char/char2 — один проход на всех Char), оверлеи кромки, кладка стены (`wall_pattern`), клип. `pop_bg.h` — публичный API ВСЕГО слоя (обеих половин). |
| `pop_room.c` | ХОЛОДНАЯ половина слоя фона (банк 7, раз на комнату/событие): полная отрисовка комнаты (`seg008` draw_tile), точечные перерисовки тайлов (пики, ворота, кнопки, дверь уровня, дрожащие плиты), падающие куски loose (`seg007` mob), загрузка атласов. |
| `_pop_bg.h` | Внутренний контракт между половинами: общее состояние + три тонкие `__banked`-обёртки. Там же расписано, почему рез прошёл именно здесь и сколько стоит. |
| `pop_kid.c/.h` | Анимация/движение: интерпретатор seqtbl `play_seq` + frame_table (`seg006`), окна `Char` (loadkid/loadshad), атласы Кида. |
| `pop_cdraw.c/.h` | ОТРИСОВКА персонажей — одна на всех Char (порт `add_kid_to_objtable`/`add_guard_to_objtable`, `seg008:22F0/2324`): спрайт, clip_char, брызги, клинок, heal; + полоса HP и палитры соперника. Там же ПРОПУСК неизменившегося кадра (`pop_char_skip_mask`): персонаж, у которого с прошлой отрисовки этой страницы ничего не изменилось и фон под ним не трогали, не перерисовывается вовсе. |
| `pop_ctrl.c/.h` | Управление: диспетчер `control()` (`seg005`) + ввод `read_user_control` (`seg006`) поверх `<kbd_raw.h>`. |
| `pop_map.c/.h` | Коллизия с картой + физика падения/приземления/стены + loose-полы (`seg005/006/007`). |
## Порядок слоёв в кадре (важно для окклюзии)
`roomtest.c` каждый тик рисует в СКРЫТУЮ страницу: `pop_char_heal` (стереть
прошлый кадр — по слоту на персонажа) → `kid_tick``pop_phys_tick`
`pop_loose_tick` (loose СЛОЙ ФОНА — ДО персонажей, чтобы они были поверх
плиты) → `pop_char_draw`/`pop_char_fore` для соперника и Кида (кто позже —
тот поверх, порядок задаёт обход тайлов) → `pop_room_clip_borders` (чистка
бортов, гейт по флагу — только в кадрах падения) → флип на vsync.
Отрисовка персонажа ОДНА на всех (`pop_cdraw.c`): слот выбирает окно
`Char`, набор атласов и heal-прямоугольники, всё остальное общее — как два
одинаковых входа `add_*_to_objtable` в оригинале.
**Дабл-буфер:** у каждой страницы своя видео-ОЗУ и теневая ОЗУ-копия; heal
берёт чистый фон из копии ТОЙ страницы, в которую рисуем. Любой динамический
элемент (Kid, loose-плита, падающий кусок) обязан чистить свой прошлый кадр
на КАЖДОЙ из двух страниц — иначе остаток виден через кадр как **мерцание**.
Это типовой источник багов «остаётся кусочек» (см. memory
`pop_fall_debug_baseline`).
## Ключевые memory
`pop_kid_plan`, `pop_hang_state`, `pop_fore_layer`, `pop_fall_debug_baseline`,
`accel_vertical_copy`, `kbd_raw_fifo_drain`, `pop_check_sdlpop_first`.