Задача на завтра: smoke-прогон уровня 9 + регресс уровней 1-8 (слой фона теперь весь спрашивает pop_upside). Записаны раскладка кода по банкам после разгрузки (куча 902 -> 5795 Б), устройство переворота с замерами, два правила, которые линкер не проверяет (банк не маппит W3; прямые вызовы только внутри одного банка), новые тайминги моста и грабли дня — включая баг SDCC с потерянным `return 1` и разницу логических/экранных координат при перевороте. Открытый риск на проверку: имена kid10_v.atl…kid27_v.atl — 9 символов до точки при DSS 8.3.
15 KiB
Точка входа для следующей сессии (обновлено 2026-08-12, ночь)
Файл для старта с чистого контекста: где всё стоит, что делать первым, какие
грабли уже собраны. Читать целиком — он короткий. Дальше по ссылкам:
TASKS_OPEN.md (доска), BUGS_OPEN.md (открытые баги), CLAUDE.md.
1. СЛЕДУЮЩАЯ ЗАДАЧА: smoke-прогон уровня 9 и регресс 1-8
Переворот (зелье инверсии) доведён и принят пользователем 2026-08-12:
фон, персонажи, клинок, полоса HP, факелы, окклюзия передних граней и
переходы между комнатами работают зеркально; чит U и зелёное зелье идут
одним путём. Осталось пройти сам уровень 9 живьём и убедиться, что на
уровнях 1-8 ничего не поехало (весь слой фона теперь спрашивает pop_upside).
Что смотреть на прогоне в первую очередь:
- вис на кромке и спуск в перевёрнутом виде — это новый срез
clip_char(он режет кадр СНИЗУ вместо верха); тело должно уходить за кладку, а не ложиться поверх неё; - бой со стражем перевёрнутым — у соперника свой зеркальный набор
(
g*_v.atl), а тень рисуется атласами Кида, отдельных файлов ей не надо; - зелье в другой комнате — переворот перерисовывает не комнату, а чистит факелы и отражает страницу; проверить, что после смены комнаты и возврата фон остаётся чистым.
Открытый риск (пока не проявился, но проверить осознанно): имена зеркальных
атласов kid10_v.atl…kid27_v.atl — 9 символов до точки, а DSS работает
по 8.3. Если часть кадров Кида в перевёрнутом виде рисуется обычными
(неперевёрнутыми) — это оно; лечится переименованием в pop_pack_kid.py
(например k10v.atl) плюс те же имена в pop_kboot.c.
Дальше по плану — уровни 10/11 (по анализу нового в них нет) и MEM-COLD2 п.3, если снова понадобится место.
Ощущенческий хвост прежний — GRAB-KBD-TIMING, разбирается после готовности всех уровней (решение пользователя).
2. Состояние репозитория
Всё закоммичено, main чист. tests-host: 6 наборов проходят
([char] 65, [geom] 3144, [grab] 55, [mouse] 17, [phys] 1727,
[wall] 1).
Коммиты 2026-08-12 (сверху — свежие):
| хеш | что |
|---|---|
7f5c99b |
чистка факелов при перевороте — в СТАРОЙ системе координат |
c9aa6d9 |
переворот: не перерисовывать комнату, стирать только запечённое пламя |
948d8f0 |
переворот: перерисовка вместо отражения, переключение на границе кадра, clip_char |
5b6664f |
зеркальные кадры персонажей — готовыми файлами *_v.atl |
c4dd2c7 |
отсев тайлов по fore-окну — в ЛОГИЧЕСКИХ координатах |
7551906 |
L9-INVERT II.4: персонажи рисуются перевёрнутыми |
055d6d7 |
чтение файлов нельзя выносить в банк (код банка сам живёт в W3) |
147cb18 |
резидент: pop_kid/pop_guard/main расколоты по частоте вызова |
5d31ed0 |
резидент: pop_redraw + холодная половина pop_level в банки |
f541c0a |
клип поля при перевороте (мусор в бортах) |
Уровни 1-8 проходятся, уровень 9 ждёт прогона.
3. Раскладка кода после разгрузки резидента
Куча выросла 902 → 5795 Б (_CODE 25 628 → 20 569). Кандидатов выбирали
не по размеру, а по тому, КТО зовёт: если единственный вызывающий уже в
банке, код едет к нему и трамплин не появляется вовсе.
| банк | модули |
|---|---|
| 1 | guards.c |
| 2 | pop_bg.c |
| 3 | pop_map.c |
| 4 | pop_cdraw.c + pop_kdraw.c (клинок, блит по id — прямые вызовы) |
| 5 | pop_ctrl.c |
| 6 | pop_trob.c |
| 7 | pop_room.c + pop_redraw.c |
| 8 | roomtest_cold.c + pop_level_cold.c + pop_kboot.c + pop_guard_cold.c |
Внутренние контракты: _pop_level.h, _pop_kid.h, _pop_kdraw.h. В них же
записаны два правила, которые линкер НЕ проверяет:
- Банковый модуль не маппит W3 — он в этом окне и исполняется (через W0
можно). Поэтому
pop_level_read_fileиpop_kid_data_loadостались в резиденте: первый же вызов из банка улетал в halt по мусору. - Прямые вызовы между модулями одного банка корректны только пока они действительно в одном банке; меняешь раскладку в Makefile — проверь пары.
4. Переворот (L9-INVERT) — как он устроен
- Флаги:
pop_upside_want(пишут зелье, смерть Кида, читU) иpop_upside(читают все слои). Переключение — ОДНО место, начало кадра, вместе сpop_flip_screen. Оригиналу этого не нужно (он рисует в offscreen неперевёрнутым и зеркалит на выводе) — расхождение записано в../docs/impl_diff.md. - Сам переворот: чистка запечённого пламени (
pop_torch_wipe, 0..4 тайла) + отражение страницы акселератором + прямая копия во вторую. 904 247 тактов = 2.1 кадра. Вариант с полной перерисовкой комнаты (какredraw_screenв оригинале) стоил 6 245 477 = 14.5 кадра — отвергнут. - Спрайты персонажей: column-major, вертикальное зеркало для них не
бесплатно, поэтому второй набор файлов делает упаковщик (
vflip_colsвpop_pack_kid.py). Загрузка одной страницы с диска — 970 003 такта, разворот той же страницы в рантайме — 12 645 582; поэтому файлы.pop_vflip_load_all()идемпотентна и зовётся откуда угодно (сейчас — старт и смена уровня, если этоPOP_UPSIDE_LEVEL; задел под загрузку в интро). - Координаты: у окна fore-клипа ДВЕ пары границ — экранная
(
pop_t_fclip_y0/y1, по ней режется блит) и логическая (pop_t_fclip_ly0/ly1, по ней идут ВСЕ отсевы тайлов). Смешать их — и передние грани перестают перекрывать персонажа.
5. Состояние окружения
- MAME запущена с мостом, на HDD — roomtest с
LEVEL=9. Стартовый уровень теперь параметр сборки:make LEVEL=9(дефолт 9; для «настоящей» игры —LEVEL=1), константа объявлена вpop_tune.h. - ПРАВИЛО ПОЛЬЗОВАТЕЛЯ: перед сборкой образа —
make clean, потомmake, потомmake hdd(memorymame_build_clean_rule). После пересборки образа MAME обязана пройти полный рестарт (memorymame_hdd_rebuild_restart): остановка черезexitв дебаггере, затемrun_bridge.sh. - Тайминги моста (правило пользователя, длиннее не ждать; они же в
docs/mame-autotest.md§10): стартrun_bridge.sh→ 6 с →go→ 8 с →keyseq d:{ENTER}+keyseq roomtest{ENTER}→ 5 с → программа работает. Весь цикл перезапуска ~20 секунд. - MCP-инструменты
mame-z80работают (в отличие от прошлой сессии):press_key,screenshot,read_logical_memory,debugger_command. Профилирование —bpset <addr>+print totalcycles, затемoutи сноваprint totalcycles; кадр = 430 000 тактов. - Адреса состояния смещаются после КАЖДОЙ пересборки — брать из
.sprinter-cc-roomtest/roomtest.mapзаново. Банковые функции лежат по0x0008XXXX; для брейкпоинта брать младшие 16 бит (0xC912и т.п.). - SDLPoP собран с отладочной информацией (
-O0 -g3). - НЕ ПРИБРАНО: в
SDLPoP/src/seg008.cдиагностическийfprintfс меткойDBGMIRRORв началеadd_objtable.
6. Остальное открытое
- FORE-DUP — передний слой тайла рисуется дважды при перекрытии объектов. Картинку не портит, тратит такты. Сначала замерить, потом чинить.
- DIED-ON-BUTTON —
died_on_button(seg007:776) не портирован. - TORCH-ANIM-RIGHT — под запечённым пламенем могут застыть пики/меч/зелье справа от факела.
- GATE-FORE-KID — Кид в проёме ворот виден поверх решётки; портирован только шовный случай.
- Долгие:
BUG-SPIKE-1,BUG-CHOMP-JUMP-1,L3-COLOR,L1-SPEED,TUNE-1. - Вид тени (OR+XOR) отложен до готовности всех уровней —
../docs/shadow_render.md.
7. Грабли, собранные 2026-08-12 (вечер/ночь)
return 1;из ветки у SDCC 4.5 может не положить единицу в A — функция возвращает мусор. Годами маскировался (в A случайно оставался ненулевой байт) и вылез ровно на__banked. Репро —docs/bugs/sdcc-z80-ret-const-lost/, обход — один выход через переменную. Аудит всех.asmroomtest: других мест нет.- Банковый модуль не может маппить W3 (см. §3). Симптом — halt по мусору в 0xC4xx на первом же вызове.
- Логические и экранные координаты при перевороте — разные системы. Три
бага подряд из одного корня: отсев тайлов по fore-окну,
clip_char, чистка факелов. Правило: всё, что считается ИЗ РЯДА комнаты (63*row + …), — логическое; всё, что режет блит, — экранное. - Пламя факела ЗАПЕКАЕТСЯ в ОЗУ-копию (heal'а у него нет, следующий кадр
накрывает предыдущий непрозрачно). Любое отражение/копирование страницы
тащит его с собой — перед такими операциями звать
pop_torch_wipe. pop_upsideпереключать в середине кадра нельзя: остаток кадра рисуется зеркально поверх ещё неперевёрнутой картинки, и запечённые транзиенты остаются в комнате навсегда.- Перенос константы между файлами — потерянное значение. При выносе
FIRST_LEVELвpop_tune.hзаписал дефолт 1, и игра молча стартовала с первого уровня вместо девятого; заметил пользователь. Теперь это параметр сборки (make LEVEL=N). make -C tests-hostне пересобирает по изменению заголовков — при правке прототипов (особенно__banked) обязателенmake -C tests-host clean, иначе стаб и вызывающий разъезжаются по ABI и тест врёт.
Грабли 2026-08-10/11
- Не оценивать железо по своей же memory-заметке. Дважды поправлял
пользователь: accel умеет не только копирование (есть AND/OR/XOR) и умеет
работать с обычной RAM, а не только с видео (
docs/part2/accelerator_doc.txt: «supports data blocks transfer of RAM and Video-RAM», не поддерживает только ROM и FastRAM). Проверять по драйверу MAME и докам, а не по памяти. - lldb через FIFO — плохая идея. Работает надёжно:
fprintf(stderr, …)прямо в SDLPoP, пересборка (секунды) и чтение stdout. - Доска отставала от кода на три задачи. Перед планированием — сверять
TASKS_OPEN.mdс кодом грепом, а не верить записи. - Диапазон
obj_x= −416..695 (посчитан изkid_data.bin).