Files
soothe2-re/handoff/BLOCKMAP_529fe0.md
T

290 lines
20 KiB
Markdown
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.
# 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-прологов) — метрика только спектральная!