Фиксированный логический кадр по лучу + режимы FASTEST/FAST/NORMAL

Период стал max(n, ceil(W)) вместо ceil(W)+2: три gfx_wait_vsync после
работы отсчитывались от её КОНЦА, поэтому бюджет кадра был один растр.
Теперь ждём от якоря начала кадра, и при n=3 бюджет 1 290 240 тактов.

Счёт кадров — программный, по биту 5 порта 0xFE (положение луча), а не по
кадровым прерываниям: те теряются в DI-окнах акселератора фазозависимо
(замер: 0..2,8 %, на полной перерисовке три подряд).  Условие точности
одно — зазор между выборками меньше 64 512 тактов; точки выборки
расставлены по замеру, а не на глаз.

Режимы (pop_pace.h), клавиша P по кругу, дефолт FASTEST.  Условие боя
взято у оригинала буквально (SDLPoP seg003.c:363): Kid.sword ==
SWORD_2_DRAWN, а не «идёт бой».

Проверено в MAME на 11/15: счётчик без недосчёта на 270 кадров, период
ровно 3 растра на 302 логических кадрах (ни длиннее 3,1, ни короче 2,9),
NORMAL даёт ровно 4, с вынутым мечом — ровно 5.
This commit is contained in:
2026-08-19 23:06:27 +03:00
parent 61b8d80275
commit a6e39070af
11 changed files with 310 additions and 9 deletions
+10
View File
@@ -19,6 +19,7 @@
#include <string.h> /* memcpy = LDIR: цикл на C тут стоил ~390 тактов на байт */
#include "pop_kid.h"
#include "pop_map.h"
#include "pop_pace.h" /* pop_beam_sample — счёт кадров по лучу */
#include "pop_ctrl.h" /* pop_ctrl_shift_held() — для check_grab */
#include "pop_geom.h" /* общая геометрия (x_bump/y_land/y_to_row) */
#include "pop_tile.h" /* POP_TILE_DIV/MOD — деление на ширину тайла таблицей */
@@ -1994,14 +1995,17 @@ static void check_collisions(void)
}
pop_dbg_p2(); /* ЗАМЕР: вход в тело */
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
set_char_collision();
pop_dbg_p3(); /* ЗАМЕР: set_char_collision */
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
move_coll_to_prev(Char.curr_row); /* заодно снимет окно prev */
coll_prev_row = Char.curr_row;
calc_coll_window();
coll_lo = win_lo; coll_hi = win_hi;
coll_scan_prepare();
pop_dbg_p4(); /* ЗАМЕР: окно посчитано */
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
/* Порядок важен: prev уже забран, теперь три ряда пересчитываются
* НА ЭТОТ кадр (в оригинале ровно так же, seg004:0004). Ряд здесь
* гарантированно 0..2, поэтому соседние ряды — это те же базы ±10, без
@@ -2036,6 +2040,7 @@ static void check_collisions(void)
coll_row(coll_above);
}
pop_dbg_p5(); /* ЗАМЕР: три ряда просканированы */
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
/* Обход СВЕРХУ ВНИЗ, как в оригинале (9→0): побеждает МЛАДШАЯ колонка,
* в которой флаг перешёл 0→1. Только по ПЕРЕСЕЧЕНИЮ окон: вне его
* сравнивать нечего (у оригинала там prev_coll_room != curr_row_coll_room
@@ -2685,6 +2690,7 @@ void pop_loose_tick(void) __banked
* Инкремент+сравнение считают row/col без деления. */
uint8_t row = 0, col = 0;
pop_dbg_m9(); /* ЗАМЕР: вход pop_loose_tick */
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
/* Гейт холостого хода: если ни одна фаза не взведена, оба цикла (30 + 10
* позиций) пропускаем целиком — замер 11/15 дал 9 852 такта на комнату,
* где loose-плит нет вовсе. Флаг ставят места записи фазы, снимаем его
@@ -3164,11 +3170,14 @@ static void kid_phys(void)
bump_into_opponent(); /* seg003: безоружный Кид отскакивает от стража */
check_collisions(); /* seg004: флаги перекрытия по колонкам ряда */
pop_dbg_p6(); /* ЗАМЕР: check_collisions целиком */
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
check_bumped(); /* удержать у стены до check_action (порядок PoP) */
check_action();
pop_dbg_p7(); /* ЗАМЕР */
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
check_press(); /* seg006: стойка/пробой loose → make_loose_fall */
pop_dbg_p8(); /* ЗАМЕР */
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
check_spike_below(); /* seg006: над колонкой с пиками → выдвинуть пики */
check_spiked(); /* seg006: напоролся на вредные пики → смерть */
check_chomped_kid(); /* seg004: перемололо в сомкнутых челюстях → смерть */
@@ -3229,6 +3238,7 @@ void pop_phys_tick(void) __banked
* кончился), а уход из комнаты и сотрясение отсечены внутри kid_phys. */
pop_loadkid_and_opp();
pop_dbg_p1(); /* ЗАМЕР: окно Char загружено */
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
kid_phys();
pop_savekid_and_opp();
}