Files
Sprinter-SDCC/applications/PoP/roomtest/pop_state.c
T
snark13 2bdaf0f4cd P2a: coll_scan переведён на 8 бит и снят с IX — минус 3 486 в коллизиях
Разбор pop_phys_tick (61 266 тактов на НЕПОДВИЖНОМ Киде) зондами по
звеньям kid_phys:

  check_collisions      33 846   55 %
  хвост (spike/spiked/chomped/knock/leave/save)  16 188   26 %
  check_press            4 140
  check_action           2 622
  loadkid_and_opp        2 148
  determine_col          1 182
  fall_accel+fall_speed    582
  bump_into_opponent       198

Внутри check_collisions: три coll_row (сканирование рядов) — 23 256,
подготовка окна 3 240, set_char_collision 1 788, обход пересечения 5 562.

Сгенерированный asm coll_scan показал 322 такта Z80 на ПУСТУЮ колонку
(с wait-state'ами 773 — ровно замеренные 750), из них 137 (43 %) —
обращения через IX-фрейм, и четыре 16-битные операции на колонку там,
где от колонки зависит один операнд.

Сделано:

  1. вся арифметика цикла в 8 битах.  scan_left = x_bump[col+5] + TILE_MIDX
     при колонках окна -2..11 лежит в [37, 233], wall_dl в [-1, 10],
     wall_dr в [0, 13] — суммы в [36, 246], переполниться не могут.
     Границы персонажа приводятся к 8 битам с клипом, и клип точен: порог
     ниже 37 означает «условие не выполнится никогда», выше 233 — «всегда».
  2. dst снят с IX-фрейма в file-scope (scan_dst).

ОТРИЦАТЕЛЬНЫЙ РЕЗУЛЬТАТ, не повторять: предпосчёт таблиц порогов по типу
стены (thr_l[6]/thr_r[6] на кадр) сделал ХУЖЕ — check_collisions
33 846 -> 36 570, синяя фаза +10 269.  Колонок в окне четыре-пять, а типов
стен пять: кэша получилось больше, чем потребления.

Проверено на кодогенерации: register на параметре-указателе SDCC 4.5 z80
проигнорировал (asm байт в байт), а file-scope дал 607 -> 454 такта.

Итог: check_collisions 33 846 -> 30 360 (-10 %), работа кадра
657 882 -> 654 990.  Крупной статьи в физике нет: остаток размазан по
десятку честных проверок.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 12:06:43 +03:00

198 lines
14 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* pop_state.c — данные, которые читают ОБЕ стороны банковой границы.
*
* Зачем отдельный модуль: sprinter-cc собирает банковый модуль с
* --dataseg BANKn, поэтому ВСЯ его писучая память (глобалы, file-static,
* статики внутри функций) линкуется В СТРАНИЦУ БАНКА и снаружи не читается
* — проверено на .map (2026-07-30: глобал банкового модуля лёг по
* 0x0001C000, а не в _DATA). Значит любую переменную, к которой нужен
* доступ и из банка, и снаружи, надо держать в НЕбанковом модуле — здесь.
*
* Модуль обязан оставаться в W1/W2: его читают и главный цикл, и банки.
*/
#include "pop_state.h"
#include "pop_ctrl.h" /* объявления шины control_* (живёт здесь) */
uint8_t pop_mob_busy;
uint8_t pop_loose_landed;
/* Кусок loose УШЁЛ ВНИЗ из комнаты (порт хвоста move_loose, seg007:1126:
* mob_down_a_row переносит его в комнату снизу; у нас симуляция там не
* продолжается, и приземление разбирает главный цикл). 0 = нет, иначе
* col+1; комната, ИЗ которой он ушёл, — в pop_loose_exit_room.
* Сигнал отдельный от pop_loose_fell: тот взводится в момент ОТРЫВА плиты
* (там верен только remove_loose), и по нему кнопка внизу нажималась,
* пока кусок ещё летел. */
uint8_t pop_loose_exit;
uint8_t pop_loose_exit_room;
uint8_t pop_droppedout;
/* leveldoor_open (seg007:456): дверь уровня доехала до верха. Ставит
* анимация двери (pop_trob, банк 6), читает спецсобытие подъёма скелета
* (guards.c, банк 1) — снова случай «данные через границу банков».
* Сбрасывается стартом уровня (seg003:97).
*
* ШИРИНА — word, как в оригинале (data.h:361), а не byte: на уровне 8
* do_timers СЧИТАЕТ этой же переменной время до появления мыши
* (`++leveldoor_open` каждый кадр, пока Кид в комнате 16), и байт
* переполнился бы через 255 кадров, обнулив признак «дверь открыта». */
uint16_t pop_leveldoor_open;
/* ---- Шина управления (seg006 globals) ------------------------------ *
* Объявлены в pop_ctrl.h, а ЛЕЖАТ здесь: к ним обращаются ТРИ банка —
* pop_ctrl (банк 5) пишет с клавиатуры, guards.c (банк 1) подаёт
* синтетический ввод ИИ (autocontrol_*, seg002), pop_map (банк 3) читает
* через pop_ctrl_shift_held. Сегодня оно уцелело бы и в pop_ctrl.c
* (собираем без --bank-data, данные банков остаются в _DATA), но это
* флаг сборки: включат его — и шина уедет в страницу банка 5.
*
* control_x/y/shift — «сырое» состояние осей ЭТОГО кадра (абсолютно:
* X LEFT/-1..RIGHT/+1); *_forward/backward/up/down/shift2 — конечный
* автомат held/released/ignore (авто-повтор). */
int8_t control_x, control_y, control_shift;
int8_t control_forward, control_backward, control_up, control_down, control_shift2;
/* Чит «бессмертие» (клавиша I). Отладочный: Кид получает анимацию удара,
* но HP не теряет — иначе проверять боёвку приходится с трёх попаданий. */
uint8_t pop_immortal;
/* Габариты последней нарисованной двери уровня (порт leveldoor_right /
* leveldoor_ybottom): пишет draw_leveldoor из банка pop_bg, читает
* clip_char из pop_map. См. pop_state.h. */
int pop_leveldoor_right;
int pop_leveldoor_ybottom;
/* ---- Отладочная «пустышка» для брейкпоинтов из БАНКОВ ---------------- *
* Зачем. PC-брейкпоинт видит только логический адрес, а 0xC000+ — это
* окно, куда мапятся ВСЕ банки: точка на адресе банковой функции ловит
* заодно чужой код, случайно легший по тому же смещению (проверено:
* точка на start_fall из банка 3 срабатывала на каждом кадре — попадала
* в pop_bg из банка 2). Условные брейкпоинты этот отладчик MAME не
* поддерживает («error in assignment expression» на `==`).
*
* Приём (идея пользователя, 2026-08-04): позвать ЭТУ функцию ровно из
* того места банкового кода, которое отлаживаем, и поставить брейкпоинт
* на неё — она резидентна в W1, её адрес однозначен. По возврату из неё
* читаем что нужно. Условие «когда именно ловить» пишется обычным `if`
* в C — это гибче любых выражений отладчика.
*
* Счётчик нужен, чтобы вызов не выкинул оптимизатор; заодно видно, сколько
* раз точка прошла, если ловим не останавливаясь. */
uint8_t pop_dbg_hits;
void pop_dbg_trap(void) { pop_dbg_hits++; }
/* ВРЕМЕННО: трасса kidobj — порт вывода, который пользователь добавил в
* SDLPoP add_kid_to_objtable (seg008:1679), чтобы сверить поведение у шва
* кадр в кадр. Буфер РЕЗИДЕНТНЫЙ, потому что читает его брейкпоинт MAME:
* printf из действия точки берёт байты по фиксированному адресу. Поля —
* ровно те же и в том же порядке, плюс наша комната девятым (у оригинала
* смена комнаты видна по скачку tilepos, а нам удобнее явно). */
int8_t pop_dbg_obj[9];
/* ВРЕМЕННО (разбор оверлея падающего куска): по слоту mob.
* pop_dbg_pass[i] — что было в проходе отрисовки:
* bit0 рассмотрен, bit1 active, bit2 draw_y задан, bit3 defer совпал,
* bit4 нарисован, bit5 позван mob_overlay_neighbour.
* pop_dbg_ovl[i*4 + 0..3] — что решил mob_overlay_neighbour:
* [0] r+128, [1] rt+128, [2] флаги, [3] col.
* Флаги: bit0 вошли, bit1 c<=9, bit2 tile(r,c), bit3 габарит r,
* bit4 ВЫЗОВ по r, bit5 tile(rt,c), bit6 габарит rt, bit7 ВЫЗОВ по rt. */
uint8_t pop_dbg_pass[14];
uint8_t pop_dbg_ovl[14 * 4];
/* ВРЕМЕННО: какие тайлы РЕАЛЬНО перекрывают спрайт куска. Бит
* (ряд+1)*2 + (0 своя колонка / 1 соседняя), ряды −1..2. */
uint8_t pop_dbg_tiles[14];
/* Зеркало уровня 4 — см. pop_state.h. */
int8_t pop_jumped_mirror;
/* Тайл СОСЕДНЕЙ комнаты изменился, пока она не отрисована (плита, которую
* Кид расшатал и бросил, провалилась заочно — animate_loose в pop_trob).
* Кромочные колонки и у отрисовки (lcol_fg), и у коллизии (g_lcol/g_rcol) —
* СНИМКИ на момент входа в комнату, поэтому их надо перечитать. Ставит
* банк 6, разбирает главный цикл. */
uint8_t pop_neigh_dirty;
/* seamless (seg000:900 + seg003:158): переход 12 → 13 идёт БЕЗ двери
* уровня — уровень кончается фактом попадания в комнату 23, и следующий
* начинается в комнате с тем же номером и тем же рядом. Флаг говорит
* старту уровня «не сбрасывай HP» (и, когда появятся заставки, «не
* показывай „LEVEL 13“»): Кид продолжает бежать, не заметив границы.
* Ставит главный цикл, гасит pop_start_level. */
uint8_t pop_seamless;
/* «Кид попал в комнату ЧИТОМ навигации» — держит бесшовный переход уровня.
* Отладочная добавка, у оригинала телепорта между комнатами нет вовсе; см.
* pop_dbg_roomnav и ../docs/impl_diff.md. Снимается первой же СМЕНОЙ
* КОМНАТЫ ОБЫЧНЫМ ХОДОМ и стартом уровня. */
uint8_t pop_nav_hold;
/* ВРЕМЕННО (замер фаз кадра 2026-08-13): четыре маркера-пустышки в
* РЕЗИДЕНТЕ. Брейкпоинт на каждом + printf totalcycles даёт длительность
* блоков: m1..m2 — логика (синий), m2..m3 — фон (зелёный), m3..m4 —
* спрайты (циан). В банк брейкпоинт ставить нельзя: 0xC000 общее окно. */
void pop_dbg_m1(void) { }
void pop_dbg_m2(void) { }
void pop_dbg_m3(void) { }
void pop_dbg_m4(void) { }
/* ВРЕМЕННО (разбор зелёного блока 2026-08-13): состав перерисовок в САМОМ
* тяжёлом кадре. pop_redraw_needed считает вызовы по видам, и если кадр
* оказался тяжелее прежнего максимума — сохраняет расклад сюда.
* Индексы 0..7 — POP_RD_*, 8..9 — POP_RDA_* (полоса у потолка). */
uint8_t pop_dbg_rdmax[10];
uint8_t pop_dbg_rdmax_tot;
/* ВРЕМЕННО (замер внутренностей 2026-08-13): m5..m8 — границы внутри
* pop_ceil_shake_draw (heal / draw_tile) и mob_render (heal / блиты). */
void pop_dbg_m5(void) { }
void pop_dbg_m6(void) { }
void pop_dbg_m7(void) { }
void pop_dbg_m8(void) { }
/* ВРЕМЕННО (разбор pop_loose_tick 2026-08-13): m9..m12 — границы ВНУТРИ
* самого pop_loose_tick. Он банковый, но зовёт резидент W1 напрямую,
* так что брейкпоинты по этим адресам однозначны. */
void pop_dbg_m9(void) { }
void pop_dbg_m10(void) { }
void pop_dbg_m11(void) { }
void pop_dbg_m12(void) { }
/* ВРЕМЕННО (разбор pop_ceil_shake_draw 2026-08-13): heal / draw_tile. */
void pop_dbg_m13(void) { }
void pop_dbg_m14(void) { }
void pop_dbg_m15(void) { }
/* ВРЕМЕННО (разбор pop_redraw_needed по ВИДАМ 2026-08-13): вид приезжает в
* A (__sdcccall(1), uint8 arg1), брейкпоинт на первой инструкции логирует
* его вместе с тактами — так видно цену КАЖДОГО вида, а не число пометок. */
void pop_dbg_kind(uint8_t k) { (void)k; }
void pop_dbg_m16(void) { }
/* ВРЕМЕННО (разбор pop_blit_b 2026-08-13): сколько в одном блите занимают
* САМИ пиксели, а сколько пооспрайтовые накладные (map/unmap страницы
* атласа, чтение заголовка, пометка грязного прямоугольника). */
void pop_dbg_b1(void) { } /* вход (спрайт прошёл отсев) */
void pop_dbg_b2(void) { } /* atlas_image + gfx_w0_map */
void pop_dbg_b3(void) { } /* сам блит */
void pop_dbg_b4(void) { } /* pop_cd_touch */
void pop_dbg_b5(void) { } /* gfx_w0_unmap (конец) */
void pop_dbg_b6(void) { } /* взведён = пошли в gfx_blit_noclip, а не в _part */
/* ВРЕМЕННО (разбор pop_phys_tick 2026-08-19): звенья цепочки kid_phys. */
void pop_dbg_p1(void) { } /* loadkid_and_opp сделан */
void pop_dbg_p2(void) { } /* fall_accel + fall_speed */
void pop_dbg_p3(void) { } /* determine_col */
void pop_dbg_p4(void) { } /* bump_into_opponent */
void pop_dbg_p5(void) { } /* check_collisions */
void pop_dbg_p6(void) { } /* check_bumped */
void pop_dbg_p7(void) { } /* check_action */
void pop_dbg_p8(void) { } /* check_press */
/* ВРЕМЕННО (регрессия цены блита 2026-08-13): w и h упакованы в один
* 16-битный аргумент (arg1 -> HL при __sdcccall(1)), брейкпоинт логирует
* HL — дальше цена раскладывается как a + b*h + c*w*h, где b и есть
* построчные накладные ядра. */
void pop_dbg_wh(uint16_t wh) { (void)wh; }