Документы отстали от кода: PORT_PLAN писал «PoC не начат», хотя играется весь уровень 1, а четыре плана были исполнены целиком. - PORT_PLAN: таблица статусов по разделам; фазы 0-3 сделаны, 4-6 нет; риски §8 п.1/п.3 закрыты, п.2 переформулирован под реальный движок (спрайтовый движок для персонажей не используется, лимит «21 спрайт» неприменим), п.4 — найдено расхождение таймингов: оригинал считает логический кадр за 5 тиков при BASE_FPS=60 (83.3 мс, в бою 100 мс), а мы ждём три vsync (60 мс) — игра идёт примерно на 39 % быстрее эталона. - levels_plan.md — новый: машинерия перехода между уровнями, второй тайлсет (palace), потабличные различия и читы SDLPoP, которые окупаются сразу. Инвентарь тайлов снят прямо с res200N.bin: уровень 2 не требует ни одного нового ассета и ни одной новой механики. - roomtest/TASKS.md — новый: доска текущих задач с критериями готовности. - Удалены как исполненные и перекрытые кодом: clip_char_plan, double_buffer_plan, loose_floors_plan, size_optimization_plan. Его §8 (замеры скорости отрисовки) не был перекрыт — перенесён в layout_plan_v2 §9, чтобы не потерять цифры. - KID_PLAN / gates_spikes_plan — шапки «реализовано, оставлено справочником»; room_model_plan — «S1 сделан, остальное не срочно». - docs/README.md стал индексом с отметками актуальности. - ideas_backlog: зелье переворота экрана — оригинал переворачивает готовый буфер построчно, спрайты не трогает; по данным уровней тип 4 встречается только на уровне 9, до него механика не нужна. - examples/scroll: ссылка на удалённый план вела к неверному факту «теневая копия одна — общая»; заменено на подтверждённое «у каждой страницы своя». Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
8.0 KiB
Идеи и вопросы «на подумать» (PoP)
Не план работ, а список того, что осознанно отложено: каждая запись — гипотеза с причиной, по которой её стоит проверить, и с тем, что мешает сделать это прямо сейчас.
Зелье «переворот экрана» (upside-down)
Вопрос пользователя (2026-08-01). Тайлы фона у нас лежат строками, а
кадры Кида/стражей — КОЛОНКАМИ (transpose_cols в pop_pack_kid.py, ради
бесплатного горизонтального зеркала). Значит вертикальный переворот для
персонажей заметно сложнее, чем для фона. Верно; но прежде чем это чинить,
надо знать три факта.
Факт 1 — когда оно вообще нужно. Зелье переворота — тип 4
(proc_get_object, seg006.c:1885 → toggle_upside()). Скан всех уровней
по данным (res200N.bin, тайл 10 = зелье, тип в backtable): тип 4
встречается впервые на уровне 9 (две склянки), и больше нигде. Тип 3
(перо, медленное падение) — уровень 7. То есть до уровня 9 механика не
нужна вообще, и «на первом этапе просто не реализовывать» — не компромисс,
а точное соответствие данным уровней 1..8.
Факт 2 — что именно делает оригинал. НЕ переворачивает спрайты.
flip_screen (seg009.c:1042) → flip_not_ega (seg009.c:1023) меняет
местами СТРОКИ готового offscreen-буфера (top↔bottom, порядок пикселей
внутри строки не трогает — это вертикальное зеркало, не поворот на 180°).
Вызывается вокруг отрисовки кадра целиком (seg003.c:296..301): перевернул
буфер → дорисовал → перевернул обратно. Так что в оригинале это
post-process всего экрана, и вопрос «как перевернуть колоночный спрайт»
там просто не возникает.
Факт 3 — почему нам этот приём не подходит как есть. У нас нет шага «готовый offscreen → экран»: рисуем прямо в видеостраницу, а heal берёт фон из ОЗУ-копии этой же страницы. Переворот всей страницы построчно — это 320×192 Б копирования КАЖДЫЙ кадр, что мимо бюджета на порядок.
Варианты, которые надо будет взвесить (не сейчас):
- Предпечённые перевёрнутые атласы. Второй набор кадров
Кида/стража, перевёрнутый по вертикали ещё в
pop_pack_kid.py(там уже есть транспонирование — добавляется одной строкой). Рантайм: выбор набора + зеркальная арифметика Y. Память: ещё ~28 страниц EMM при бюджете ~3.3 МБ — не проблема. Похоже, самый дешёвый по тактам путь. - Фон рисовать с обратным Y — для row-major тайлов строка остаётся непрерывным accel-прогоном, меняется только адрес назначения; цена — вызов на строку вместо вызова на тайл. Померить, прежде чем закладывать.
- Аппаратная помощь — до проектирования проверить, есть ли у
акселератора направление копирования «вниз» (обратный инкремент адреса);
если есть, вариант 1 может и не понадобиться. Смотреть
docs/new/06-accel.mdиdocs/reference/accel_r.txt.
Почему не сейчас. Уровни 1..8 этого не требуют, а к уровню 9 у нас уже будет ответ на вопрос «сколько стоит кадр» (задачи CLIP-1/T-2) — без него выбирать между вариантами выше бессмысленно.
Заменить генератор псевдослучайных чисел
Сейчас стоит LCG оригинала, шаг на ассемблере (~1 020 тактов), бит-в-бит
совместимый с SDLPoP. Есть более дешёвые Z80-генераторы (86–148 тактов),
но потолок выигрыша — 2 814 тактов за кадр, 0.65 %, и он растворяется в
обёртках вызова. Тексты процедур, разбор качества и порядок действий —
prng_alternatives.md. Первый шаг там не про генератор: слить приведение
к диапазону в ту же asm-процедуру, чтобы на вызов был один call, а не три.
Отключать мышь на время игры
Гипотеза. Мышь на Sprinter — источник прерываний (обёртки RST 30h,
см. memory mouse_api). Игре она не нужна вообще: управление —
raw-клавиатура (<kbd_raw.h>), которую мы и так забираем у DSS целиком.
Значит каждое мышиное прерывание за кадр — украденные такты в бюджете,
который у нас и без того занят на 86 %.
Откуда взялось (2026-07-30). При замере бюджета по 100 кадрам три кадра выбились до 552–647 К тактов (1.28–1.51 кадра) при типичных 371 К. Причиной оказалось движение мыши на ХОСТЕ: при неподвижной мыши 225 кадров подряд прошли без единого превышения. То есть эффект реальный и измеримый, просто в тесте он был наведён извне.
Что проверить.
- Есть ли у драйвера мыши (RST 30h) функция «выключить/включить» — разобрать список из 14 обёрток; если нет явной, посмотреть, что делает «hide cursor» и снимает ли она обработчик.
- Сколько тактов реально стоит одно мышиное прерывание на нашем железе (замер: breakpoint на входе ISR + totalcycles, при движении мыши).
- Не ломает ли отключение выход в DSS: состояние обязано
восстанавливаться при
exit, включая аварийный (atexit).
Почему не сейчас. Выигрыш проявляется только когда игрок реально
двигает мышью, то есть в норме его нет; а риск оставить систему без мыши
после выхода — заметный. Делать после того, как закроем стражей и
займёмся бюджетом всерьёз (там же, где батчинг кроссбанковых вызовов и
возможный возврат pop_bg в резидент --w3).