Фиксированный логический кадр по лучу + режимы 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
+17
View File
@@ -14,6 +14,7 @@
#include <fcntl.h>
#include <unistd.h>
#include "pop_bg.h"
#include "pop_pace.h" /* pop_beam_sample — счёт кадров по лучу */
#include "_pop_bg.h" /* контракт с холодной половиной (pop_room.c, банк 7) */
#include "pop_tile.h"
#include "pop_map.h" /* pop_upside — переворот (зелье инверсии) */ /* общие листья слоя фона (резидент): блит, тайлы, таблица */
@@ -57,6 +58,7 @@ static void ov_mark(int x, int y, int w, int h)
void pop_fore_heal(void) __banked
{
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
uint8_t pg = gfx_get_draw_page() & 1;
if (!(ov_pending & (1 << pg))) return;
pop_heal_off(ov_x0, ov_y0, ov_x1 - ov_x0, ov_y1 - ov_y0);
@@ -197,6 +199,7 @@ static const uint8_t WR_RULE[4][6] = {
static void wall_rnd(int row, int col)
{
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
/* Кэшируем только штатную сетку: колонка вне 0..9 дала бы индекс
* соседнего РЯДА и чужую раскладку (тихий баг вместо промаха). */
int idx = (col >= 0 && col < 10) ? (row + 1) * 10 + col : -1;
@@ -282,6 +285,7 @@ static uint8_t wpp_d[5]; /* пять prandom(2), 0..2 */
static void wall_rnd_palace(int row, int col)
{
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
int idx = (col >= 0 && col < 10) ? (row + 1) * 10 + col : -1;
pop_rnd_t saved;
uint8_t i;
@@ -366,6 +370,7 @@ static void wpp_fill(int x, int ytop, int w, int h, uint8_t colour)
static void wall_pattern_palace(int row, int col, int which_part)
{
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
int xh = POP_COL_XH[col];
int dbys = 63 * row + 65; /* база БЕЗ POP_YOFF — для блитов */
int dmys = dbys - 3;
@@ -411,6 +416,7 @@ static void wall_pattern_palace(int row, int col, int which_part)
static void wall_pattern(int row, int col, int which_part)
{
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
int xh = POP_COL_XH[col];
int dby = 63 * row + 65;
uint8_t parts;
@@ -450,6 +456,7 @@ static void wall_pattern(int row, int col, int which_part)
/* Решётка ворот (portcullis) из ЛЕВОЙ комнаты (seg008.c:1110). */
static void draw_gate_back(uint8_t modl, int xh, int dby, int dmy)
{
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
int x = xh * 8;
int gate_top_y = dby - 62;
int openness = (((modl > 188) ? 188 : modl) >> 2) + 1;
@@ -480,6 +487,7 @@ static void draw_gate_back(uint8_t modl, int xh, int dby, int dmy)
* Клип по прямоугольнику персонажа делает pop_t_fclip (см. pop_tile.h). */
static void draw_gate_fore(uint8_t modl, int xh, int dby, int dmy)
{
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
int x = xh * 8;
int gate_top_y = dby - 62;
int openness = (((modl > 188) ? 188 : modl) >> 2) + 1;
@@ -576,6 +584,7 @@ static const uint8_t FORE_ANY[32] = {
/* Контекст ft_* обязан быть проставлен вызывающим. */
static void fore_only_tile(int row, int col)
{
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
/* Нажатая кнопка → пол/stuck (get_tile_to_draw) — это уже учтено в ft_code
* (его ставит pop_tile_code_drawn), иначе поверх Kid лезет передняя грань
* КНОПКИ. */
@@ -622,6 +631,7 @@ static void fore_only_tile(int row, int col)
* backtable — под персонажем. */
static void ceil_over_kid_tile(int col)
{
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
uint8_t code = pop_tile_code(-1, col);
const piece *t = &pop_tile_table[code];
int x = POP_COL_XH[col] * 8;
@@ -711,6 +721,7 @@ static void other_overlay_tile(int row, int col); /* fwd (draw_floor_overlay -
static void climb_overlay_tile(int row, int col, uint8_t fidx)
{
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
/* тип — ПОДСТАВЛЕННЫЙ (нажатая кнопка = пол): иначе кромка, за которую
* Kid спускается/подтягивается, закрашивается целым тайлом */
uint8_t code;
@@ -747,6 +758,7 @@ static void climb_overlay_tile(int row, int col, uint8_t fidx)
* draw_tile_base, draw_tile_anim (свои пики) и draw_tile_bottom. */
static void overlay_mid_tile(int row, int col)
{
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
uint8_t code = pop_tile_code_drawn(row, col);
uint8_t lcode = pop_tile_code_drawn(row, col - 1);
const piece *t;
@@ -809,6 +821,7 @@ static void overlay_mid_tile(int row, int col)
* сразу), поэтому порядок эмулируется явной проверкой. */
static void other_overlay_tile(int row, int col)
{
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
uint8_t code, left;
if (!tile_in_fclip(row, col)) return;
code = pop_tile_code(row, col);
@@ -853,6 +866,7 @@ static void other_overlay_tile(int row, int col)
void pop_mob_overlay_tile(int row, int col, int8_t oref_row, int8_t oref_col,
int mx, int my, uint8_t mh) __banked
{
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
int8_t saved_row = pop_bg_obj_row, saved_col = pop_bg_obj_col;
pop_bg_obj_row = oref_row;
pop_bg_obj_col = oref_col;
@@ -952,6 +966,7 @@ static void char_footprint(int obj_x, int obj_y, uint16_t w, uint16_t h,
* ничем не режется. */
void pop_fore_tile_b(int row, int col) __banked
{
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
uint8_t saved = pop_t_fclip_on;
pop_t_fclip_on = 0;
fore_tile(row, col);
@@ -974,6 +989,7 @@ void pop_fore_tile_b(int row, int col) __banked
* странице её надо будет восстановить (pop_fore_heal). */
void pop_ceil_fore_tile_b(int col) __banked
{
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
int x = POP_COL_XH[col] * 8;
/* ov_mark ЗДЕСЬ НЕ НУЖЕН, и это важно для бюджета. Передние куски потолка
* рисуются от dby = 2, то есть занимают строки 0..2 поля, а пометку ставит
@@ -999,6 +1015,7 @@ void pop_fore_pass_end(void) __banked { gfx_set_bank(GFX_BANK_NORMAL); }
void pop_fore_over_char(const pop_char_t *ch, int obj_x, int obj_y,
uint16_t w, uint16_t h) __banked
{
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
int8_t cL, cR, cLraw, rT, rB, rTraw, r, c;
uint8_t frame = ch->frame, action = ch->action;
pop_t_fclip_on = 1; /* окно = прямоугольник спрайта */