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>
This commit is contained in:
2026-08-10 23:02:48 +03:00
parent 8beb66a48d
commit d154cb452c
2 changed files with 157 additions and 0 deletions
+3
View File
@@ -13,6 +13,9 @@
иначе) перечислены в `../docs/impl_diff.md` — новое туда же записью, а не
только комментарием в коде.
**НАЧИНАТЬ С `NEXT_SESSION.md`** — состояние окружения, задача на
ближайший заход и грабли последней сессии одним файлом.
**Что в работе сейчас — `TASKS_OPEN.md`** (доска задач: приоритеты, критерии
готовности); сделанное с протоколами замеров — `TASKS_CLOSED.md`. Баги —
`bug_list.md` (только ОТКРЫТЫЕ) и `bug_closed.md` (закрытые + разбор корней:
+154
View File
@@ -0,0 +1,154 @@
# Точка входа для следующей сессии (записано 2026-08-10, поздний вечер)
Файл для старта с чистого контекста: где всё стоит, что делать первым, какие
грабли уже собраны. Читать целиком — он короткий. Дальше по ссылкам:
`TASKS_OPEN.md` (доска), `bug_list.md` (открытые баги), `CLAUDE.md`.
---
## 1. Состояние репозитория
Всё закоммичено и запушено, `main` в синхроне с `origin/main`.
Последний коммит сессии — `8beb66a`.
`tests-host`: 5 наборов, все проходят (`[geom] 3144`).
`make size-check`: чисто, роста нет.
Коммиты за день, по порядку:
| хеш | что |
|---|---|
| `8cac51d` | убраны три последних `/63 %4` в `pop_room.c` |
| `72797e1` | инвентаризация ВСЕХ делений по `.asm`, три убраны |
| `8175121` | `scr_x` таблицей на весь диапазон, включая отрицательные |
| `fc0ede9` | байтовая таблица `x/7` вместо словарной (−1152 Б, на такт быстрее) |
| `e261a35` | замер в MAME: A/B со сборкой до правок, делений в горячем пути 0 |
| `67a4c71` | `BUG-TORCH-CHOMP-2`: застывший чомпер накрывался пламенем |
| `1b2111f` | L4-MIRROR шаги 1-2: зеркало в атласе + постановка тайла |
| `844fa6d` | L4-MIRROR шаг 4: прыжок сквозь зеркало и рождение тени |
| `8f0362f` | L4-MIRROR шаги 3 и 5 + **левый клип колонок в libbgi** |
| `3913f1e` | fore-проход поверх отражения (ноги/голова вылезали из арки) |
| `8beb66a` | заведён `FORE-DUP` с разбором |
## 2. Состояние окружения
- **MAME запущена** с образом уровня 4 (`FIRST_LEVEL=4`, `PROF_BORDER=1`).
Пересобрать образ: `make PROF_FLAGS="-DPROF_BORDER=1 -DFIRST_LEVEL=4" hdd`,
после этого MAME **обязан** полный рестарт (memory
`mame_hdd_rebuild_restart`). Загрузка: `keyseq d:{ENTER}`, потом
`keyseq roomtest{ENTER}`, ждать ~18 с.
- **SDLPoP пересобран с отладочной информацией** (`-O0 -g3`). pkg-config на
машине НЕТ, собирать так:
```
cd applications/PoP/SDLPoP/src
SDLC="-I/opt/homebrew/include -I/opt/homebrew/include/SDL2 -D_THREAD_SAFE"
SDLL="-L/opt/homebrew/lib -lSDL2main -lSDL2 -Wl,-framework,Cocoa \
-L/opt/homebrew/Cellar/sdl2_image/2.8.12_1/lib -lSDL2_image"
make -j8 CFLAGS="-std=gnu99 -D_DARWIN_C_SOURCE -O0 -g3 $SDLC" LIBS="$SDLL"
```
`-std=c99` НЕ работает (прячет `strncasecmp` на Darwin), нужен `gnu99`.
- **НЕ ПРИБРАНО:** в `SDLPoP/src/seg008.c` мой диагностический `fprintf`
с меткой `DBGMIRROR` в начале `add_objtable`. Убрать перед следующей
сборкой SDLPoP (или оставить — он гейтится по `obj_type == 1 || == 4`).
## 3. ЗАДАЧА НА ЗАВТРА: XOR/OR-блит через акселератор
Единственный открытый пункт `L4-MIRROR` — вид тени.
**Что установлено (замером, не гипотезой).** Тень уровня 4 в оригинале
рисуется ДВУМЯ блитами одного и того же спрайта Кида (seg008:1602):
```c
case 1: // shadow
add_midtable(obj_chtab, obj_id + 1, obj_xh, obj_xl, obj_y, blitters_2_or, 1);
add_midtable(obj_chtab, obj_id + 1, obj_xh, obj_xl + 1, obj_y, blitters_3_xor, 1);
```
OR на месте, XOR со сдвигом на пиксель вправо — XOR гасит совпавшее, остаются
края, отсюда «контурный» вид. Это ЗАМЫСЕЛ оригинала, не артефакт SDLPoP.
Подтверждено печатью из живого SDLPoP (`DBGMIRROR`): и тень, и отражение идут
из `chtab=2` (собственные спрайты Кида), `swordbits=0`, обычными кадрами:
```
type=4 chtab=2 img=40 dir=0 clipL=137 clipT=3 charid=0 frame=41 <- отражение
type=1 chtab=2 img=41 dir=0 clipL=137 clipT=3 charid=1 frame=42 <- тень
```
Различие между ними — ТОЛЬКО блиттер. Наша тень сейчас рисуется обычной
прозрачной копией, то есть выглядит вторым Кидом.
**Механизм на Sprinter** (`docs/part2/accelerator_doc.txt`, memory
`sprinter_accelerator` дополнена сегодня). Акселератор умеет блочные
AND/OR/XOR; операцию задаёт ОПКОД CPU между триггерами:
```asm
LD A,(DE) ; триггер чтения: блок из спрайта -> память акселератора
XOR (HL) ; триггер операции: блок XOR с тем, что по адресу приёмника
LD (HL),A ; триггер записи: результат обратно
```
Цена — «число байт / 7 МГц», попиксельного цикла CPU НЕТ. **Операция
ортогональна направлению**: горизонтальный/вертикальный режим (`LD L,L` /
`LD A,A`) выбирается отдельно и на операцию не влияет — то есть с нашими
column-major спрайтами ([[accel_vertical_copy]]) это работает так же, как
копия. Мнемоника: `XOR (HL)` даёт `A = A ^ (HL)`; «xor (hl),a» на Z80 нет.
### План
1. **Эксперимент в MAME на маленьком тесте в `tests/`, НЕ сразу в PoP.**
Примитив трогает ассемблерное ядро libbgi, проверять его надо в изоляции.
Цель: убедиться, что связка read-триггер / `XOR` / запись даёт ожидаемый
блок в вертикальном режиме.
2. **libbgi: `_bgi_blit_cols_op_raw`** — клон `_bgi_blit_cols_raw` (asm), где
write-триггер `LD (DE),A` заменён парой «`XOR (dst)` + `LD (dst),A`».
Наружу — `gfx_blit_cols_part_op(...)` с параметром операции
(COPY / OR / XOR), чтобы одним примитивом закрыть оба блиттера тени.
Побочно закрывается давний пункт `2d-1` из `docs/TODO.md`
(`putimage` с `XOR/OR/AND_PUT` до сих пор на попиксельном пути).
После правки libbgi — `make size-check` ОБЯЗАТЕЛЕН.
3. **PoP:** тень двумя блитами, OR на месте + XOR со сдвигом `+1` по X.
Место — ветка слота соперника в `pop_char_draw` (`pop_cdraw.c`), где уже
стоит выбор атласа и клип тени по `CHARID_1_SHADOW`.
4. **Смотреть на палитру глазами.** Тут предсказать нельзя: акселератор
XOR-ит ИНДЕКСЫ, а SDLPoP делает XOR в 24-битном RGB (`blit_xor`,
seg009:3190). DOS-оригинал (режим 13h) тоже XOR-ил индексы, то есть мы
будем БЛИЖЕ к DOS, чем SDLPoP, но конкретные цвета контура определит
раскладка нашей палитры (атласы перепакованы `pop_pack_kid.py`).
Может выйти и лучше, и мусорнее — это надо увидеть.
## 4. Остальное открытое
- **`FORE-DUP`** (`bug_list.md`) — передний слой тайла рисуется дважды при
перекрытии объектов (Кид+отражение всегда, Кид+соперник — весь ближний
бой). Картинку не портит, тратит такты. В оригинале невозможно: там
`redraw_at_char` только ПОМЕЧАЕТ тайлы. **Сначала замерить, потом чинить**
— окно клипа вместо перебора тайлов в своё время дало 3.2×.
- **`TORCH-ANIM-RIGHT`** (`bug_list.md`) — под запечённым пламенем могут
застыть не только челюсти чомпера, но и пики/меч/зелье справа от факела.
На уровнях 1-4 такого соседства нет.
- **`DIED-ON-BUTTON`** — не портирован `died_on_button` (seg007:776).
- Долгие: `BUG-SPIKE-1`, `BUG-CHOMP-JUMP-1` (оба низкий приоритет),
`L3-PASS`, `L3-COLOR`, `L1-SPEED`, `TUNE-1`.
- **Не проверено в MAME** из вчерашнего: отражение с fore-проходом и клип
тени слева (собрано и залито, но живьём не смотрели).
## 5. Грабли, собранные сегодня
- **Не оценивать железо по своей же memory-заметке.** Я заявил, что accel
умеет только копирование и XOR потребует ~25 % кадра на CPU — неверно,
поправил пользователь. Заметка описывала копирование, я принял её
неполноту за свойство железа.
- **lldb через FIFO — плохая идея.** Повторяющиеся `-o` при
`breakpoint command add` записываются НЕПОЛНЫМИ (берётся последний), а
оставшийся от неудачной попытки `script print(... lldb.frame ...)` уронил
lldb прямо в обработчике точки останова. Три прыжка пользователя ушли
впустую. **Работает надёжно:** добавить `fprintf(stderr, ...)` прямо в
SDLPoP, пересобрать (он собирается за секунды) и читать stdout. В дереве
уже есть такие метки (`DBG kidobj`).
- **`make` без `hdd` не обновляет образ MAME**, а `FIRST_LEVEL` живёт в
`roomtest.c` — при смене нужен `touch roomtest.c`. Я дважды сказал
«образ пересобран», когда он не был.
- Диапазон `obj_x` = **416..695** (посчитан из `kid_data.bin`: `dx` кадров
Кида −5..+10, стража −2..+10, плюс `render_dx ∈ {140,0,+140}`).
Пригодится всякий раз, когда нужна таблица по экранной X.