Немного о самом осциллографе. Речь пойдет про Tektronix TDS540D. Судя по серийному номеру (его все еще можно проверить на сайте производителя), осциллограф изготовлен в конце 1998 года. Несмотря на то, что осциллографу уже больше двадцати лет, его характеристики неплохи даже по нынешним меркам: 4 канала Частота дискретизации — 2 GS/s. Полоса пропускания — 500 MHz  Недостатком являются — вес (больше 10 кг), немаленькие размеры, шум при работе. Во многом они связаны с тем, что осциллограф использует ЭЛТ в качестве экрана. У этих осциллографов есть свое сообщество энтузиастов, которые собирают информацию об опыте их ремонта, использовании и модернизации. Есть вот такая полезная страничка. Плюс много информации по осциллографом этой линейки моно найти здесь.Свой осциллограф я купил на eBay в 2018 году специально для изготовления самодельного лидара. Заказывать устройство с ЭЛТ из США с доставкой «Почтой России» — тот еще риск, но все же осциллограф доехал нормально, во многом за счет качественной упаковки. Вместе с доставкой он обошелся мне в примерно 500$, что значительно дешевле новых осциллографов с аналогичными характеристиками. Что представляет собой этот осциллограф с программной точки зрения?  Если почитать форумы, то можно выяснить, что он построен на базе процессора Motorola 68EC040, используемая ОС — RTOS VxWorks, загружаемая из Flash памяти. Единственный доступный дисковый накопитель — дисковод FDD. Главное назначение дисковода — сохранение осциллограмм и настроек осциллографа.Но также, судя по информации с форумов, с дискеты можно запускать простые скрипты, написанные на несколько экзотическом языке — Smalltalk. Замечу, что в инструкции на сам осциллограф указано, что пользователь может запускать дополнительные приложения с дискеты, но никакой информации о том, как их создавать, какое использовать API — в открытом доступе нет. Энтузиасты, в основном используют эти скрипты, чтобы сохранять «сырые» данные NVRAM на дискету — это важно, так как в качестве NVRAM используются дурацкие микросхемы Dallas со встроенной батарейкой, которую невозможно заменить. Кроме того, используя скрипты, можно программно активировать некоторые аппаратные и программные функции (к примеру, FFT и больший объем памяти), отключенные с завода.  Но есть еще более интересная информация, которая чудом дожила до наших ней — на старшую модель этих осциллографов (имеющую HDD и больший объем ОЗУ) можно было установить виртуальную машину Java, при этом в сети сохранился софт инсталлятора! А зачем вообще устанавливать Java на осциллограф? Чтобы можно было разрабатывать высокоуровневый софт для осциллографа. К сожалению, в сети не сохранилось примеров таких приложений (кроме одного), но судя по имеющейся информации, они точно были.Точно было приложение для анализа джиттера (TDSJIT1), анализа силовых линий (TDSPWR1), анализа линий связи (TDSCPM1). Пример скриншота приложения из инструкции: Про то, как писать такие приложения, опять же, никакой информации нет. Сохранилось, правда, одно единственное Java приложение — TDSPRT1, предназначенное для более удобного управления процессом печати данных прямо с осциллографа. Таким образом, у меня имелись: Бинарные коды виртуальной Java машины, не предназначенные для запуска на имеющемся осциллографе Упакованная программа на Java Несколько простых скриптов, не делающих чего‑либо интересного. Первое, что я попытался сделать — все же запустить Java на своем осциллографе.В состав инсталлятора JAVA входят файлы: Файл install.app - как раз скрипт инсталлятора, файлы с расширением APP можно запустить из меню самого прибора.  Фактически, программа создает на диске несколько папок и копирует туда файлы с дискеты, заодно переименовывая их. В конце программа показывает окошко с текстовым сообщением. А вот содержимое другого файла — rte1.bat, который вызывается уже в процессе запуска осциллографа, после того, как Java‑машина установлена:  Видно, что программа загружает четыре объектных файла в память, и далее — настраивает виртуальную машину Java. Выходит, фактически, получается всего лишь модифицировать этот скрипт, чтобы загрузка происходила с дискеты, а не HDD? Я попробовал сделать это, но никакого видимого результата не получил. Немного доработал скрипт — добавил в него вывод сообщений после загрузки каждого объектного файла — и выяснил, что загрузка проваливается на файле «extcp.o», но с чем это связано — непонятно. Тупик? На тех же форумах мне встречались упоминания того, что у этой линейки осциллографов есть специальный отладочный порт, куда осциллограф выводит логи, и куда можно отравлять команды для управления им. Для экономии порт представляет собой просто краевой разъем на печатной плате (PCB Gold Fingers), куда выведена часть параллельной шины системы. Чтобы получить к нему доступ, не нужно снимать крышку с осциллографа - он находится под специальной съемной панелью сзади (вместо простой панели там может быть панель разъемов RS232/LPT). К этому разъему можно подключить микросхему MC68 681 — приемопередатчик UART, и именно этот UART и можно использовать для связи с осциллографом. Подробнее про устройство и схему — в обсуждении на форуме. Автор обсуждения там использовал микросхему в корпусе PLCC44, я же использовал микросхему в корпусе DIP-40 — ее легче было достать, да и панелька для нее дешевле. Однако, в таком случае пришлось несколько изменить схему. Разъем для подключения к плате осциллографа сделал из кабеля 5-дюймового дисковода — его пришлось заметно порезать. Важно подтянуть вход RX микросхемы к 5В через резистор — без этого, я так понимаю, неподключенный вход ловил помехи, и это мешало прошивке осциллографа нормально стартовать. После запуска осциллограф выдает довольно много информации в отладочный порт:  В основном тут идут данные тестирования периферии и заканчиваются они переходом в консоль Smalltalk/VxWorks. Можно, например, посмотреть информацию о системе: Или посмотреть список запущенных задач: Можно также смотреть и редактировать память, делать некоторые другие вещи, связанные с ОС, ну и запускать любые команды, используемые в вышеупомянутых скриптах. Далее я попробовал опять загрузить виртуальную машину Java (теперь делать скрипты для дискеты больше не нужно, все команды можно отправлять из консоли). В итоге, как оказалось, команда ld < fd0:/app/tdsrte1/extcp.o выдала ошибку: undefined symbol: _rdrfunc ld error: error reading file (errno = 0x1c0001). Что вообще это значит? Тут стоит подумать над тем, что же вообще делает команда «ld». LD — команда VxWorks, предназначенная для загрузки программы в память. Подробнее про нее можно почитать здесь. Но этот процесс — не простое копирование, а фактически, динамическая компоновка (т. е. dynamic linking). В процессе загрузки этот компоновщик устанавливает связи между адресами в загружаемой программе и символами (функциями и переменными), уже загруженными в память ОС. Для этого используется таблица символов ОС, содержащая, в том числе, текстовые названия всех символов. Символы загружаемой программы, объявленные глобально, также добавляются таблицу символов ОС. Таким образом, появившаяся ошибка говорит о том, что в процессе загрузки компоновка провалилась — ld не смог найти в таблице символов ОС функцию «_rdrfun». Почему же это произошло? Я не сразу смог выяснить, в чем дело.  Хорошо, что текстовые названия символов присутствуют в «сыром» виде в объектных файлах (это я выяснил, из любопытства открыв из в текстовом редакторе). Проверка показала, что ни в одном другом из трех объектных файлов, функция «_rdrfun» не упоминается. В составе VxWorks есть специальная команда, которая ищет в таблице символов ОС информацию про указанные символы и выводит ее в консоль: lkup «symbol_name». В конкретно этом случае эта команда ничего не выдала, то есть «_rdrfun» действительно не является частью ОС.  Может, она не загрузилась по какой‑то причине? У меня были примеры прошивок для этой линейки осциллографов, и я заглянул текстовым редактором в них. Поиск «_rdrfun» ничего не дал. Похоже, что нужно искать где‑то в другом месте. Тут я решил внимательней присмотреться к остальным файлам. DAT-файлы внутри практически пустые, и ничего интересного из себя не представляют.А что у нас в скрипте «ossa.bat»? Оказывается, файл logo.bin — вовсе не простая картинка в каком‑то проприетарном формате, а объектный файл! И при помощи текстового редактора я обнаружил, что он‑то и содержит «_rdrfun». Ну а функция jplogosetup() в скрипте запускает отрисовку изображения логотипа. Судя по всему, осциллограф запускает скрипт «hd0:/ossa.bat» в процессе загрузки (конечно же, если у него есть HDD). Я попробовал загрузить вручную файл logo.bin в память, а потом — запустить jplogosetup(), и в результате получил такое изображение: Осциллограф, правда, после этого завис)  Этот файл показался мне довольно полезным — имеющаяся в нем функция jplogosetup() явно умела выводить графику. После того, как я загрузил в память logo.bin, загрузка файла extcp.o тоже прошла без проблем. Замечу, правда, что загрузка всех пяти файлов с дискеты занимает ну очень уж много времени — несколько минут. Дальше попытался инициализировать Java и запустить программу на выполнение, но с этим возникли проблемы:  Явно есть какие‑то ошибки в процессе запуска Java, и есть ошибки в коде запуска приложения. Последние, судя по всему, связаны с тем, что у моего осциллографа слишком мало памяти. Это можно было бы поправить, изменив настройки виртуальной машины Java — но я уже как‑то устал от ковыряний с Java.  К тому моменту я распаковал содержимое java‑программы tdsprt1.jar, и выяснил, что вся связь между логикой работы пользовательского приложения и софтом самого осциллографа в виртуальной машине Java организована через виртуальные GPIB‑сообщения — то есть программа пользователя формирует текстовые строки‑команды, Java‑машина перебрасывает их в прошивку осциллографа, прошивка их парсит и выполняет уже свои собственные функции. Звучит не очень быстро, и пользователь ограничен только теми командами, что поддерживаются парсером GPIB. Плюс сама по себе загрузка виртуальной машины в память и ее запуск занимали слишком много времени. А что делать, если хочется большей гибкости и скорости? Написать свой собственный объектный файл, и запустить функцию из него на исполнение! Что же представляют собой объектные файлы, используемые в VxWorks, которые загружает команда «ld»? Потратив некоторое время на поиски в Google, я выяснил, что тут используется довольно старый формат «A.OUT». Радует, конечно, что этот формат — открытый, VxWorks не стали создавать свой проприетарный формат. Проблема, правда, в том, что формат этот — реально старый. Современные компиляторы о нем не знают. То, что это действительно «a.out», можно проверить в Linux утилитой «file»:file safecopy.osafecopy.o: a.out SunOS mc68020 executable not stripped Для начала, я решил попробовать дизассемблировать код нескольких файлов, входящих в состав дистрибутива Java‑машины — safecopy.o (1 202 байт) и logo.bin. Для этого я использовал Open Source программу Ghidra. Она открывала файл, могла его дизассемблировать, иногда даже находила безымянные функции и циклы в них, но явно было видно, что анализ явно неполный. Объектный файл содержит в себе названия все своих и внешних символов, так где же они?  Все оказалось довольно просто — Ghidra не поддерживает «A.OUT», и воспринимает такие файлы просто как бинарный код. Однако тут мне повезло — на Github нашелся fork c доработками, добавляющими поддержку «A.OUT»: обсуждение здесь. После того, как я его скачал, и собрал Ghidra из исходников, она действительно начала «видеть» названия некоторых функций — тех, что являются экспортируемыми, т. е. передаются в ОС при загрузке файла. А вот импортируемые из ОС функции (printf, например) и переменные Ghidra не обнаруживала.  В итоге мне пришлось заглянуть в исходные коды самой Ghidra (конкретно в те, что были изменены в форке). Там я и выяснил, что добавление импортируемых символов в Ghidra просто не реализовано в этом форке. Пришлось несколько доработать код, используя в качестве примера уже имеющийся в составе Ghidra код для анализа ELF файлов. В результате мне удалось заставить Ghidra отображать и импортируемые символы, но за получившийся код несколько стыдно (  В принципе, происходящее внутри функции довольно понятно. Используя logo.bin, можно посмотреть, как реализована работа с графикой (замечу, что она сделана очень запутанно). Используя эти знания, уже можно написать простую программу, выводящую графику на экран, или работающую с консолью. Но что делать с другими функциям осциллографа, как, например, опрашивать кнопки? Чтобы выяснить, что же происходит в прошивке осциллографа, можно дизассемблировать и ее. В интернете есть даже статья о том, как это сделать на более старой модели осциллографа. В этой статье автор считал прошивку из осциллографа, используя GPIB. Я решил пойти по более простому пути и просто вычитал содержимое Flash на дискету, используя команды в консоли осциллографа. Данные я вычитывал блоками по 500 Кбайт, вся Flash память имеет размер 4 Мбайт. Сам скрипт у меня не сохранился, основан он был на базе уже имеющегося. Получив прошивку, я открыл ее в Ghidra — и она действительно смогла обнаружить большое число функций и переменных, правда, безымянных. При этом в текстовом редакторе я мог видеть очень много текстовых названий функций, что логично, так они должны каким‑то образом попасть в таблицу символов ОС. В статье выше автор выяснил адреса функций, вызывая команду VxWorks lkup — она может выдать названия и адреса всех функций, имеющихся в таблице символов ОС. Правда, выдает она их частями, требуя от пользователя нажимать Enter, да и перебирать все буквы алфавита в двух регистрах довольно муторно. Автор же в статье не раскрыл, как именно он получил всю таблицу символов — собирал ее вручную, или как‑то автоматизировал процесс. Я нашел более простой способ заполнить таблицу символов в Ghidra — нашелся вот такой набор скриптов, из которых для меня был самым полезным «vxhunter_firmware_init.py» — он действительно переименовал все функции и переменные, и правильно определил смещения адресов в памяти (как оказалось, стартовый адрес — 0×05 001 000). В итоге, результат выглядит так: Здесь уже видно огромное количество функций, причем можно видеть, какие у них аргументы, и как конкретно они используются. Замечу, правда, что Ghidra не смогла дизассемблировать весь код целиком — большое количество найденных функций ниоткуда не вызываются (точнее, Ghidra не нашла их вызовов), часть бинарного кода не повергалась дизассемблированию. Я довольно быстро смог найти функции, отвечающие за простую графику (_drawHLine, _drawVLine — могут рисовать и стирать линии, _pokeDiag — рисует наклонные линии), нашел функции отвечающие за очистку экрана (тут очень повезло, что софт осциллографа не перерисовывает экран постоянно, фактически, всегда перерисовывается только та область, где требуется изменение графики, и обычно это связано только с действиями пользователя). Графика есть, а что же с кнопками? На передней панели их целая куча. Как оказалось, с ними все сложнее. Вот со светодиодами — нет проблем, функция «_frontPanelLed», переключающая светодиоды, нашлась легко. А вот с кнопками так просто нужные функции не находились. В конце концов удалось найти функцию _getFrontPanel(), которая читает адрес 0×0558a274. Как именно прошивка использует эту функция — непонятно, так как в Ghidra цепочка вызовов функций быстро оборвалась. Тем не менее, удалось выяснить, что значение по адресу 0×0558a274 действительно меняется и есть прямая зависимость его от нажатых кнопок. При повторном нажатии кнопки значение не меняется, но его можно затереть нулем — это не нарушает работу прошивки. А вот как выводить текст на экран, я так и не смог выяснить. Казалось бы — огромная часть кода должна быть связана с выводом текста на экран, но нет, я потратил на поиски кучу времени, и провалился. Похоже, что вывод текста как‑то тесно интегрирован с ОС.Замечу, что меня не удивит, если часть кода осциллографа тоже написана на Smalltalk, а текст скриптов запакован. Как минимум, я не смог найти тексты, выдаваемые при нажатии кнопки HELP, зато нашел символ (скорее всего — массив) с названием «_Packed_text», и соответственно, функцию _unpack_text(). Для вывода небольших текстовых сообщений можно использовать специальные команды GPIB: Используя команды GPIB, очевидно, можно получить доступ к большинству функций осциллографа, благо документация о том, как управлять осциллографом по GPIB есть в открытом доступе. Для того, чтобы создать объектный файл из С кода, нужны компилятор и ассемблер. В данном случае — под довольно старый процессор 68K и плюс с поддержкой формата «a.out». Раньше такая функциональность была в GCC, но к нынешнему времени ее удалили, насколько я смог понять. Так что я потратил довольно много времени, пытаясь раздобыть себе компилятор с ассемблером. Пытался искать варианты старого софта для древних версий Windows, пытался собрать в Linux старые версии Binutils+GCC (GCC собрать не удалось, вероятно, мой системный компилятор был слишком новый). В итоге, удалось организовать следующую связку, работающую в Windows: Binutils (содержит в себе ассемблер as): Его я установил, используя найденный скрипт для MSYS2. К сожалению, скрипт рассчитан для сборки варианта компилятора, работающего с ELF файлами, так что мне пришлось его модифицировать, заменив --target=m68k-elf  на --target=m68k-unknown-aout . В итоге Binutils собрался, а GCC - нет. GCC. Его я взял тут: ссылка — вариант m68k-elf-gcc4.8.0. Так как этот gcc выдает ELF, то приходится запускать его в режиме формирования ассемблерного кода (ключ командной строки "-S"). В результате сборка выглядит следующим образом: Запускаем компилятор в командной строке Windows:  C:\sysgcc\m68k-elf\bin\m68k-elf-gcc.exe D:\{path_to_file}\tetris.c -o D:\{path_to_file}\tetris.s -SВ результате получается программа на ассемблере. Её нужно вручную отредактировать - удалить все упоминания слова ".section" (ELF их поддерживает, "a.out" - нет). Также я переименовывал все упоминания ".rodata" в ".data". Все глобальные переменные нужно инициализировать - иначе ассемблер тоже не понимает файл от компилятора После этого я копировал файл ассемблера в папку C:\msys64\opt\m68k\bin - там установлен ассемблер (правильней было бы настроить пути в MSYS2, но я поленился).  Далее - нужно в командной строке MSYS2 перейти в папку ассемблера и запустить его: cd /opt/m68k/bin/./m68k-unknown-aout-as.exe tetris.s В результате в этой же папке появляется файл с названием "a.out" - это и есть получившийся объектный файл. Проверка показала, что этот файл без проблем можно загрузить в память осциллографа командой "ld", функции, объявленные в нем, добавляются в таблицу символов ОС, и их можно спокойно запускать  на исполнение. Никаких особых требований к исходному файлу на С нет. Я решил, что запуск игры Тетрис на осциллографе сможет хорошо продемонстрировать возможности свободного программирования осциллографа, и не потребует серьезных затрат времени.  Чтобы не изобретать велосипед, в качестве образца взял этот пример. Конечно, его пришлось радикально переделать в плане вывода графики. Код получившейся программы приведен ниже — в ссылке на Github. Вся графика отрисовывается только линиями, стирание объектов — тоже за счет рисования «черных» линий. Управление движением фигур — четырьмя кнопками на цифровой клавиатуре. Важно, чтобы программа не блокировала CPU на все время — иначе управление осциллографом перестаёт работать, поэтому задержки нужно организовывать, используя системную функцию _taskDelay(). Для удобства запуска программы с дискеты я написал простейший скрипт, который можно запустить через меню осциллографа.  Результат выглядит так: А вот так программа выглядит в работе (на видео все изображение мерцает, но глазами это не видно): Существует старшая модель осциллографа, с цветным экраном (что интересно, ЭЛТ там такой же, но перед экраном установлен специальный ЖК-светофильтр, управляемый электроникой): подробнее про технологию. Плата и прошивка там такие же, только ОЗУ на плате установлено больше. Есть даже инструкция, как переделать ЧБ осциллограф в цветной. Я заметил, что если вызвать функцию _initFullColorPalettes(100,100); , то осциллограф действительно меняет палитру, и на подключенном VGA мониторе появляется цвет, там где раньше были градации серого: Интересно, что в еще более старом осциллографе HP 54600 Тетрис был частью прошивки (Easter Egg): ссылка Ссылка на Github  Программист микроконтроллеров