# Идеи и вопросы «на подумать» (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 Б копирования КАЖДЫЙ кадр, что мимо бюджета на порядок.

**Варианты, которые надо будет взвесить (не сейчас):**
1. **Предпечённые перевёрнутые атласы.**  Второй набор кадров
   Кида/стража, перевёрнутый по вертикали ещё в `pop_pack_kid.py` (там уже
   есть транспонирование — добавляется одной строкой).  Рантайм: выбор
   набора + зеркальная арифметика Y.  Память: ещё ~28 страниц EMM при
   бюджете ~3.3 МБ — не проблема.  Похоже, самый дешёвый по тактам путь.
2. **Фон рисовать с обратным Y** — для row-major тайлов строка остаётся
   непрерывным accel-прогоном, меняется только адрес назначения; цена —
   вызов на строку вместо вызова на тайл.  Померить, прежде чем закладывать.
3. **Аппаратная помощь** — до проектирования проверить, есть ли у
   акселератора направление копирования «вниз» (обратный инкремент адреса);
   если есть, вариант 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
кадров подряд прошли без единого превышения.  То есть эффект реальный и
измеримый, просто в тесте он был наведён извне.

**Что проверить.**
1. Есть ли у драйвера мыши (RST 30h) функция «выключить/включить» —
   разобрать список из 14 обёрток; если нет явной, посмотреть, что делает
   «hide cursor» и снимает ли она обработчик.
2. Сколько тактов реально стоит одно мышиное прерывание на нашем железе
   (замер: breakpoint на входе ISR + totalcycles, при движении мыши).
3. Не ломает ли отключение выход в DSS: состояние обязано
   восстанавливаться при `exit`, включая аварийный (atexit).

**Почему не сейчас.** Выигрыш проявляется только когда игрок реально
двигает мышью, то есть в норме его нет; а риск оставить систему без мыши
после выхода — заметный.  Делать после того, как закроем стражей и
займёмся бюджетом всерьёз (там же, где батчинг кроссбанковых вызовов и
возможный возврат `pop_bg` в резидент `--w3`).
