# Запуск, меню и состояния старой прошивки

Исследован `C:\dump\fw_128k.bin`, восстановленные начальные данные RAM и сохранённый дизассемблерный листинг `Untitled-1.txt`. Дополнительно 20 проверок выполнили **оригинальные ARM-инструкции на программном эмуляторе**. Вызовы дисплея, GPIO и энергонезависимой памяти заменены записывающими заглушками; физический прибор не подключался.

Полный протокол: `evidence/ui-verification.json`; воспроизводимый запуск: `tools/verify_ui.py`. Протокол содержит SHA-256 исследованного BIN, адреса вызовов, аргументы и полученные переходы. Это проверка управления программой; работоспособность исполнительных узлов и длительности в реальных секундах таким способом не проверяются.

## Основная архитектура

Вход прикладной программы — `0x104A4`. Она последовательно вызывает настройку периферии, дисплея и данных, затем регистрирует через `0xDAB4` четыре задания и переходит в постоянный диспетчер `0xDC28`.

| Обработчик | Аргумент периода | Установленная роль |
|---|---:|---|
| `0x1056C` | 10 | Основная машина состояний, обновление измерительного пути и обработка текущего экрана |
| `0x1534` | 10 | Управление сигнализацией с проверкой флагов каналов и состояния прибора |
| `0xFFF8` | 2 | Преобразование событий кнопок в команды |
| `0xD898` | 5 | Обработчик очереди/кадров с собственным состоянием; точное назначение здесь не установлено |

В обработчике SysTick `0xE590` счётчик делится на 10: только каждый десятый вызов прерывания обслуживает таймеры заданий. Поэтому аргумент «10» не следует автоматически называть 10 мс. Дополнительная проверка даёт для него номинально 100 мс при нормальном начальном тактировании — см. [timing.md](timing.md). Диспетчер исполняет готовые задания по очереди; длительный обработчик способен задержать остальные.

Основное состояние — байт `0x20000094`. Структура UI начинается с `0x200014D0`, её общий счётчик — слово `0x200014D4`. Функция `0xCF00` устанавливает несколько байтов состояния UI и обнуляет счётчики. `0x1ED8` очищает экран: восемь страниц по 128 записей нулевых байтов.

В `0x1056C` при состоянии **не менее 10** вызываются `0x1A8C`, `0x11478`, `0xAEC4`, `0x1924`, `0x1BC4` **до** обработки отдельного экрана. Это означает, что вход в большинство меню не останавливает этот общий вычислительный путь. Уточнения его расчётов находятся в отчёте об измерениях; один числовой тест `state >= 10` нельзя считать полноценной моделью допустимости измерений во всех режимах.

## Реальная последовательность запуска

Компилятор поместил таблицу переходов сразу после вызова `0xF1D2` по адресу `0x105FE`. Её байты по `0x10602` были перепроверены непосредственно в BIN. В первоначальном экспорте Ghidra часть этих переходов была ошибочно представлена данными/инструкциями. Вход `0x1054C` из первой автоматической декомпиляции ложный: там лежит литерал, настоящее начало обработчика — `0x1056C`.

| Состояние | Обработчик | Что делает | Следующее состояние |
|---:|---|---|---|
| 0 | `0x4604` | Проверяет вход GPIO; по одному пути входит в режим зарядки, по событию `0x2000` читает показатель питания через `0x1F04` и сравнивает с `0xD48` | 1 либо 2 при выполнении условий; единицы показателя питания здесь не установлены |
| 1 | `0x3830` | Экран зарядки: по GPIO выбирает `Charging`/`Charge Over`, обновляет индикацию; событие `0x800` начинает запуск | 2 |
| 2 | `0x5E70` | Показывает графический ресурс, выдаёт повторяемые импульсы GPIO | 3, после 34 вызовов обработчика в контрольном исполнении |
| 3 | `0x5670` | Показывает `Welcome`, выдаёт короткую последовательность импульсов GPIO | 6, после 8 вызовов |
| 6 | `0x5570` | Показывает `Starting` и `Please Wait`, меняет точки анимации; ждёт счётчик больше 30 | 4, после 31 вызова |
| 4 | `0x39F4` | Поочерёдно обрабатывает четыре канала, вызывает загрузку/проверку их данных `0x61D0`, затем читает блок калибровок и служебные параметры памяти | 5, после 40 вызовов при заглушённых зависимостях |
| 5 | `0x4848` | Последовательно показывает `Led Check`, `Shock Check`, `Audio Check` и переключает соответствующие выходы | 10, после 48 вызовов |
| 10 | `0x5390` | Рабочий экран; перебирает отображаемые каналы, обрабатывает команды пользователя | Событие `0x800` открывает меню 20 |

Таким образом, реальный маршрут — `2 → 3 → 6 → 4 → 5 → 10`. Порядок номеров не отражает порядок этапов. Указанные числа вызовов измерены в отдельных исходных обработчиках с восстановленной начальной RAM; значения периферии и задержки задач при настоящем запуске могут менять итоговое поведение.

Надпись `Starting` с фиксированным ожиданием **не доказывает контроля физической стабилизации газового сенсора**. В исследованном `0x5570` нет критерия готовности по разбросу измерений или по отдельному датчику — переход происходит по счётчику. В `0x4848` проверка сигнализации является последовательностью команд включить/выключить: в этой функции не найдено чтения обратной связи, которое подтверждало бы реальный звук, вибрацию или свет.

Управление выходами установлено по веткам с подписанными экранами: `Led Check` переключает маску `0x40` в блоке `0x40000C00`; `Shock Check` — маску `0x20` в `0x40000C40`; `Audio Check` — маску `0x10` в `0x40000C80`. Это программное сопоставление по UI и вызовам `0x91B4`/`0x91D2`, не прозвонка платы и не гарантия активного уровня электрической схемы.

## Кнопки и меню

Обработчик `0xFFF8` получает от распознавателя кнопок `0xC9C0` категорию события и номер кнопки. Для категории `0x10` номера 1/2/3 преобразуются в `0x1000`/`0x800`/`0x400`; для категории `0x45` — в `0x8000`/`0x2000`/`0x4000`. По использованию это разные виды нажатия; точные времена удержания и физические подписи кнопок требуют отдельной проверки.

Основной обработчик `0x1056C` копирует накопленный код из `0x20000098` в `0x2000009C` и очищает источник. В меню `0x4684` команды `0x400`/`0x1000` меняют пункт с циклическим переходом между 1 и 9, `0x800` выбирает его.

| Пункт меню | Название, подтверждённое таблицей UI | Переход при выборе |
|---:|---|---|
| 1 | `Zero Adjust` | `0x50`: ввод сервисного кода |
| 2 | `Cal Gas` | `0x5A`: подменю калибровки при флаге `0x200000CE != 0x5A`; иначе `0x50` |
| 3 | `Alarm Set` | `0xD2`: выбор канала |
| 4 | `Alarm Rec` | `0x78`: журнал |
| 5 | `Time Set` | `0xA0`: настройка даты/времени |
| 6 | `Language` | `0xAA`: язык |
| 7 | `Factory mode` | `0x50`: ввод сервисного кода |
| 8 | `DefaultSetting` | `0xB6`: экран подтверждения восстановления |
| 9 | `Exit` | 10: рабочий экран |

Имена подтверждаются `0x75E8` и восстановленной английской таблицей: `0x76DC` использует запись размером 8 байтов по `0x200007FC + ID*8`, указатель строки находится в её поле `+4`. Это связывает текст с используемым пунктом, а не просто отмечает наличие строки в файле.

У большинства исследованных меню общий таймаут `counter > 0x6E` возвращает в рабочий экран. У `Select Gas`, обработчик `0x3B74`, порог другой — `counter > 0x32`. Число вызовов до возврата зависит от начального значения счётчика; для заводского меню `0x5D50` эксперимент подтвердил 110 вызовов без ввода. Таймаут — часть поведения UI, которую стоит явно задавать для каждого режима.

## Вход в калибровку и заводские действия

`0x4330` действительно реализует `Input Password`: две редактируемые десятичные цифры и сравнения с фиксированными константами, зависящие от выбранного пункта. Для пунктов Zero Adjust/Cal Gas после успешного сравнения открывается выбор канала/подменю калибровки. Это не доказательство общей защищённости сервисного доступа; чужие фиксированные коды переносить в собственную прошивку не следует.

`0x3338` показывает `Cal Gas`, далее `0x6180` рисует три варианта: `Zero Cal`, `Other Cal`, `Exit`. Первые два ведут в `0xD2`; там `0x3B74` циклически выбирает канал 0–3 и направляет в обработчик Zero Adjust (`state 0x1E`), Zero Cal (`state 0x28`) или Other Cal (`state 0x64`) в зависимости от выбранной операции. Пункты `Stand Value`, `Real Value`, `Zero Value`, `Mark point` нужно связывать с конкретными стадиями расчёта по отчёту о калибровке, а не трактовать по одним словам.

Заводские действия разделены:

- `state 0xB4`, функция `0x5D50`: выбор `Default Setting`/`Save Data`;
- `state 0xB5`, функция `0x5CA0`: `SaveCurrentData` + `As Factory Data`; команда `0x1000` вызывает `0x112E8`, `0x400` выходит без такого вызова;
- `state 0xB6`, функция `0x5C00`: `Restore Default`; команда `0x1000` вызывает `0x11150`, `0x400` выходит без такого вызова.

Эти четыре варианта подтверждения/отмены проверены на исходных инструкциях с подменой функций хранения. На реальном приборе действий не выполнялось.

Важная особенность загрузки: `0x39F4` после чтения EEPROM `0xEC` проверяет `value != 0x5A || value != 0xA5` и поэтому всегда присваивает RAM-флагу `0x200000E9` значение `0xA5` (`0x3B24…0x3B34`). Это подтверждается инструкциями сравнения и ветвления. Назначение флага следует устанавливать по потребителям; саму ошибочную проверку двух допустимых значений воспроизводить в новой прошивке не нужно.

## Имена газов действительно индексированы, но каналы задаются конфигурацией

`0x77E8` в английской ветке берёт имя из `0x20000A8C + type*11`. Начальная RAM содержит точное соответствие `0 → EX`, `1 → O2`, `2 → H2S`, `3 → CO`, `11 → CH4`, `15 → CO2`, `23 → VOC`. Полный список из первых 25 записей сохранён в протоколе.

В `0x3B74` тип для вывода берётся из конфигурации канала: `0x20000EA3 + channel*0x13 + 2`. Поэтому **тип 1 означает O2, но канал с индексом 1 не обязан всегда быть кислородным**. `EX` — именно текст прошивки; конкретный сенсор, принцип измерения и физическая распиновка из этой надписи не следуют.

## Что заложить в будущую прошивку

1. Явные состояния питания, зарядки, загрузки настроек, готовности датчиков, проверки сигнализации, измерения и обслуживания. У каждого состояния должны быть входные действия, критерий завершения, таймаут и реакция на ошибку.
2. Готовность измерения должна иметь собственный статус. Анимация запуска и фиксированная задержка не заменяют проверку валидных данных, калибровки и условий готовности сенсора.
3. Разделить тест включения исполнительного узла и подтверждение его исправности. Если аппаратная обратная связь не предусмотрена, интерфейс не должен заявлять, что электрическая проверка доказала работу звука/вибрации/света.
4. Обработку измерений и тревог вызывать с определённым периодом независимо от рисования экранов и редактирования меню. Поведение во время калибровки, зарядки и восстановления настроек описать явно.
5. Для кнопок определить короткое/долгое нажатие, повтор, подавление дребезга и отмену; для редакторов — таймаут, сохранение и выход без записи. Не распределять эту политику неявно по обработчикам экранов.
6. Отдельно хранить заводские параметры и рабочие настройки; подтверждать восстановление и сохранение калибровки, отражать результат записи и делать его проверяемым после перезапуска.
7. Разделить индекс физического канала, тип газа и модель датчика. Название газа на экране должно следовать проверенной конфигурации, а возможность выбора газа не должна создавать видимость физической смены сенсора.

Наличие этих программных состояний в старом приборе не определяет окончательные параметры и требования нового устройства. Для будущей модели полезны структура сценариев и проверяемые переходы; длительности, коэффициенты, электрические настройки и допуски потребуют своих данных.
