Files
Sprinter-SDCC/applications/PoP/roomtest/pop_geom.h
T
snark13 37fc572cc3 PoP roomtest: LCG оригинала на ассемблере — точность без потери скорости
Возврат к БИТ-В-БИТ генератору оригинала по умолчанию (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>
2026-07-30 17:05:13 +03:00

62 lines
3.7 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_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