3bcaf51221
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
188 lines
15 KiB
Markdown
188 lines
15 KiB
Markdown
# Точка входа для следующей сессии (обновлено 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](TASKS_OPEN.md#mem-cold2) п.3, если снова понадобится место.
|
||
|
||
Ощущенческий хвост прежний — [GRAB-KBD-TIMING](BUGS_OPEN.md#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`. В них же
|
||
записаны два правила, которые линкер НЕ проверяет:
|
||
|
||
1. **Банковый модуль не маппит W3** — он в этом окне и исполняется (через W0
|
||
можно). Поэтому `pop_level_read_file` и `pop_kid_data_load` остались в
|
||
резиденте: первый же вызов из банка улетал в halt по мусору.
|
||
2. **Прямые вызовы между модулями одного банка** корректны только пока они
|
||
действительно в одном банке; меняешь раскладку в 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`** (memory `mame_build_clean_rule`). После пересборки
|
||
образа MAME **обязана** пройти полный рестарт (memory
|
||
`mame_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](BUGS_OPEN.md#fore-dup)** — передний слой тайла рисуется дважды
|
||
при перекрытии объектов. Картинку не портит, тратит такты. Сначала
|
||
замерить, потом чинить.
|
||
- **TORCH-ANIM-RIGHT** — под запечённым пламенем могут застыть пики/меч/зелье
|
||
справа от факела.
|
||
- **[GATE-FORE-KID](BUGS_OPEN.md#gate-fore-kid)** — Кид в проёме ворот виден
|
||
поверх решётки; портирован только шовный случай.
|
||
- Долгие: `BUG-SPIKE-1`, `BUG-CHOMP-JUMP-1`, `L3-COLOR`, `L1-SPEED`, `TUNE-1`.
|
||
- **Вид тени (OR+XOR) отложен** до готовности всех уровней —
|
||
[`../docs/shadow_render.md`](../docs/shadow_render.md).
|
||
|
||
## 7. Грабли, собранные 2026-08-12 (вечер/ночь)
|
||
|
||
- **`return 1;` из ветки у SDCC 4.5 может не положить единицу в A** — функция
|
||
возвращает мусор. Годами маскировался (в A случайно оставался ненулевой
|
||
байт) и вылез ровно на `__banked`. Репро — `docs/bugs/sdcc-z80-ret-const-lost/`,
|
||
обход — один выход через переменную. Аудит всех `.asm` roomtest: других
|
||
мест нет.
|
||
- **Банковый модуль не может маппить 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`).
|