Запуск, меню и состояния старой прошивки
Исследован 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. Диспетчер исполняет готовые задания по очереди; длительный обработчик способен задержать остальные.
Основное состояние — байт 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 — именно текст прошивки; конкретный сенсор, принцип измерения и физическая распиновка из этой надписи не следуют.
Что заложить в будущую прошивку
- Явные состояния питания, зарядки, загрузки настроек, готовности датчиков, проверки сигнализации, измерения и обслуживания. У каждого состояния должны быть входные действия, критерий завершения, таймаут и реакция на ошибку.
- Готовность измерения должна иметь собственный статус. Анимация запуска и фиксированная задержка не заменяют проверку валидных данных, калибровки и условий готовности сенсора.
- Разделить тест включения исполнительного узла и подтверждение его исправности. Если аппаратная обратная связь не предусмотрена, интерфейс не должен заявлять, что электрическая проверка доказала работу звука/вибрации/света.
- Обработку измерений и тревог вызывать с определённым периодом независимо от рисования экранов и редактирования меню. Поведение во время калибровки, зарядки и восстановления настроек описать явно.
- Для кнопок определить короткое/долгое нажатие, повтор, подавление дребезга и отмену; для редакторов — таймаут, сохранение и выход без записи. Не распределять эту политику неявно по обработчикам экранов.
- Отдельно хранить заводские параметры и рабочие настройки; подтверждать восстановление и сохранение калибровки, отражать результат записи и делать его проверяемым после перезапуска.
- Разделить индекс физического канала, тип газа и модель датчика. Название газа на экране должно следовать проверенной конфигурации, а возможность выбора газа не должна создавать видимость физической смены сенсора.
Наличие этих программных состояний в старом приборе не определяет окончательные параметры и требования нового устройства. Для будущей модели полезны структура сценариев и проверяемые переходы; длительности, коэффициенты, электрические настройки и допуски потребуют своих данных.