37fc572cc3
Возврат к БИТ-В-БИТ генератору оригинала по умолчанию (POP_PRANDOM_EXACT=1): по нему проще отлаживать и сверять картинку с эталоном. Чтобы это не стоило процента бюджета, сам шаг LCG переписан на Z80-ассемблере — единственное место в порте, где это сделано, с явного разрешения. Приём: 214013 = ((((1<<1)+1)<<2 + 1)<<4 + 1)<<10 - 3 — схема Горнера по РАЗРЕЖЕННОЙ записи константы. Вместо 12 сложений (по числу единиц в 0x343FD) — 17 удвоений, три сложения и одно вычитание; величина 3*s, нужная в конце, попадается по дороге на втором шаге. Проверка в ДВА этапа: - схема на хосте: horner(s) == s*214013+2531011 на 3 000 000 сидов; - сама asm-транскрипция на живой машине: breakpoint на pop_prandom, 11 последовательных состояний сида из MAME — каждый переход совпал с s*214013+2531011 бит-в-бит. Замер, комната 3, 175 кадров (медиана кадра / prandom->torch_draw): C, бит-в-бит (16-бит половины) 403 632 / 10 933 C, xorshift16 + шаг Вейля 397 986 / 7 927 asm, бит-в-бит 400 800 / 9 331 То есть asm вернул половину разрыва (2 832 такта за кадр), сохранив совместимость с эталоном. Ветка xorshift оставлена под -DPOP_PRANDOM_EXACT=0 как запасной ход — брать её имеет смысл, только если не хватит последних 2 800 тактов. Итог оптимизационного круга: 416 154 -> 400 800 (0.968 -> 0.932 кадра). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
62 lines
3.7 KiB
C
62 lines
3.7 KiB
C
/*
|
||
* pop_geom.h — общая геометрия PoP и PRNG оригинала.
|
||
*
|
||
* Сюда сведено то, что раньше дублировалось по модулям (x_bump/y_land в
|
||
* pop_kid и pop_map, y_to_row в pop_bg и pop_map, 32-битный LCG в pop_bg и
|
||
* pop_trob). Кроме экономии это ФУНДАМЕНТ bank-safe API: модуль живёт в
|
||
* W1/W2 и не трогает графику, поэтому его может звать и главный цикл, и
|
||
* резидент W3, и будущий __banked-код стражей (docs/layout_plan_v2.md §4).
|
||
*/
|
||
#ifndef POP_GEOM_H
|
||
#define POP_GEOM_H
|
||
|
||
#include <stdint.h>
|
||
|
||
#define FIRST_ONSCREEN_COLUMN 5 /* индекс col 0 комнаты в x_bump */
|
||
#define TILE_SIZEX 14 /* ширина тайла в координатах персонажа */
|
||
#define TILE_SIZEY 63 /* высота ряда */
|
||
|
||
/* x_bump (data.h): левая граница колонки в координатах персонажа; колонка
|
||
* комнаты col = x_bump[col + FIRST_ONSCREEN_COLUMN]. y_land: пол ряда
|
||
* (индекс row+1; [0] — ряд «над комнатой»). */
|
||
extern const int16_t pop_x_bump[20];
|
||
extern const int16_t pop_y_land[5];
|
||
|
||
/* y_to_row_mod4 (seg006): ряд по координате y с оборотом mod 4 (−1 = полоса
|
||
* над комнатой, 3 = под комнатой). */
|
||
int8_t pop_y_to_row(int16_t y);
|
||
|
||
/* prandom (порт seg009:321). Сид ВНЕШНИЙ: у стен (раскладка кладки) и у
|
||
* анимаций тайлов (фаза факелов) свои последовательности, смешивать их
|
||
* нельзя.
|
||
*
|
||
* ДВА генератора, выбор через POP_PRANDOM_EXACT (см. pop_geom.c):
|
||
* 1 (по умолчанию) — LCG оригинала (s*214013+2531011) БИТ-В-БИТ: та же
|
||
* раскладка кладки и те же броски, что в SDLPoP, поэтому по картинке
|
||
* можно сверяться с эталоном;
|
||
* 0 — xorshift16 + шаг Вейля, без единого умножения. Дешевле ровно на
|
||
* 5 646 тактов за кадр, 1.3 % бюджета (замер A/B в MAME, комната 3,
|
||
* 175 кадров: 403 632 против 397 986). Последовательность другая —
|
||
* статистически эквивалентная, но не та же.
|
||
*
|
||
* Обе версии считают 16-битными половинами, а не unsigned long: на 32-бит
|
||
* умножении SDCC зовёт __mullong, и это 8 430 тактов на вызов (замер в
|
||
* MAME, breakpoint на __mullong).
|
||
*
|
||
* ЗАДЕЛ: разрыв между версиями определяется не алгоритмом, а кодогенерацией
|
||
* SDCC — те же генераторы на Z80-ассемблере укладываются в 86..148 тактов.
|
||
* Если процент понадобится, сначала переписать в asm ОРИГИНАЛЬНЫЙ LCG (там
|
||
* умножение на константу), а менять генератор — только если и этого мало. */
|
||
typedef struct { uint16_t lo, hi; } pop_rnd_t;
|
||
|
||
#ifndef POP_PRANDOM_EXACT
|
||
#define POP_PRANDOM_EXACT 1
|
||
#endif
|
||
|
||
uint16_t pop_prandom(pop_rnd_t *seed, uint16_t maxv);
|
||
|
||
/* Сид из 16-битного числа (старшая половина = 0). */
|
||
#define pop_prandom_set(s, v) do { (s).lo = (uint16_t)(v); (s).hi = 0; } while (0)
|
||
|
||
#endif
|