# Спектроанализатор REFV (DualTachyon) — где он лежит

Разбор по образу `refw/rt-4d-refw-beta42.bin`
(251 904 Б, md5 `c6ab8306b5512b21b1f49fda8df2b1f5`, база загрузки `0x08002800`).

Именно эту реализацию хвалят пользователи. Ниже — как до неё добраться и из чего она состоит.

---

## 1. Как спектр вызывается

Спектр **не является пунктом меню**. Это **функция назначаемой клавиши № 22** —
`Analog Spectrum` в списке функций горячих клавиш (`0x080213A9`, записи по 16 байт).

> В стоке та же функция есть под номером **21** — REFV сдвинул нумерацию, вставив
> `Bandwidth Toggle` на позицию 15.

Диспетчер функций клавиш: **`TBH @0x080094EC`**, 45 ветвей (`cmp #44` перед таблицей),
таблица с `0x080094F0`. Ветка 22 → **`0x08009CAA`**.

## 2. Точка входа `0x08009CAA`

```c
if (mode == 3 || mode == 4) return;      // в этих режимах спектр запрещён
bl 0x08010ADC;                            // подготовка
                                          // [area*5 + 0x84] — режим области:
                                          // 0=Frequency, 1=Channel, 2=Zone
if (area_mode == 2) bl 0x08015318;        // ← спектр ПО ЗОНЕ
else                bl 0x080139E4;        // ← ОБЫЧНЫЙ спектр
goto 0x08009BC0;                          // общий выход
```

Это ровно совпадает с официальным описанием REFV: «переключитесь в режим VFO или канала,
если нужен обычный спектр; из зоны он сканирует эту зону».

## 3. Обычный спектр — `0x080139E4` (инициализация)

```
0x080139E4  push {r4-r8,lr}
0x080139E8  bl 0x08014888              ; подготовка подсистемы
            ...                        ; сброс флагов состояния
0x08013A2C  mla r2, #0x30, chan, r3    ; запись канала = 48 байт
0x08013A30  ldr [r2+5] -> [r4+5]       ; копирование частоты RX/TX
0x08013A38  ldr [r2+9] -> [r4+9]       ;   в рабочий VFO
            ...                        ; полоса/режим: bfi в [r4], [r4+4], [r4+0xe]
0x08013A86  bl 0x08011164              ; применить настройки ВЧ-тракта
0x08013AA6  bl 0x080136EC              ; подготовка экрана
0x08013ABA  bl 0x08013944              ; отрисовка шапки
0x08013AC8  bl 0x08015978              ; печать числа (r1=2 разряда)
0x08013AF8  ite eq / 'W' : 'N'         ; метка полосы Wide/Narrow
0x08013B1C  bl 0x0801C62C              ; запуск
0x08013B22  movs r2,#0x0B
0x08013B24  strb r2,[0x200009B0]       ; ← ЭКРАН = 11
0x08013B26  pop
```

**Ключевой вывод: спектр — это экран № 11.** Архитектура та же, что в стоке
(`g_screen`, значения 0…11), только переменная в REFV живёт по адресу **`0x200009B0`**.

## 4. Модуль спектра целиком

| Адрес | Роль |
|---|---|
| `0x08009CAA` | точка входа с горячей клавиши, выбор обычный/зонный |
| **`0x080139E4`** | **инициализация обычного спектра**, ставит `экран = 11` |
| **`0x08013BA4`** | **тик**: автомат на 4 состояния, счётчик `[база+0x3E] & 3` |
| **`0x08013BFC`** | **обработчик клавиш** спектра, `cmp r0,#0x10` + `TBH @0x08013C20` (17 клавиш) |
| `0x08013598` | шаг развёртки (состояние 1) |
| `0x08012FA0` | рабочий обработчик (состояния 2 и 3) |
| `0x0801307C` | ветка при `состояние > 1` |
| `0x080135E8` | помощник (вызывается из `0x0801449C`) |
| `0x080136EC`, `0x08013944`, `0x08015978` | подготовка экрана, шапка, печать чисел |
| `0x08014888` | инициализация подсистемы |
| **`0x08015318`** | **спектр по зоне** — отдельная реализация |

### Автомат тика `0x08013BA4`
```c
state = (state + 1) & 3;            // [база+0x3E]
switch (state) {
  case 1: flags[1]=flags[2]=0; bl 0x08013598;  break;   // шаг развёртки
  case 2: flag=0;  goto 0x08012FA0;                     // обработка
  case 3: flag=1;  goto 0x08012FA0;
  default:         goto 0x0800A57C;                     // пометить перерисовку
}
```
Развёртка размазана по тикам главного цикла — экран не блокируется, поэтому спектр
и ощущается отзывчивым.

### Спектр по зоне `0x08015318`
Перебирает **200 каналов** зоны (`cmp r6,#0xC8`), запись зоны — **0x1BD байт**:
```c
for (i = 0; i < 200; i++) {
    ch = zone[i].channel;                       // halfword, отбрасывается если >= 0x400
    if (!(zone.scanlist_bitmap[i>>3] >> (i&7) & 1)) continue;   // битовая карта scan-list
    read_channel(sp, 0x30*ch + 0x4000);         // запись канала 48 байт из SPI
    // разбор полей: частота ubfx(…,8,12), CTCSS, полоса, режим
    // укладка в массив с шагом 28 байт
}
```
Отсюда и фича «зонный спектр показывает живую информацию о субтонах».

## 5. Прочие детали

- **Перерисовка**: `0x0800A5FC` — `TBB` на 12 экранов (`cmp #0x0B`), каждая ветка делает
  `memset(флаги_экрана, 1, N)`. Для экрана 11 — 16 элементов по адресу `0x200000A9`.
- **Идиом связки экранов** встречается в 6 местах:
  `if (screen == 11 || (prev == 11 && (screen & ~4) == 8))` — экраны 8 и 12
  накладываются поверх спектра (меню/подсказки).

## 6. Что даёт REFV сверх стока (полная таблица UI, 338 записей)

Новые функции горячих клавиш: `Bandwidth Toggle`, `Talker Alias`, `RX AM/FM/**SSB** SW`,
`DMR DCDM Switch`, `DMR TS/CC Switch`, `DMR Key Select`, `Next Zone`, `DTMF List`,
`Freq Monitor`, `Last Call Info`, `Address Book`, `Contacts List`.

Новое в меню: верхнеуровневое **`Extra`**, `Extra hotkey` + `0…9 Hotkey`,
`Edit Scan List` в зонах, `SMS Templates`, `Time Calibrate`, `AM/FM/SSB RX` в канале,
`Last Channel`, `Skip Scan`, `Radio Info`, `Flash Type`.

## 7. Как это перенести к нам

Прямой перенос бинарного кода невозможен — REFV написан с нуля, у него своя карта
памяти и свои структуры (запись канала 48 Б, запись зоны 0x1BD Б, экран в `0x200009B0`).
Переносится **архитектура**:

1. Спектр — отдельный экран в общей диспетчеризации, а не модальный цикл.
2. Развёртка разбита на состояния по тикам, поэтому UI не подвисает.
3. Вход — функция горячей клавиши, а не пункт меню.
4. Отрисовка перерисовывает только помеченные элементы (массив флагов).

В нашей прошивке всё это ложится на уже найденные точки: хук `0x080207E6` (отрисовка) и
`0x08020816` (клавиши) в `ui_tick_normal @0x080207DC`, диспетчеризация по
`g_screen @0x200008B3`. Обязательно **вызывая оригинальный обработчик** — см.
`docs/firmware-parts.md`.
