Files

793 lines
54 KiB
Markdown
Raw Permalink 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.
# BLOCK MAP: FUN_180529fe0 (полная разметка по raw asm, 22v)
Источник: `handoff/nls_dasm/f529fe0_full.dis` (soothe_mem.bin, база 0x180000000,
диапазон 529c6052ba00; старый f529fe0.dis был ОБРЕЗАН на 52a813).
Регистры: r13 = индекс полосы, r12 = state-ptr ctx+0x440518, esi/r8d = nbin,
rbx = nbands ([rsp+0x30]), rdi = ctx. Флаг 0x5408b8 выбирает float/double
вариант thunk-операций (семантика пар идентична).
## Исправление адресации (главное)
«Скалярные» буферы NOTES 21b — МАССИВЫ пер-полосных векторов (шаг 16 байт):
- bands[i] = [ctx+0x540678+i·16] — кривые полос
- acc[i] = [ctx+0x5407c8+i·16] — ПЕР-ПОЛОСНЫЙ аккумулятор combine
- track[i] = [ctx+0x540768+i·16] — трек основного цикла
## Thunk-таблица (ILT-стаб 180001xxx -> jmp [table + idx*8], idx @1826159a0)
Резолв при live idx=4; семантика из тел + NOTES 21b:
| стаб float | стаб double | impl | op |
|---|---|---|---|
| 0x180001f10 | 0x180001c70 | dc40 / **8d60** | dst = B A (sub) |
| 0x180001fa0 | 0x180001940 | ee20→487a0 / **3c40** | fma att/rel половин |
| 0x180001d60 | —(встречен в 1b80-паре?) | — / **5a20** | dst += B (add) |
| 0x180001970 | — | **3f40** | sub (float) |
| 0x1800019a0 | — | **4200** | mul scalar? (делегирует 181a63fe0) |
| 0x180001a00 | — | **4720** | mul |
| 0x180001a60 | — | **5160** | sub DOUBLE |
| 0x180001850 | — | **25e0** | add DOUBLE |
| 0x180002000 | 0x180001c40 | ? | axpy-класс (band ⊕ track) |
| 0x180002060 | 0x180002120 | 10860/11940 | transform со скаляром xmm3 |
| 0x180002270 | 0x1800022a0 | 14c40/15060 | transform со скаляром xmm0/xmm1 |
| 0x180002030 | 0x180001d30 | ? | transform float/double |
| in-place кернелы | | 140950/140980/1409b0/1409e0/140a40/140ad0/140b00/140b60 | bigkernel-семейство (exp2/mask) |
## Карта блоков
### Пролог
- `52a03a52a396`: PRNG-пролог. LCG state @ctx+0x2404e0, шаги +0x3cdca,
+0x140236, +0x10d56, +0xdf6b6, +0xa8c5e, +0x72916; LUT ptr @ctx+0x5408b0;
константы 262b704/262b5c8(int)/262b700(int)/24c3c58. Результат: индексы
iVar7/iVar8 ([rsp+0x130]/счётчики) и стартовая позиция цикла.
Live-поведение залочено раньше (fVar30=1).
- `52a397`: r14 = &bands[0].
### Pre-combine #1 (52a39752a421)
- `52dbc0(f6f8_data, bands[0], nbin)` — copy bands[0] → f6f8.
- цикл i=1..nbands−1 по массиву @0x540688 (= &bands[1]!): transform(f6f8, bands[i])
через 20f0(float)/1850(double=25e0 ADD). ⇒ **f6f8 = Σ_{i≥1} bands[i]** (или min/max
— точный op 20f0 не залочен, кандидат ADD по double-паре!).
### Нормировка + mix-вес (52a42152a458)
- `52d920(f6f8, xmm11/[rsp+0x148](int), nbin)` — f6f8 /= K.
- `xmm7 = powf([ctx+0x2c], xmm13)` — mix^p.
### Pre-combine #2 (52a45e52a4f6) по всем полосам
- band[i] *= (xmm11 xmm7) [thunk 2030/1d30]
- f6f8 ⊕= ... с весом xmm7 [thunk 1fd0/2150]
⇒ взвешенное смешение кривых полос ДО основного цикла.
### Основной цикл по полосам (52a4fb..52b3c7, тело с 52a580)
На каждую полосу i (r13):
1. `scale` (52a58352a5c4): s = xmm11·[0x540870]; если флаг 0x5408b8:
s ← exp(PRNG)-ветка; затем ·[0x54088c]. Читается track_i (@0x540768[i]) в [rsp+0x40].
2. `transform(bands[i], s)` (52a5cc52a607): bands[i] *= s [2030/1d30].
3. флаг-оп (52a60852a645): transform(bands[i], bands[i]) через 140980/1409b0
(bigkernel, вероятно exp2-кернел) — только при флаге.
4. `52d650(state@0x440518, f6f8, bands[i], nbin)` (52a64652a658) — БИДИР-IIR #1:
band → f6f8 (double, коэф down@0x440528/up@0x4c0528, acc@0x540528, reset внутри).
5. Инлайн БИДИР-IIR #2 (52a65d52a85a, только при флаге; иначе прыжок на 6):
вход f6f8 → выход bands[i] (тот же state 0x440518, reset отдельно).
6. `transform(f6f8 ⊕ bands[i])` (52a85b52a896) [1970/1a60 = 3f40 SUB float /
5160 SUB double]: f6f8 = bands[i]? (аргументы rcx=f6f8, rdx=band).
7. **БИДИР-IIR #3** (52a89752aa6e, инлайн): state base 0x3404f8
(down@0x2404f8, up@0x2c04f8, acc@0x3404f8), длина из поля ctx+0x2404e8,
IN-PLACE по bands[i]. Reset каждый вызов.
8. `op(bands[i], скаляр xmm9=0?)` (52aa6e52aaaf) [2060/2120].
9. Ветка флага==0 (52aabc52ab90):
- op(vec@0x540698, f6f8, scalar=(конст1 [0x54087c])) [1a00/1be0 = mul]
- op(f6f8, scalar=[0x54087c]·xmm10) [2270/22a0]
- bigkernel `140b60/140950(bands[i], f6f8, bands[i], nbin)`
10. **COMBINE (52ab9052abd7)**: acc_i = [0x5407c8+i·16];
`op(rcx=acc_i, rdx=bands[i], r8=f6f8, r9=nbin)` [1f10→dc40 float-SUB /
1c70→8d60 double-SUB]: **f6f8 = bands[i] acc_i** ✓ NOTES 21b.
11. Половины f6f8 + fma (52abd752ad04):
- rbp = f6f8 + nbin (верхняя половина); op(f6f8_low?, ...) [2060/2120, scalar 0]
- op(rcx=f6f8_upper, rdx=vec@0x5406c8, r8=acc_i) [1fa0/1940 = **3c40 fma**]
- op(rcx=f6f8_lower, rdx=vec@0x5406e8, r8=acc_i) [1fa0/1940]
⇒ fma с коэф-массивами 0x5406c8 (upper/att) и 0x5406e8 (lower/rel) ✓.
12. `acc_i += bands[i]` (52acf452ad03) [1b80→6840 / 1d60→**5a20** add] ✓.
13. Ветка флага!=0 (52ad0452ae57): повтор п.9 с теми же адресами
(0x540698, f6f8-blend, bigkernel 1409e0/140ad0).
14. `op(bands[i], скаляры 0x1824c4680 / xmm13)` (52ae5852ae56 хвост).
15. `bands[i] ⊕= track_i [rsp+0x40]` (52ae7452ae8a) [2000/1c40].
16. `op(vec@0x5406a8, bands[i])` (52ae8f52aecа) [2000/1c40].
17. флаг → bigkernel in-place 140980/1409b0 (52aecb52af08).
18. **БИДИР-IIR #4 ×2** (52af0952b2af и повтор 52b0de52b2af):
state base 0x440510 (down@0x340510, up@0x3c0510, acc@0x440510),
длина ctx+0x340500, in-place bands[i]. ДВА прохода подряд (каждый с reset).
19. Финальные scale/op полосы (52b2af52b3af):
- скаляр `[ctx+0x1c+i·4] · xmm14` → transform [2030/1d30]
- bigkernel in-place 1409e0/140ad0 (при флаге) ИЛИ
op(scalar=xmm12/xmm8) [2270/22a0] + `140a40/140b00(band,band,scalar)`
20. Инкремент i (52b3af–52b3c7), выход при i ≥ nbands.
### Эпилог (52b3cd52b935)
- GUI-snapshot блок (флаг 0x2404bc): копии векторов @0x540728 ← bands[i]
(совпадает с consumers_out.txt:144-183).
- Остаток до 52b935: восстановить при транскрипции (вероятно dry/wet + mirror).
## Состояния бидир-IIR (4 базы, лейаут {down,+0x80000 up,+0x80010/+0x100010 acc})
| # | база | downCoef | upCoef | acc | длина |
|---|------|----------|--------|-----|-------|
| 12 (52d650+инлайн) | 0x440518 | +0x10 | +0x80010 | +0x100010 | nbin |
| 3 | 0x2c04f8-группа | 0x2404f8 | 0x2c04f8 | 0x3404f8 | поле ctx+0x2404e8 |
| 4 ×2 | 0x340510/0x3c0510 | 0x340510 | 0x3c0510 | 0x440510 | поле ctx+0x340500 |
## Открытые вопросы к транскрипции
1. Точный op thunk 20f0/1850 в pre-combine #1 (ADD — кандидат).
2. Аргументный порядок 2000/1c40 (axpy) и 2060/2120.
3. Семантика bigkernel-семейства 1409xx/140axx/140bxx (какой где: exp2(-x),
exp2(-x)·blend, mirror?).
4. Что пишется в track_i @0x540768[i] (writer вне метода — искать отдельной охотой;
кандидат FUN_18052e9b0).
5. Эпилог 52b4eb52b935.
## ДОПОЛНЕНИЕ 22v: декод генератора коэффициентов
### FUN_180530b60 (апдейт параметров, size=462)
- fVar2 = powf([ctx+0x540878], K1); fVar3 = powf([ctx+0x54087c], K2)
(оба live = 0.5 — параметры attack/release-класса);
- лог-интерполяция констант → [ctx+0x540894] и [ctx+0x540898] (тау-скаляры);
- вызовы генератора на все три состояния:
- 180533340(ctx+0x2404e8, tau=0x540894-ветка, C=DAT_1824c459c, sr, ...)
- 180533340(ctx+0x340500, tau=[0x540894], ...)
- 180533340(ctx+0x440518, tau=[0x540898], ..., mult=DAT_1824c4564)
### FUN_180533340(state, tau, C, sr, p, mult) — ГЕНЕРАТОР КОЭФ. БИДИР-IIR
```
sr' = sr · DAT_1824c3d8c
acc = 0; downCoef[0] = 1.0 (int-пара {0, 0x3ff00000} @+0x10)
n = state[0]; fc_norm = (C/sr')·n
for i in 1..n-1:
g = (i <= fc_norm) ? fc_norm/i : powf(fc_norm/i, p) # частотный варп!
c = 1/(g·tau/mult + 1)
up[i] = exp(c·g·tau/state[2] · DAT_1824c46b8)
down[i] = 1 up[i]
```
ЛЕЙАУТ СОСТОЯНИЯ СОШЁССЯ: downCoef[] @base+0x10, upCoef[] @base+0x80010
(pdVar6[0x10000] = +0x80000 байт), acc — скаляр в хвосте. Это ЧАСТОТНО-
ЗАВИСИМОЕ сглаживание маски: сила растёт к низким бинам (1/i) с питч-законом
выше кросса. Вот где «размазывание» нотча в реале — НЕ плоский IIR, который
мы отвергли офлайн (22u), а пер-биновый варп!
### Статус сбора (решение: без коммитов до первой валидации)
Незакоммичено: f529fe0_full.dis, BLOCKMAP_529fe0.md, thunk-резолвер,
правки NOTES_LEVEL (22v будет добавлена при транскрипции). Следующий шаг —
транскрипция process_frame_faithful по этой карте.
## ДОПОЛНЕНИЕ 23b: резолв ILT + декод FIR-цикла (52b55052b8bb)
### Резолвер
`scripts/ilt_resolve.py`: стаб = `mov eax,[rip+idx]; lea r10,[rip+tbl]; jmp [r10+rax*8]`;
live idx=4 у всех; таблицы @0x1826xxxx. Полная таблица «стаб→impl»:
| стаб | impl | роль (уточн.) |
|---|---|---|
| 0x180002210 | 0x136e0 | copy scratch→FIR? (валидатор → 39de0/3a040/3a220) |
| 0x180002180 | 0x125e0 | complex-op A/C float (воркер 3a4a0; r9 НЕ передаётся ⇒ длина из объекта) |
| 0x180001bb0 | 0x6a40 | complex-op A/C double |
| 0x180001a90 | 0x5560 | complex-op B/D float — ЧИСТЫЙ FMA (fma=28, mul/add=0!) |
| 0x1800019d0 | 0x4340 | complex-op B/D double |
| 0x180001880 | 0x2c60 | paired-scalar op 1 (флаг-ветка) |
| 0x180001ca0 | 0x9380 | paired-scalar op 2 |
| 0x180001df0 | 0xb3c0 | final op float |
| 0x180001f70 | 0xe360 | final op double |
| 0x180140a10 | 0x141400→[1826181d8]=**0x1802a24c0** | дизайн float |
| 0x180140a70 | 0x141580→[1826183c8]=**0x1802fa420** | дизайн double |
| 2030/1d30 | ffe0 / 9be0 | scalar-transform |
| 2270/22a0 | 14c40 / 15060 | scalar-transform-2 |
| 2000/1c40 | fb60 / 8700 | axpy-класс |
| остальное | dc40,8d60,5a20,4720,4200,3f40,5160,25e0,ee20,3c40 | как в BLOCKMAP ✓ |
### Дизайн-тело 0x1802a24c0 (float, вход=rcx bands[i], выход=rdx scratch@0x540628)
Функция входа 0..0x675 (~1.6КБ), далее сиблинги. Алгоритм = **ВЕКТОРНЫЙ LOG2**
(range reduction бит-трюком с 0.6666667, полином Хорнера на YMM-константах
0.5, 0.3333656, 0.2500466, 0.198225, 0.1646246, 0.1696488, 0.1517721,
реконструкция ×ln2=0.6931472; константы @FN+0x1cdfac0..d20). Маскированный
хвост через vmaskmovps/popcnt-таблицы (@0x181F8xxxx). ИТОГ: **scratch = log(bands[i])**.
Диспетчер 535a70 делает swap аргументов (rcx↔rdx) перед прыжком!
### Структура FIR-цикла (полностью залочена)
На полосу: 2×scalar-transform(bands[i], xmm8·[540888] при флаге 540890==0, иначе
xmm8; затем xmm12xmm8) [ср. decomp 184-185: A=[0x540874]expf(K), wet=[0x540888]]
→ DESIGN=log(bands[i])→scratch → copy(scratch→FIR@0x540668) [2210]
→ opA(FIR,buf548,buf598) [2180/1bb0] → FIR[n]=0 (n=0x540534=4096!)
→ 52d920(&FIR[1], xmm13, n/21) деление; 52db50(&FIR[2049], xmm9, n/21)
→ opB [1a90/19d0] → bigkernel in-place 140b30/140aa0 (n/2+1)
→ opC [2180/1bb0] → FIR[n]=0 → 52d990(FIR, WIN_freq+n/2, n/2) УМНОЖЕНИЕ на
**падающую половину Hann** (WIN_freq=wperiodicHann4096: w[1024]=0.5,w[2048]=1.0!)
→ 52db50(&FIR[2048], xmm9, n/2) → opD [1a90/19d0] → FIR[0]=1.0, FIR[1]=0
→ флаг-ветка: парно-скалярные 1880/ca0 → dry/wet scale ×[0x540888] над **2n флоатов**
→ final(df0/f70) с сохранённым track_i ([rsp+0x138][i]).
### Живые буферы (firtrace.py по s1/s2 + свежие захваты dual_b1q_0.5)
- 0x540658 WIN_freq = периодический Hann(4096), пик 1.0 @bin2048 (НЕ рамп 0.5→0.8!)
- 0x540758 freqaxis = линейный, шаг ≈11.713 Гц (≈48000/4096=11.71875, лёгкое
занижение — похоже на накопительную ошибку f32 при построении суммой)
- 0x540598 = SIMD-маски: 8×1.0 / 8×0.0 периодом 16 флоатов, ровно 1024 шт
(YMM lane-select для комплексных кернелов)
- 0x540550 NULL (float-путь активен, флаг 0x5408b8=0); 0x540628 scratch нулевой
между колбеками; 0x540668 FIR = комплексная единица (1,0)×2049 бинов МЕЖДУ
колбеками — кернел потребляется и сбрасывается внутри колбека
- 0x5406f8 хранит аудио-масштабный сигнал (~±0.1) между колбеками — overlap/STFT
буфер conv-движка (не «нулевой acc»!)
- 0x540788 vs 0x5407f8 различаются (max 1.108; @171: 1.751 vs 1.409) — две РАЗНЫЕ
кривые; ни одна не достигает нужных 3.89×@171 (11.82 дБ) ⇒ применённый фильтр
≠ поточечная копия любой из живых кривых (подтверждение разрыва 22y)
## ДОПОЛНЕНИЕ 24l: точный декод FIR-цикла по дизасму (52b550–52b8bb) + константы
Инструмент: scripts/disasm.py (capstone, base 0x180000000 над soothe_mem.bin).
ILT-резолв bigkernel-стабов даёт impl в 141100..141940 — все они IAT-thunk
массивы (`mov rax,[rip+..]; jmp rax`) вне дампа ⇒ тела за импортами; НО
последовательность и КОНСТАНТЫ цикла видны полностью:
### Пошагово (float-ветка, флаг 0x540890==0):
```
52b5a3: xmm6 = xmm8 · [ctx+0x540888]
52b5bf: th2030(bands[i], xmm6, n) ; bands *= 1.0·s888 (xmm8=double 1.0!)
52b5f6: th2270(bands[i], xmm7, n) ; scalar-transform-2
52b62f: 535a70(scratch@540628, bands[i], n/2+1) ; DESIGN: swap→scratch=log(bands)
52b644: th2210(rcx=scratch, rdx=0, r8=FIR, r9=n/2+1) ; копия (+упаковка?)
52b696: FIR[n]=0 ; float-индекс n=4096
52b69e: 52d920(&FIR[1], xmm13=-1.0, n/2-1) ; РАЗДЕЛЕНИЕ НА −1 ⇒ ИНВЕРСИЯ ЗНАКА бинов 1..n/2!
52b6ab: 52db50(&FIR[n/2+1], xmm9=0, n/2-1) ; ОБНУЛЕНИЕ верхней половины до exp!
52b6e1: opB = th1a90(FIR, buf548, mask598, n/2-1) ; FMA-complex (twiddle!)
52b716: BIGKERNEL 140b30(FIR, FIR, n/2+1) ; EXP in-place (тело за IAT)
52b74b: opC = th2180(FIR, buf548, mask598, n/2-1)
52b76d: FIR[n]=0
52b77c: 52d990(FIR, WINfreq+n/2, n/2) ; окно: th2000-класс (FMA axpy!)
52b78c: 52db50(&FIR[n/2], xmm9=0, n/2) ; верхняя половина ×0 снова
52b7ba: opD = th1a90(...)
52b7d4: FIR[0]=1.0f; FIR[1]=0
если флаг f890!=0:
52b803: th1880(FIR, {xmm10,xmm9}, n) ; парно-скалярный с (−1|0.8, 0)
52b81f: th1ca0(FIR, {xmm12,xmm9}, n) ; парно-скалярный с (1.0, 0)
52b857: th2030(FIR, s888, 2n) ; ×wet (=1 live)
52b893: final df0(FIR, track_i, n)
```
### Константы (статические значения из дампа):
| reg | адрес | значение | роль |
|-----|-------|----------|------|
| xmm8 | 1824c4140 (double) | 1.0 | множитель шага 1 = NO-OP при s888=1 |
| xmm13 | 1824c46a0 (double) | 1.0 | делитель бинов 1..n/2 (ИНВЕРСИЯ!) |
| xmm9 | xorps | 0.0 | обнуление верхних половин |
| xmm10 | 1824c4680 / 1824c3e28 | 1.0 / **0.8** | парный скаляр (0.8 активен в цикле) |
| xmm12 | 1824c3ea4 | 1.0 | парный скаляр 2 |
| xmm14 | 1824c4674 | 0.7 | (использование вне FIR) |
| xmm15 | 1824c4670 | 0.5 | (использование вне FIR) |
| xmm7 | вычисл. | sens·[540870]-цепочка | аргумент th2270 |
### СЛЕДСТВИЯ (меняют понимание построения кернела):
1. Перед EXP: бины 1..n/2 = log(bands) ⇒ после exp = **1/bands** (обратная
величина!), бины выше = exp(0)=1. Экспонента применяется НЕ к спектру маски
напрямую — вокруг неё twiddle-FMA стадии (ops B/C/D = радиальные проходы
FFT-класса над упакованным вещественным спектром; buf548 = cos/sin таблица,
mask598 = lane-select).
2. «×0.984» из 23d НЕ найден как константа цикла — либо внутри exp-IAT-обёртки,
либо следствие нормировки twiddle-стадий. Требуется численная репликация
пайплайна против живых захватов (rendersnap2 ph*.npz содержат готовые пары).
3. Шаг 1 и шаг ×s888 — no-op при дефолтных параметрах (s888=1, live 24j).
4. 52d920/52d990/52db50 = тонкие обёртки: 920→th2030/1d30 (scalar-op),
990→th2000/1c40 (axpy!), db50→то же. «УМНОЖЕНИЕ на окно» реализовано
axpy-кернелом, «деление» — scalar-op.
5. Ops A–D работают над дескрипторными векторами (проверка тега [obj]==6 в
4ca80; ошибка 0xfffffff3 при несоответствии).
### Динамика (попытки перехвата; окружение)
- realtime-playback НЕ тикает DSP (нет аудио-девайса; треды спят в futex);
кривые 22y = результат инициализации при загрузке проекта
- `-renderproject`: полный цикл init+render+exit хоста занимает ~0.8 c
(host@1.2s, workers@1.4s, wav@1.6s, exit@2.0s); обработка идёт в окне ~0.5 c
- INT3-ptrace: SEIZE+TRACECLONE обязательны до CONT (иначе untraced thread
ловит SIGTRAP и убивает процесс — подтверждено); DR-брейкпоинты: DR0@user+0x380,
но POKEUSER DR7 даёт EIO. Скрипты: fntrace*.py, fnhw.py, fnall.py, firstop.py,
firtrace.py, hotips.py (см. scripts/)
- Вывод: FUN_180529fe0 и 52d650/536300/52e260 НЕ ловятся в рендер-окне —
маск-цепь выполняется при ЗАГРУЗКЕ/изменении параметров, стационарный рендер
использует закэшированный кернел; либо трассировать надо момент инициализации
- Выход плагина НЕдетерминирован: md5 двух свободных рендеров различен при
одинаковом rms (PRNG-дизеринг из LCG-прологов) — метрика только спектральная!
## ДОПОЛНЕНИЕ 24t: ТОЧНЫЕ СЕМАНТИКИ СКАЛЯР-ОПОВ + опознание 563a60
Инструмент: scripts/disasm_func.py (полный дизасм функции с резолвом
RIP-констант инлайн).
### Тела скаляр-трансформов (float-ветки):
| стаб/impl | семантика | спец-случаи |
|---|---|---|
| th2030→ffe0 | dst[i] *= scalar | scalar==1 → скип; ==0 → zero-fill |
| th2270→14c40 | dst[i] += scalar | scalar==0 → скип |
Оба: скалярный хвост + AVX2 основной цикл (vmulps/vaddps ymm). Двойные
ветки (1d30/22a0) аналогичны на sd/pd.
### FUN_180563a60 = init-time ПОСТРОИТЕЛЬ таблицы (не per-bin!)
- Один caller: 1805636c2 (init-семейство).
- Константы пролога: xmm11=8.68589 (20/ln10 — ln⇒дБ!), xmm12=1/1024,
xmm14=0.5, xmm10=2, xmm9=1, xmm6=1; вызов IAT 181a14cd0 (log-класс)
над double из [obj+0x4198], ×8.68589 → дБ, запись пар во вектор
([obj+0xe0], рост через realloc 56c640).
- Вывод: строит дБ-доменную таблицу парами при param-rebuild — согласуется
с «LUT» ролью. Хвост с вирт. вызовом [rax+0x10] не декодирован.
### Алгебра T1: обе формы фитуют серию драйва (8 точек недоопределены)
- softplus α·ln1p(L/β)+c: rms 0.016 (α=3.2193 β=0.4927 c=0.542)
- LUT-форма t=((dBA)/(BA))^γ·M: rms 0.029 (A=36.2 B=20.0 γ=2.097 M=10.49)
Различить только декомпом тракта am/res→scratch или бОльшим числом точек.
### Открытые микровопросы
1. Значение xmm7 на входе th2270 в FIR-цикле (52b5f6): трассировка от
52a583; если mix^p=1 → «bands+=1» противоречит провалам ⇒ xmm7 иной
либо порядок аргументов иной.
2. Раскладка объекта-вектора (тег 6) для ops AD.
## ДОПОЛНЕНИЕ 24u: WIN_WINDOW движка — кусочное окно аудио-пути
Таблица WIN_WINDOW[8193] (dsp/tables_data.hpp, live-захват):
- [0]=0.500000, плавный подъём до [2048]=0.800000, затем СКАЧОК до 1.0
и единица до конца ([2049..8192]).
- Длина перехода = РОВНО 2049 сэмплов = число бинов кернела (4096-сетка)!
- Формула перехода не каноническая (ханн/синус/степенные не сошлись,
maxdiff ≥0.09); для репликации достаточно встраивания таблицы как есть.
- Структура намекает: блок аудио 8192 сэмплов ({16384,8192} из cfg движка),
первые 2049 позиций получают взвешивание 0.5..0.8 (область «кернельного
взаимодействия»?), остальное прозрачно; 0.8 подозрительно = blend.
Проверка употребления — в resize fe00 / audio-клее (следующий раунд).
### Движок dc30: карта инициализаторов (24u)
ctor 18052dc30 вызывает: 5335c0 (×2 — до/после валидатора), 534550 (×5 —
регистрация конфиг-итемов, аргументы edx из стека [rsp+0x30..0x4c] =
цепочка {2,4096},{16384,8192},{2,257}), затем ILT-стабы 2240/1c10.
Следующий шаг декода: тела 534550/5335c0 + употребление WIN_WINDOW
(по xref на таблицу или указатель из объекта).
## ДОПОЛНЕНИЕ 24w-3: ЖИВЫЕ КОНСТАНТЫ ГЕНЕРАТОРА 533340 (параметры частотных IIR)
Из scalar-bank (rendersnap2 v4+) на дефолтных параметрах multi:
```
p (экспонента) = [ctx+0x54087c] = 1.000000
tau1 = [ctx+0x540894] = 1200.000122
tau2 = [ctx+0x540898] = 180.000015
mult (стадия-3) = DAT_1824c4564 = 360
C_hz = DAT_1824c459c = 1000
константы 530b60 : 0.7, 2, 800→(×2)=1600, 1200, 15, 9, 180, 0.01,
[540898]=180*p+xmm6 (формула видна в дизасме)
```
Формула тела подтверждена дизасмом: g=min(fc_norm/i,(fc_norm/i)^p);
c=1/(1+g*tau/mult); up=exp(...); down=1-up.
Эксперимент: одноразовый bidir с этими коэф. НЕ двигает центр кривой
⇒ q-зависимость катов не через эти IIR напрямую; тракт между main-loop
и scratch содержит ещё этапы (ops AD / steps 919 BLOCKMAP).
## ДОПОЛНЕНИЕ 24z: FUN_180563a60 ДЕКОДИРОВАН ПОЛНОСТЬЮ — ЭТО ДЕТЕКТОРНАЯ LUT-ФОРМА!
Постройка (на каждый банд, 1024 точки, вход double[+0x4198+i·8],
выход пары-floats в вектор [+0xe0], stride вектора 0x18):
```
dB = log(input) · 8.68589
idx = i / 1024 → store[2j]
t = (dB A) / (B A) ; A=[sub+0], B=[sub+4] (sub=obj@[rcx+0x180])
t = clamp(t, 0, 1)
если γ(=[sub+0xc]) ≠ 1:
если [sub+0x10]==0: out = t^γ (классика)
иначе : out = sign(2t1)·|2t1|^γ + 1 (симметричный режим!)
else: out = t
out *= 0.5 → store[2j+1]
```
Виртуальная альтернатива: [sub+0x90]!=0 → вызов [sub vtbl+0x10](A,B,dB)
с клампом [0,1].
⇒ ЭТО ТА САМАЯ ФОРМА, ЧТО ДАЛА ФИТ cut=LUT(A,B,γ,M)! Стадия компрессии
уровня найдена материально: A/B/γ живут в объекте [band+0x180],
заполняются сеттерами. Осталось: снять их live-значения для наших конфигов
(расширить scalar-dump на [ctx+0x180]-объект) и найти потребителя таблицы
(интерполятор dB→out) в аудио-тракте.
## ДОПОЛНЕНИЕ 24cc: точная формула коэф. 533340 (дизасм+live)
[state+8] = n = 2049 (∀ трёх состояний; live probe_states.py).
```
fc_norm = (X/(sr·0.5))·n ; X=xmm2 (C_hz=1000 или tau — уточнить)
g(i) = min(fc_norm/i, (fc_norm/i)^p) ; p=[ctx+0x54087c]=1 @defaults
c = 1/(1 + g/mult) ; mult=360 (стадия-3)
arg = |c| · g / n ← НАСЫЩАЮЩАЯ форма (пол g→mult/n)
up[i] = exp(arg · (2π)) ; down[i]=1up[i]
```
При дефолтах down ∈ [0.003..0.19] — очень мягкие сглаживатели; эффект
только каскадом в полной цепочке шагов 9–19.
Осталось уточнить: роль xmm13 (множитель перед c), знак/конст экспоненты,
соответствие X=C_hz-vs-tau.
## ДОПОЛНЕНИЕ 24ff: семантика шага 11 (fma att/rel) — тройки (re,im,coef)
Цепочка вызова: ee20 → 487a0 → 1065c0; воркер получает r10=n/2·12 байт
⇒ элементы по 12 байт = (dst_re f32, dst_im f32, coef f32).
Call-site (52ac7052acd4):
```
шаг 11a: fma(rcx=f6f8_upper, rdx=ATT@[5406c8], r8=ACC_i, r9=n/2)
шаг 11b: fma(rcx=f6f8_lower, rdx=REL@[5406e8], r8=ACC_i, r9=n/2)
```
⇒ семантика: f6f8[k] += coef[k]·ACC_i[k] (комплексный axpy с пер-биновым
скаляром), верхняя половина спектра — с ATTACK коэф., нижняя — с RELEASE.
ACC_i персистентен между кадрами ⇒ утечный интегратор: стационар
ACC = input/(1coef); усиление 1/(1att): @43=2.57 @85=2.93 @171=3.42.
Коэф. массивы СТАТИЧНЫ (∀ конфигов бит-в-бит — проверено dual/qmap/sens18).
Гипотеза: резонансное усиление ACC объясняет k>1 аномалии уровней
(q2: impl/ours=2.48 ≈ A(85)); точная алгебра подачи ACC в кривую — в
остатке шагов 13–19.
## ДОПОЛНЕНИЕ 24gg: шаг 12 — ЭТО КОПИЯ (исправление BLOCKMAP)
th1b80→6840→1a5a0→181a646c0 = ЧИСТЫЙ MEMCPY (vmovdqu без арифметики).
«acc_i += bands[i]» из старого BLOCKMAP — НЕВЕРНО; реально копия
(направление/роли rbx/rbp уточнить трассировкой регистров через цикл —
аргументы на 52acf8: rcx=rbx, rdx=rbp, r8=n; значения rbx/rbp меняются
через 52ac98 [0x5406e8] и др. — нужен полный dataflow-проход шагов 9–19).
Следствие: если acc_i не накапливается сложением, «резонансная» гипотеза
24ff требует пересмотра — возможно ACC обновляется через fma шага 11
(ACC входит как источник), а шаг 12 синхронизирует массивы.
### Статус декода шагов 9–19 (24ff+24gg)
- шаг 11: f6f8[k] += att/rel_coef[k]·ACC_i[k] ✓ (тройки re,im,coef)
- шаг 12: COPY (не add!) ✓ исправлено
- шаги 1316: th2270-add ветка с xmm6=xmm12[54087c]; bigkernel 140950;
axpy 2000/1c40 с vec@540778 — тела не декодированы
- полный dataflow-проход = задача следующего раунда (связная, ~сессия)
## ДОПОЛНЕНИЕ 24hh: DATAFLOW шагов 9–12 (точный, из дизасма)
```
шаг 9a: vec698 *= (xmm12 [54087c]) ; th1a00=MUL; при 11=0 → ZERO
шаг 9b: vec6f8 += 0.8 ; xmm10=0.8 ([1824c3e28]); th2270=ADD
шаг 9c: bigkernel 140b60(vec6f8, bands_curve_i, vec6f8)
шаг 10: vec6f8 = bands_curve_i ACC_i ; dc40: out[r8]=rdxr10 ✓COMBINE
шаг 11: f6f8_upper += ATT[k]·ACC_i[k] ; тройки re/im/coef
f6f8_lower += REL[k]·ACC_i[k]
шаг 12: COPY(...) ; направление уточнить
```
Регистры: rbx↔vec698/6f8/acc_i, rbp↔bands_curve/[5406e8], r14=vec6f8.
Картина: строится КОРРЕКЦИОННАЯ кривая (bands−ACC), модулированная
att/rel-огибающими ACC — адаптивная петля редукции.
Остаток прохода: шаги 13–19 + эпилог + связь с FIR-секцией (52b3cd+).
## ДОПОЛНЕНИЕ 24ii: семантики шагов 13–16 + ИСПРАВЛЕНИЕ th2000
th2000/fb60 (и 1c40/8700) = ПОЭЛЕМЕНТНОЕ УМНОЖЕНИЕ МАССИВОВ
(dst[k] *= src[k]) — НЕ axpy! Исправляет интерпретации:
```
шаг 13 (флаг≠0): зеркало шага 9 (vec698*=..., vec6f8+=0.8·..., bigkernel)
шаг 14: bands_curve += (1.0) ; константа [1824c4680]=1 через th2270!
затем bigkernel 1409e0/140ad0 IN-PLACE на bands_curve
шаг 15: bands_curve *= track_i ; th2000 array-multiply
шаг 16: bands_curve *= kWarp@[5406a8] ; th2000 array-multiply
шаг 17(флаг): bigkernel in-place ещё раз
```
Картинка: кривая центрируется (−1), проходит нелинейность (bigkernel,
вероятно exp/abs — тела за IAT), модулируется track и warp.
Против log-входа design'а значение после этих шагов должно быть >0.
### Bigkernel-тела за ВЛОЖЕННЫМ диспатчем
Таблицы стабов содержат смесь IAT-слотов и внутренних адресов (напр.
table[7]=140a00), но внутренние ведут к call runtime-helper + НОВЫЙ
ILT-стаб с собственной idx-ячейкой (паковка/протектор). Статическое
разворачивание обрывается. Тела bigkernel'ов (exp/abs-нелинейность шага
14/17) остаются за пакером — при необходимости снимаются дампом памяти
ВОКРУГ вызова в рантайме (STOP + чтение таблиц после инициализации).
## ДОПОЛНЕНИЕ 24jj: ТЕЛА BIGKERNEL'ОВ НАЙДЕНЫ (рантайм-резолв IAT)
iat_name.py v2 (SIGSTOP + двойной deref + PE-экспорты) резолвит:
```
стаб 140b30/140b60 → runtime 1803a06a0 (общий для float/double!)
стаб 1409e0 → runtime 180296c80
стаб 140ad0 → runtime 180323f20
стаб 140a40 → runtime 1802dc0e0
```
Все — НАСТОЯЩИЕ функции внутри дампа (не импорты!): большие стек-фреймы,
x87 FNU-контроль, AVX2 полиномы, ДВЕ x87-трансцендентные инструкции
(fyl2x/f2xm1 класс = 2^x/exp семейство). Полный декод математики каждого —
отдельная сессия; вход/выход уже известны из контекста вызовов
(in-place над n/2+1 элементами FIR-буфера).
## ДОПОЛНЕНИЕ 24jj2: R@540788 — БАНДПАС-ФОРМА, НЕ совпадающая с нашим twin!
Сравнение (multi6, band1=500/q0.5/s12):
```
бин R@540788 наш_res R/наш
43 0.802 0.117 6.84
85 1.086 0.374 2.91
171 1.517 0.839 1.81
342 3.521 1.401 2.51 ← ПИК ~3.5-4 кГц!
512 2.910 1.673 1.74
684 1.829 1.811 1.01
1024 1.146 1.931 0.59
1536 0.689 1.988 0.35
```
Наш twin res растёт монотонно от fc; R@788 — БАНДПАС с пиком ~bin300-342
(~3.5-4 кГц) и СПАДОМ к Найквисту. Форма напоминает кривую равной
громкости / слухового взвешивания!
R@5407f8 = единичная нормировка (все 1.0000 в этом прогоне).
### ГИПОТЕЗА (проверяемая):
Детекторный уровень = am · ВЕС(f) / res(f), где ВЕС — кривая типа
равной громкости (R@788?). Тест: X=am·R/res против катов multi6 —
НЕ сошлось лобово (X@43=1.44 макс при мин кате) ⇒ взвешивание входит
иначе (до/после res-деления, или в log-домене).
### Ценность
Объясняет ВСЕ аномалии дальних бинов разом: наши дальние res слишком
велики (нет спада), их lvl занижен, каты недобираются. Формула ВЕСА —
ключ к кросс-конфиг параметризации.
## ДОПОЛНЕНИЕ 24kk3: слот [ctx+0x540668] ПОЛИМОРФНЫЙ!
Прямой проб во время рендера: [ctx+0x540668] содержит
`3fdbcd8940000000` = ДВА FLOAT (~0.434, 2.0), НЕ указатель!
(пробник probe_668.py; EIO на части чтений — слот мигрирует).
### Следствия:
1. rendersnap2 пропускает слот 668 (ptr<0x10000 или мусорный ptr) ⇒
во ВСЕХ новых захватах (sc_multi4b, sc_tt*, sc_q*, ...) НЕТ FIR-массивов.
2. Старые захваты rendersnap.py v1 ИМЕЛИ валидный FIR-указатель в эти
моменты (ловили фазу обработки). Данные старых phase*.npz про FIR —
валидны для своих моментов, но смешивать с новыми нельзя.
3. Все «FIR mag» анализы через этот слот зависят от ТОГО, в какой фазе
слот был пойман: указатель-на-буфер vs скаляры vs сброс.
4. Значения скаляров (~0.43, 2.0) — кандидаты: att/rel? dry/wet? g-компоненты?
### Статус
Канонический путь чтения ПРИМЕНЁННОЙ маски: слот [ctx+0x540678] (кривая
банды) — он стабилен и МАТЧИТ АУДИО в deep-фазах (±5%).
## ДОПОЛНЕНИЕ 24mm2: ПОЛНАЯ РЕЗОЛЮВСЯ ВСЕХ 10 ЯДЕР + ИСПРАВЛЕНИЯ DATAFLOW
Инструмент: статический резолв цепочки стаб→таблица→L2→IAT-слот по
soothe_mem.bin (без live). Стаб = `movsxd rax,[idx@1826159a0]; lea r10,[tbl];
jmp [r10+rax*8]`, idx=4, L2=`mov rax,[slot]; jmp rax`.
### Таблица резолва (исправляет 24jj!)
| стаб | таблица | runtime | опознание по константам |
|------|---------|---------|------------------------|
| 140950 | 182617448 | **18026b820** | exp2/exp DOUBLE (ln2, log2e, 1021.5, 2^27) |
| 140980 | 182617488 | **18027c120** | **logf** FLOAT (ряд −½,+⅓,−¼,+⅕,−⅙; ln2 hi/lo; 2^32) |
| 1409b0 | 1826174c8 | **18028d1e0** | **powf/log+exp** DOUBLE (ряд log + магия expf вместе) |
| 1409e0 | 182617508 | 180296c80 ✓ | **expf** FLOAT — ДЕКОДИРОВАН ПОЛНОСТЬЮ (ниже) |
| 140a40 | 182617588 | 1802dc0e0 ✓ | exp-вариант FLOAT c hi/lo сплитами |
| 140aa0 | 182617608 | **18030fee0** | **sincos** DOUBLE (1/6,1/120,1/5040; π hi/lo) |
| 140ad0 | 182617648 | 180323f20 ✓ | кусочно-табличная DOUBLE (сетка Δ=0.00541521) |
| 140b00 | 182617688 | **180367980** | pow/exp DOUBLE (1023/1022, магия 1.5·2^20) |
| 140b30 | 1826176c8 | **1803831c0** | кусочно-табличная FLOAT (π/2, π/4, сетка 184.665!) |
| 140b60 | 182617708 | 1803a06a0 ✓ | **DIVIDE** FLOAT B/A (rcp+квантование+vpermps-таблицы+полином невязки) |
**ИСПРАВЛЕНИЕ 24jj**: «140b30/140b60 → общий 1803a06a0» — НЕВЕРНО.
FIR-секция вызывает 140b30 = 1803831c0 (табличная кривая), divide только в шаге 9c.
### ДИСПЕТЧЕР float/double
Каждый call-site имеет ПАРУ стабов через `call [181bab008]; test eax,eax; jne`:
float-стаб (eax==0) / double-стаб. Дескрипторы type_info СТАТИЧЕСКИ идентичны
(оба →182650db8) ⇒ eax=0 ⇒ **double-ядра мертвы на нашем пути**; рендеры
идут по float. Double-тела не транскрибируем (отмечено на будущее M8).
### expf 180296c80 — полная формула (горячий цикл, FMA-точно)
```
n = fma(log2e_hi=1.4427, x, MAGIC=12582912.0) ; округление до int
k = n MAGIC
r = (x 0.693146·k) 1.42861e-06·k ; ln2 hi/lo
p = (((0.00829172·r + 0.0418735)·r + 0.166674)·r + 0.499994)·r + 1)·r + 1
out = bits( (k<<23) + bits(p) ) ; vpaddd сборка
guard: |x|>87.3365 → slow-path; head/tail через vmaskmovps+popcnt-маски
```
Коэф. минимаксные — транскрибировать КАК ЕСТЬ.
### DIVIDE 1803a06a0 — структура (90%)
```
A=[rcx], B=[rdx], dst=[r8]; r9d=n
q0 = rcp(A); q0 += 2^23-magic (округление); q = q0 & 0xfff00000 ; 12 бит
e = (q>>23); idx = q>>20 → vpermps tbl@1821269c0 (127±ε) и @182126a00
err = 1 q·A
полином невязки {0.207515, 0.241687, 0.288535, 0.360671, ..., 0.240264, 0.0555119}
сборка через магию 1.5·2^20 + vpslld 20
результат ≈ B/A с точностью ~0.5 ulp
```
Таблицы коррекций сдамплены (per-mantissa-top-bits).
### ИСПРАВЛЕНИЯ DATAFLOW (по fn529fe0.dis, адреса call-sites)
1. **Шаг 14 порядок ОБРАТЕН к BLOCKMAP 24ii**: сначала `bigkernel exp IN-PLACE
на bands_curve` (52ae0e), ПОТОМ `bands_curve += (1.0)` (52ae40, конст.
1824c4680 через th2270).
2. Шаг 9b точно: `vec6f8 += [ctx+54087c] · 0.8` (xmm10=0.8@1824c3e28,
множитель виден в asm: mulss xmm6,xmm10 после movss xmm6,[54087c]).
3. Шаг 9a: `vec698 *= (xmm12=1.0 [54087c])` ⇒ zero-fill при дефолтах ✓.
4. Шаг 10 combine dc40: аргументы rcx=ACC_i(**таблица указателей @0x5407c8**,
НЕ дампилась rendersnap2!), rdx=bands_curve_i(@678+i), r8=vec6f8(@6f8),
семантика dst=r8: vec6f8 = bands_curve_i ACC_i. ACC-слот надо ДОБАВИТЬ
в SLOTS rendersnap2 (0x5407c8).
5. Шаги 15/16 подтверждены: th2000/th1c40 array-mul; затем rbx=[5406a8]
(kWarp) — array-mul на bands_curve.
6. Эпилог: скалярная часть из decomp (consumers_out 100-143): mix-веса,
`fVar17 = [540874] expf(DAT_1824c4704=-ln1000)` → bands += f17·[540888].
### Call-site карта больших ядер (fn529fe0)
```
52a63a/52a641: 140980(logf-float)/1409b0 — pre-combine #1
52ab84/52ab8b: 140b60(divide)/140950 — шаг 9c
52acd? : (шаги 1012 мелкие ILT)
52ae0e/52ae15: 1409e0(expf)/140ad0 — шаг 14 нелинейность
52b32c/52b336: 1409e0(expf)/140ad0 — шаг 17 (повтор)
52b3a0/52b3aa: 140a40(exp-var)/140b00 — пост-17
52b716/52b71d: 140b30(кривая-float)/140aa0 — FIR-секция
```
## ДОПОЛНЕНИЕ 24mm5: ПОЛНАЯ КАРТА ТРАКТА — буферы каждого шага; design = conv-тело 22z
### Полоса-цикл (float-путь), трасса регистров 52a580–52b3cd
```
пре: [678i] *= скаляры (s888-цепь, xmm7·[540870]·[54088c])
LOG#1 (140980!) на [678i] ; 52a63a — В ЛОГ-ДОМЕН заранее
combine 52d650([678i],[6f8])
шаг 9a: vec698@698 *= (1[54087c]) ; zero
шаг 9b: vec6f8@6f8 += [54087c]·0.8
шаг 9c: DIVIDE dst=[678i]: A=arg(rcx)=[678i], B=arg(rdx)=[6f8]
⇒ [678i] = vec6f8 / bands_curve ; in-place
шаг 10: dc40: rcx=ACC_i(@7c8+i!), rdx=[678i], r8=[6f8]
⇒ vec6f8 = bands_curve ACC_i ; ACC — таблица указателей 7c8
шаг 11: fma ATT(@6c8)/REL(@6e8) — пары вызовов 1fa0/1940
шаг 12: COPY 1b80/1d60 c [678i]
шаг 13: зеркало 9a/9b + оп 1eb0(cbe0)([678i],[6f8])
шаг 14: EXP#1 (1409e0=expf) на [678i]; затем += (1)
шаг 15: array-mul: X[rsp+0x40] *= [678i] ; НЕ bands*=track!
шаг 16: [678i] *= kWarp@[5406a8]
LOG#2 (140980) на [678i] ; 52aefd — возврат в лог!
IIR4 ×2 бидир ; ~52af0952b2b6, СПЕКТРАЛЬНОЕ
; СМЕШЕНИЕ В ЛОГ-ДОМЕНЕ
скаляры xmm14(0.7)/xmm15(0.5)-класс
шаг 17: EXP#2 (1409e0) на [678i]; += scalar; exp-var 140a40 финал
→ bands_final @678i
```
### FIR-секция (52b3cd52b94a)
```
bands_final *= s888, *= [540888]; += xmm7 (скаляр с expf(ln1000)=0.001)
DESIGN: call 535a70(rcx=scratch@628, rdx=bands)
535a70 = диспетчер СО СВОПОМ аргументов → ILT 140a10/140a70 →
→ РЕЗОЛВ: float=1802a24c0 (!!!), double=1802fa420
⚡ ЭТО ТЕЛО FFT-CONV ИЗ ОТКРЫТОГО ВОПРОСА 22z («conv_float_a24c0.dis»,
184K AVX2). Дизайн детектора == недекодированный conv. Пазл склеен.
дальше: copy th2210; complex-op th2180/th1bb0 с твидл-буферами
548/550/598; знак 1 (52d920); EXP 140b30(=1803831c0);
окно 52d990(WINfreq); pair-scalar 1880/1ca0; *= wet[540888];
финал df0(FIR, track_i)
```
### Где γ=1.760561
mask = bands_final^γ точно ⇒ γ возникает между scratch=log(bands_final)
и финальной маской: либо ВНУТРИ design 1802a24c0 (масштаб на выходе),
либо в комплекс-op цепочке 52b64452b716 перед EXP 140b30. Обе точки
локализованы до ~десятка инструкций — декод следующего раунда.
### Исправление понимания слотов
- 688 = exp(628) тривиально: 628 — копия лога bands_final (design),
688 — сами bands_final (или их exp-копия). «track» — имя рендерснапа.
- 678 ПОСЛЕ цикла = bands_final; применённая маска перезаписывает
поверх (финальный combine) — поэтому захваченный 678 матчит аудио.
## ДОПОЛНЕНИЕ 24mm6: ПЕРЕД EXP В FIR — УМНОЖЕНИЕ НА 2.0 (не −1!); гипотеза γ=2·k_design
### Точная последовательность 52b60c–52b720 (проверено, без пропусков)
```
rcx=[540628](scratch), rdx=[r15](источник design — уточнить r15!)
call 535a70 → swap → 1802a24c0(scratch ← DESIGN(src))
th2210: FIR(@540668) ← scratch (copy, edx=0)
opB: th2180(FIR, buf548|550, buf598) ; complex pass
FIR[n]=0
FIR[1 .. n/2] *= xmm13 = 2.0 @1824c41e0 ; 52d920, БЫЛО «−1» в 24l — НЕВЕРНО
FIR[n/2+1 .. n-1] *= xmm9 (=0) ; 52db50
opC: th1a90(FIR, buf548|550, buf598) ; complex pass
EXP in-place 140b30 (float) / 140aa0 (double)
```
xmm13/xmm9 не перезаписываются между 52b3d6 и использованием (проверено).
### Гипотеза источника γ
Если opB/opC сохраняют пропорциональность (упаковка real-FFT), то
mask = exp(2 · scratch) ⇒ γ = 2·k, где k — масштаб выхода design
1802a24c0 относительно ln(bands): k = 1.760561/2 = 0.8802805.
Альтернатива: k=1, а opB/opC суммарно дают множитель 0.88028.
### Открытые микровопросы (следующий раунд, всё локализовано)
1. Что такое [r15] на входе design (bands_final@678 или иной буфер)?
2. Семантика opB/opC (th2180/th1bb0/th1a90/th19d0 + твидлы 548/550/598)
— вероятно упаковка/развёртка real-FFT.
3. Масштаб выхода design: декод хвоста 1802a24c0 (файл уже есть:
nls_dasm/conv_float_a24c0.dis, 184K).
4. Согласование с identity-фазой захватов (гонка финального combine).
## ДОПОЛНЕНИЕ 24mm7: design выход = точный ln(bands_final); γ создаётся после design
### Численный тест (multi6/ph034, identity-фаза)
```
scr@628 ln(cur@678): max|r| = 9.0e-08 (float32 eps) на 1013 бинах
⇒ k_design = 1 (в момент захвата)
```
Оговорка: станционарность делает «свежий» и «сталый» scratch
неразличимы; но факт (scr, cur)=(лог, значение) одной пары твёрд.
### Следствие для γ
γ=1.760561 ≠ 2 ⇒ множитель НЕ только «×2 перед EXP». Источники:
(a) opB/opC не взаимно сокращаются (не чистая упаковка real-FFT);
(b) пост-exp шаги: окно 52d990 (варьируется по позиции — нарушил бы
степенной закон, значит действует на верхнюю половину/после),
pair-scalar th1880/th1ca0, финальный combine df0(FIR, track_i),
где track=exp(scr)=bands_final.
Комбинации дающие γ из {1,2}: 1+2x=1.760561 ⇒ x=0.3802805;
либо лог-доменное смешение track^a·FIR^b c a+2b=1.760561.
### Статус декода design 1802a24c0
AVX-512 (zmm, masked {k3}/{k4}), 3822 строки objdump — трансформ-класс.
Для замыкания γ его полный декод МОЖНО НЕ НУЖЕН: достаточно семантики
opB/opC + df0 (десятки инструкций в fn529fe0.dis).
## ДОПОЛНЕНИЕ 24mm8 (финал захода): opB/opC/df0 резолвлены
```
opB: 180002180→180004ca80(f)/18001d160(d) ; дескриптор-оп (тег [obj]==6)
opC: 180001a90→18001a0c0(f)/180018400(d)
df0: 18000df0→18000b3c0 ; f70→18000e360 ; финальный combine
```
Все четыре микровопроса 24mm6 закрыты или локализованы до тел-обёрток.
Следующий раунд: семантика 4ca80/1a0c0 (кандидаты источника γ=2k−масштаба),
затем полный numpy-конвейер.
## ДОПОЛНЕНИЕ 24mm9: opB/opC = RFFT-близнецы; df0 = complex-mul; цепь валидирована 0.0065 дБ
### Слой вызовов FIR-секции (уточнение поверх 24l/24mm6)
```
обёртки: th2180 impl=125e0, th1a90 impl=5560 — только перестановка аргументов:
воркер получает (rcx=data, rdx=data, r8=ПЛАН, r9=WORK), ин-плейс.
ПЛАН = [ctx+540548] (buf548!): tag=6 [+0], log2n=12 [+4], flag [+8]=0,
scale_flag=1 [+0xc], scale=2^-12 [+0x10], workbytes=16384 [+0x18].
WORK = [ctx+540598] — рабочая область FFT (заметение «lane-mask» из 23b).
th2180 → воркер 4ca80(f)/1d160(d): INVERSE real-RFFT (голова: X[0]±X[Nyq]).
th1a90 → воркер 1a0c0(f)/18400(d): FORWARD real-RFFT (хвост: пакинг Nyq).
тела: импортные близнецы 181b853e0(inv)/181b81b80(fwd); константы только
±0.707107; масштабов нет. ffe0 = ×scale pass (skip при scale∈{0,1}).
copy th2210 → 136e0 → 4d900(src,dst,n): pack re=v, im=0 (vunpcklps+zero).
df0 18000b3c0: ПОЭЛЕМЕНТНОЕ КОМПЛЕКСНОЕ УМНОЖЕНИЕ dst=[rdx]=arg2:
track_i := track_i ⊗ FIR (vfmaddsub213ps; f70/b560 — double версия).
EXP 140b30 → 1803831c0: полиномиальная комплексная exp (без таблиц значений):
magic 12582912 (=2^23·1.5), guard 87.33654, редукция 184.665≈128/ln2,
коэф. {0.01604,1.541667(=37/24), 3.166e-05, 1.008329, 1.65777e-06,
0.01932, 0.00134, 0.00541687, 10000, 4.19179}; AVX-512+FMA.
Численно = поточечный комплексный exp (flat-exp проигрывает 8 дБ).
```
### Полная последовательность (52b60c–52b893, все шаги, без пропусков)
```
design 535a70(scratch@628 ← ln(bands_i)) ; 52b62f, своп аргументов
copy 2210(scratch → FIR, 2049 пар (re,im=0)) ; 52b644
FIR[4096]=0 ; 52b685 Найквост ДО фолда
inv-RFFT opA ; 52b672 th2180
fold: float[1..2047]*=2.0 (xmm13@1824c41e0) ; 52d920
float[2049..4095]=0 ; 52db50
fwd-RFFT opB ; 52b6e1 th1a90
EXP in-place, аргумент×q (q≈0.80, источник ОТКРЫТ); 52b716
inv-RFFT opC ; 52b74b th2180
FIR[4096]=0 ; 52b76d
float[0..2047]*=WINfreq[2048..4095] ; 52d990 (падающий Hann)
float[2048..4095]=0 ; 52db50
fwd-RFFT opD ; 52b7ba th1a90
FIR[0]=1.0f; FIR[1]=0 ; 52b7cd
(flag f890≠0: pair-scalars 1880/ca0 — live мертво)
th2030(FIR, wet=s888, 2n float) ; 52b857, s888=1 no-op
df0(FIR, track_i, n): track_i := track_i ⊗ FIR ; 52b893
```
Смысл: классическое минимально-фазовое ядро через кепстр
(IDFT лога → фолдинг ×2 причинной части + усечение → exp → обратный ход).
### Валидация и γ
mask_sim = trk·|F(q)|: 60 ультрачистых кадров, ВСЕ 2049 бина:
rms мед 0.0065 дБ / p90 0.0075 / max 0.035 при q=0.80 (порог 0.05 ✓).
γ = 1 + s_F(q), s_F = наклон log|F| по log trk в нотче: q=0.8 ⇒ γ_pred=1.7516
(точный 1.760561). Открыто: место q в асме (внутренность 1803831c0);
unicorn не эмулирует FMA ⇒ нужен статдекод ядра или live-захват входа EXP.
Дизасмы: /tmp/opencode/cascade/{wrapA_125e0,wrapB_5560,opB_4ca80,opC_1a0c0,
df0_b3c0,h_ffe0,h_136e0*,imp_b8*3e0_full,bk_1803831c0}.dis
(*copy: python3 scripts/disasm_func.py 1800136e0 — ВАЖНО: полный VA,
короткая форма «125e0» даёт пустой файл!).