[Перевод] Легенда на ладони: создаём крошечный компьютер PDP11
Введение
2020 год был странным для всех в мире. Конкретно в моём случае мне пришлось находиться далеко от своего дома в Шанхае, потому что, когда COVID нанёс удар, мы были в Европе, и Китай усложнил процедуру пересечения границ примерно до конца года.
Отсутствие доступа к моей лаборатории и лишь ограниченный доступ к электронному оборудованию сильно сдерживал мои возможности, однако создание новых проектов не исключалось полностью [перевод на Хабре]. После завершения этого проекта я снова попал в проектный вакуум: всё, что у меня было с собой — это куча комплектов разработки ESP32 и желание что-нибудь создать.
Поэтому я подумал: почему бы не создать ещё один эмулятор? У меня был подобный опыт, поэтому это не будет слишком сложно, и как только я доберусь до своей лаборатории, то, вероятно, смогу превратить его в ещё одну миниатюрную работающую модель компьютера, похожую на то, что я сделал с крошечным Macintosh Plus [перевод на Хабре]. Но какой компьютер выбрать на сей раз? Меня всегда интересовал PDP11. Хоть у меня никогда его не было (и я даже на нём не работал), в своё время он находился в авангарде компьютерных технологий. Его архитектура набора команд (ISA) повлияла на довольно значительное количество архитектур, появившихся позже, а солидная доля ПО, которое мы используем по сей день, основывается на идеях, впервые реализованных на PDP11.
На случай, если вы не знаете, что такое PDP11: это линейка миникомпьютеров, создававшаяся Digital Equipment Corperation (также известной как Dec или просто Digital), начиная с 1970-х. Линейка компьютеров PDP стала попыткой DEC проникнуть на рынок компьютеров в 60-х. Хотя компьютеры уже существовали, в основном они были крупными мейнфреймами наподобие IBM, используемыми для сверхважных функций типа расчёта зарплаты, ведения бухгалтерии и других финансовых задач бизнеса. Поскольку обычно они стоили очень дорого, для них не было места в научно-исследовательских лабораториях. DEC увидела возможность получения доли рынка при помощи относительно недорогих машин, однако устройства, которые она собиралась продавать, не должны были называться «компьютерами», потому что это слово вызывало бы в сознании образы дорогих и недоступных мейнфреймов. Поэтому DEC придумала название «Programmable Data Processor» («программируемый обработчик данных»), или PDP.
Семейство PDP имело довольно долгую жизнь и содержало несколько интересных машин со множеством ISA: от первого 18-битного PDP1 до 12-битного PDP5 и 36-битного PDP10. Из всех этих ISA одной из самых успешных стала PDP11. Линейка PDP11 с 16-битной ISA и ортогональным набором команд на основе регистров использовалась во многих машинах, начиная с первой PDP11/20, созданной в 70-х, до PDP11/94, вышедшей в 90-х. Кроме того, с точки зрения функциональности, у машин было много вариантов использования: PDP11 могли работать с DOS-подобными операционными системами реального времени типа RT-11 или многозадачными ОС типа Unix. Их можно было использовать как однопользовательские машины, многопользовательские серверы и встроенные машины, управляющие механизмами и предоставляющие интерфейс пользователя. Архитектура PDP11 даже добралась до аркадных автоматов: одночиповый вариант PDP11 под названием T11 работал в таких аркадных играх Atari, как Paperboy.
В PDP11 меня привлекло то, что линейка PDP в целом всегда была семейством «машин для хакеров». Её представители были достаточно дешёвыми, чтобы люди могли делать с ними всякие забавные штуки, и например первая компьютерная игра, SpaceWar!, была написана на PDP1. Однако она не стала последней, написанной на машине PDP: кроме вышеупомянутых аркадных игр, в далёкой России советский разработчик программ Алексей Пажитнов написал на клоне PDP11 игру «Tetris», позже распространившуюся по всему свету.
Благодаря чудесам эмуляции сегодня мы можем использовать историческое ПО PDP11. Один из лучших эмуляторов PDP11 — это SIMH, который, похоже, работает на большинстве ОС стандарта POSIX. ESP32 с ESP-IDF имеет довольно приличную совместимость с POSIX, так почему бы не попробовать портировать эмулятор?
Очевидно, что «просто портируем SIMH на ESP32» — явное преуменьшение необходимых для проекта усилий. Даже несмотря на то, что SIMH — это очень крутая программа, потому что она не использует никаких уникальных API, она всё равно разрабатывалась с расчётом на полнофункциональную рабочую станцию с дешёвой и объёмной ОЗУ. Чтобы заставить работать эмулятор на ESP32 и оставить ещё памяти, которую можно будет использовать как ОЗУ эмулируемой машины, придётся для начала порезать программу.
Основными потерями станут сетевой стек и парсер конфигурации. По сути, SIMH состоит из множества модулей, каждый из которых эмулирует одно или несколько периферийных устройств. Например, каждый обобщённый тип диска имеет свой модуль. То же самое относится к сетевым устройствам, интерфейсам телетайпа, портам принтеров и т.п. Все эти модули соединяются при помощи конфигурации, задаваемой при помощи интерфейса конфигурации, работающего из командной строки. Этот интерфейс конфигурации используется при запуске для сборки нужной пользователю конфигурации PDP11, а также применяется в дальнейшем как интерфейс отладки из командной строки.
Хотя такая модель работы очень удобна и позволяет динамически создавать конфигурации PDP11 и даже менять их на лету, она составляет довольно большую часть кода, и на ESP32 с его ограниченными ресурсами реалистичнее будет жёстко прописать конфигурацию. Сетевой код имеет другую проблему: хоть он и обеспечивает огромное изобилие способов подключений, ни один из них несовместим с тем, что работает в ESP32. Поэтому от этого файла я тоже избавился.
Вырезал я и более мелкие вещи, например, всю необязательную периферию (плёночные приводы, векторные мониторы, считыватели перфокарт и т.д.), чтобы она не занимала места в ОЗУ и во флэш-памяти. Также были урезаны функции отладки: хотя у SIMH есть довольно приятные функции, применимые для отладки любого кода PDP11, в этой версии они нам не понадобятся.
В конечном итоге я изначально собрал голый минимум для того, чтобы запустить Tetris. Конкретно российская версия Tetris запускалась на «Электронике-60», клоне PDP11–23, имеющем не более 64 КБ ОЗУ и выполняющем вывод через последовательный порт на терминал 15ИЭ-00–013, который был основан на DEC VT52 с поддержкой кириллицы. В машине работала ОС ФОБОС — советский аналог операционной системы RT-11 компании DEC.
Так как конкретно советское железо и ПО в эмуляторе недоступно и не поддерживается, лучшее, что я мог сделать — использовать оригинальную западную систему, с которой всё это было скопировано: похоже, русские провели хорошую работу по сохранению перекрёстной совместимости. Поэтому ESP32 был сконфигурирован для эмуляции PDP11–23 с 256 КБ ОЗУ и приводом гибких дисков RX01, что давало мне 256 КБ дискового пространства под операционную систему и файлы игры. При помощи SIMH я на своём ноутбуке создал пустой диск и установил на него RT11. Затем я взял риск с русскими играми, на котором был Tetris, и скопировал двоичный файл. Этот образ диска будет прошит вместе с эмулятором на ESP32. Пока я решил не заморачиваться с терминалом, выбрав вывод консоли PDP11 на отладочный последовательный порт ESP32.
Это позволило мне запустить RT11 и получить доступ к командной строке. Однако запуск Tetris не принёс мне ничего полезного: игра ожидает советский терминал 15ИЭ, а мой современный эмулятор терминала даже не пытается его эмулировать.
К счастью, этот терминал эмулировали в рамках проекта Mame/MESS. Найдя нужные ROM, я смог запустить эмуляцию этой древности, и благодаря возне с конфигурацией и socat мне наконец удалось увидеть оригинальный Tetris, впервые запущенный на ESP32:
Очевидно, что такое решение не особо интересно; нам бы хотелось увидеть Tetris на дисплее, подключенном к ESP32. Используемый мной ESP-Wrover-Kit имеет цветной дисплей 320×240, может, стоит воспользоваться им? 15ИЭ, как и VT-50, с которого он был скопирован, может отображать 24 столбцов на 80 строк текста, то есть на отображение символа останется всего 4×10 пикселей… Судя по ROM оригинального 15ИЭ, символы на нём занимали 8×8 пикселей, то есть нам понадобится вжать два горизонтальных пикселя в один… К счастью, благодаря некоторым хитростям (в частности, LCD-пиксель, содержащий два горящих «ужатых» пикселя, я заставил быть ярче, чем LCD-пиксель только с одним «ужатым» пикселем) эффект оказался на самом деле довольно хорошо читаемым.
Терминал 15ИЭ с определёнными оговорками содержал в себе целый микропроцессор, построенный на дискретной логике. Хоть я и мог выполнить эмуляцию, чтобы приблизиться к «реальности», я решил пойти простым путём и только смоделировать на C, как он интерпретирует управляющие символы последовательного потока данных. В конечном итоге получился далёкий от идеального симулятор 15ИЭ, но он достаточно хорош, чтобы отобразить игровое поле Tetris:
Очевидно, что простой демонстрации игрового поля недостаточно, но у платы ESP-Wrover-Kit недостаточно кнопок для управления падающими тетрамино. К счастью, у ESP32 есть Bluetooth и некто под ником Rossum написал эмулятор нескольких 8-битных консолей, способный использовать для ввода Bluetooth-геймпады и клавиатуры. Так как этот код выпущен под открытой лицензией, я смог взять его, избавиться от всего Arduino-подобного (к сожалению при этом потерялась поддержка Wiimote) и вставить его в мой код.
Теперь мне достаточно было найти Bluetooth-геймпад (в качестве которого я использовал один из джой-конов моего Switch) и играть в оригинальную версию Tetris на моей плате разработки ESP32:
Итак, теперь у меня есть первая версия Tetris, запущенная на симулируемом PDP-11. Однако это кажется пустой тратой ресурсов: для Tetris достаточно всего нескольких десятков килобайт ОЗУ, а у моего ESP32 есть и внутренняя ОЗУ, и 4 МиБ PSRAM. Почему бы немного не повысить ставки и не попробовать запустить ещё одну новаторскую программу, которая, вероятно, чуть больше подходит под возможности платы ESP32?
Ещё одна программа, впервые запущенная на PDP11, пришла из мира Unix. Хотя сам Unix не был рождён на PDP11 (строго говоря, он был рождён на позаимствованной PDP7, хоть и вскоре его портировали на PDP11), на этом компьютере появился дистрибутив Unix под названием BSD. Возможно, вы узнаете его как BSD Unix, который до сих пор существует в виде FreeBSD, NetBSD и OpenBSD. Кроме того, он стал фундаментом для более потребительских программ, например, Apple iOS и операционной системы линейки игровых консолей Playstation.
Ещё один аспект привлекательности BSD заключается в том, что её самая последняя версия (2.11BSD) содержит полный сетевой стек TCP/IP. Этот стек довольно известен: он частично использовался и в первых версиях Microsoft Windows. С тех пор IPv4 и TCP сильно не изменились, поэтому этот стек по-прежнему должен оставаться рабочим: у меня должно получиться взаимодействовать его с WiFi-интерфейсом ESP32, превратив его в истинно сетевой пример BSD, запущенной на PDP11.
Заставить BSD работать оказалось не так сложно. Сконфигурировать достаточно быструю машину, дать ей жёсткий диск на 1,5 ГиБ (физически расположенный на карте micro-SD, вставленной в разъём ESP-Wrover-Kit) и достаточно памяти — вот и всё, что нужно для запуска BSD. Я выполнил установку ОС на жёсткий диск моего ноутбука, поскольку плёночный привод, необходимый для загрузки установочного носителя для BSD, был вырезан из версии SIMH для ESP32. Затем я переместил образ HD на карту micro-SD. Я смог снова использовать код терминала 15ИЭ, потому что он без проблем отображал и английские символы ASCII, и в результате смог увидеть загрузку BSD на своей плате разработки.
Однако я получил только несетевую версию BSD, потому что ранее вырезал из SIMH весь код сетевого интерфейса. (Даже если бы я его оставил его, ESP-IDF не поддерживает ни один предлагаемый способ связи, поэтому он всё равно был бы бесполезен.) К счастью, у ESP-IDF есть удобный и гибкий слой абстракции под названием ESP-NETIF, связывающий вместе сетевые интерфейсы и высокоуровневые вещи типа стеков TCP/IP. Я могу легко реализовать оболочку, получающую Ethernet-пакеты, приходящие от подсистемы WiFi, и перенаправляющую их в SIMH, и наоборот. SIMH уже содержит код для эмуляции Ethernet-карты, поддерживаемой 2.11BSD, поэтому всё должно замечательно работать.
Однако даже если PDP11 теперь способен передавать Ethernet-пакеты непосредственно драйверам WiFi для дальнейшей передачи, у меня всё равно нет хорошего решения для сетевого подключения. Во-первых, PDP11 не понимает таких связанных с WiFi аспектов, как SSID и тому подобное. Во-вторых, DHCP отсутствует в 2.11BSD, как и в его предшественнике BOOTP. Я даже задумался о портировании или реализации DHCP-клиента, однако в спецификациях DHCP есть одна странность — требование прямого доступа к сетевым пакетом с обходом слоя TCP/IP. Насколько я знаю, ядро 2.11BSD не имеет поддержки ничего подобного. Хотя я и готов был к серьёзной работе, реализация нового интерфейса ядра на устаревшем десятки лет назад ядре — это уже перебор.
Поэтому вместо этого я решил использовать непредусмотренным образом слой ESP-NETIF, чтобы система получила «раздвоение личности». У ESP32 будет иметь полный стек TCP/IP с драйверами WiFi и DHCP-клиентом. У PDP11 также будет собственный полнофункциональный стек TCP/IP BSD. Оба стека будут иметь один IP-адрес, получаемый программным обеспечением ESP32 и передаваемый на PDP11. Однако небольшой кусок кода будет решать, какому стеку IP отправлять пакеты, полученные любым из интерфейсов. Это данные DHCP от интерфейса WiFi? Передай их на LwIP. Данные для telnet-сервера на интерфейсе WiFi? Это для PDP11. Код также может самостоятельно интерпретировать некоторые пакеты, приходящие от PDP11. Отправляя эти пакеты, PDP11 может выполнять поиск сетей WiFi, настраивать идентификационные данные WiFi и т.д. Так как PDP11 может передавать эти пакеты как стандартные широковещательные UDP-пакеты, нет необходимости в сложных интерфейсах конфигурирования на уровне ядра.
Эта конфигурация реализуется небольшой программой, запущенной на PDP11: её выполнение позволяет выполнять поиск доступных сетей и подключаться к одной из них. Далее программа автоматически получает данные сети от DHCP-клиента ESP32 LwIP и соответствующим образом конфигурирует сетевой интерфейс BSD. Программа называется «wifid», как и говорилось выше, она использует широковещательные UDP-пакеты, подхватываемые написанной мной оболочкой ESP-NETIF. Код за пределами эмулятора подхватывает их и выполняет всю необходимую работу, при необходимости передавая обратно ответ широковещательными пакетами UDP.
Я не особо хотел писать подобный код на древнем компиляторе C, поставлявшемся с 2.11BSD, но, к счастью, это и необязательно: к моему большому удивлению, даже самый новый компилятор GCC замечательно поддерживает архитектуру PDP11. Скомпоновать его с двоичными файлами BSD было чуть сложнее, потребовался «хак» в виде добавления отсутствующей функции в библиотеки времени выполнения BSD. Кроме того, файлы заголовков, поставляемые 2.11BSD, используют формат, который сегодня справедливо признаётся неверным; поэтому вместо него я накидал ужасную, но работающую смесь заголовков RetroBSD, а также исправленных заголовков 2.11BSD. Конечный результат оказался некрасивым, но вполне рабочим: я могу скомпилировать двоичный файл на ноутбуке, при помощи FTP отправить его или в запущенный на ноутбуке SIMH, или в ESP32, а затем успешно запустить его там. Я считаю это свидетельством того, насколько любим был компьютер PDP11; несмотря на то, что это 50-летняя архитектура, коммерческая жизнь которой завершилась 30 лет назад, она по-прежнему имеет достаточно фанатов, которые отправляют патчи, официально включаемые в современный тулчейн GCC.
После завершения работы мне осталось только изменить один-два скрипта запуска сети для автоматического подключения PDP11 после перезапуска к последней использовавшейся сети. Теперь 2.11BSD имеет все возможности для подключения к любой сети WiFi.
Тем временем мне наконец удалось получить документы, чтобы вернуться в Китай. После 14-дневного карантина мы вернулись домой и я получил доступ ко всему своему оборудованию. Настала пора избавиться от платы разработки и вставить эту сборку в красивый корпус!
Но для начала нужно понять, какой форм-фактор использовать. Сам PDP11 продавался во множестве форм-факторов, от огромных шкафов с гигантскими плёночными бобинами до машин µPDP, больше всего похожих на PC. Отличаются от PC они тем, что не имеют стандартного интерфейса клавиатуры и монитора: для интерактивной работы необходимо подключать к машине некий внешний последовательный терминал.
Поэтому я решил, что устройство, в основном существующее из-за своего внешнего вида и возможности интерактивности, стоит поместить в корпус, который пользователи видели чаще всего: в соответствующий возрасту машины последовательный терминал. Я выбрал DEC VT-100 (на самом деле, более популярный DEC VT-102), потому что он имеет очень красивый «промышленный» вид и легко узнаваем. Кроме того, это будет не первый случай установки всего PDP11 в этот терминал: линейка VT-100 проектировалась с учётом возможности расширения, и, например, VT-103 мог вмещать в себя небольшую систему PDP11. В случае разрабатываемого мной миниатюрного PDP11 терминал VT-100 обеспечит мне достаточно места для размещения логической платы и чего-нибудь ещё.
Перейдём к проектированию корпуса VT-102. Я взял несколько изображений из Интернета и использовал их для моделирования корпуса в OpenSCAD. Корпус не имеет никаких сложных изгибов, и это хорошо, потому что мне достаточно было для моделирования только фотографий сбоку и спереди. Так я получил модель VT-102 в более-менее реальных размерах, но для миниатюрной версии её, разумеется, нужно было уменьшить. Но насколько уменьшить? Ну, в основном это зависит от выбора дисплея, потому что в корпус должна помещаться передняя панель дисплея; все остальные детали находятся внутри корпуса, поэтому я могу перемещать их как угодно.
Я нашёл на Taobao очень милый 1,8-дюймовый LCD с разрешением 320×240, у которого оказался тот же контроллер, что и у платы разработки. (Также я нашёл 1,3-дюймовый дисплей с разрешением 240×240. Теоретически, благодаря субпиксельному сэмплированию я могу добиться половинного отображения текста, но в целом весь дисплей был слишком маленьким… Металлическая штука сверху — это разъём Micro-USB.) Поскольку основным ограничением был размер дисплея, теперь я мог уменьшить VT-102 под него. (Так как оригинальный VT-102 имел 12-дюймовый дисплей, моя модель становится копией примерно в масштабе 1/6,6. И это здорово, ведь таким же получился масштаб моей модели MacPlus.)
Так как теперь я знаю размер корпуса, то могу спроектировать электронную схему и спроектировать для неё печатную плату. Плата может располагаться сбоку корпуса, что позволит вывести разъёмы сзади от корпуса. Кроме того, это значит, что гибкую печатную плату LCD можно загнуть под углом 90 градусов и подключить к плате разъёмом. Компоненты печатной платы вполне обычные: «мозги» в виде модуля ESP32-Wrover, чип CH340 USB-to-serial для программирования флэш-памяти модуля без внешнего подключения программатора, литий-ионная зарядка и LDO 3,3 В. Также там есть разъём для карты micro-SD, которую можно использовать в качестве хранилища для жёсткого диска, а также гибкая печатная плата LCD.
Спустя какое-то время прибыли заказанные компоненты и изготовленная печатная плата. С пайкой разъёма гибкой печатной платы пришлось немного повозиться, но все другие компоненты установились довольно быстро.
Для фиксации LCD и печатной платы я спроектировал разъёмы, в которые должны вставляться углы платы и края LCD. Также я разрезал корпус на верхнюю и нижнюю половины, чтобы печатать их отдельно. У нижней и верхней половин есть соединительные кромки, обеспечивающие соединение без странных трещин. Я добавил два штифта для надёжного крепления половин. Мой принтер достаточно точен для печати приемлемой резьбы M3, поэтому я добавил её. Благодаря этому сборка выполняется очень легко: вставляем LCD, вставляем печатную плату, закрываем корпус, готово. Только если бы не выключатель питания и аккумулятор. Выключатель питания находится так далеко от всего остального (потому что там он выглядит лучше всего…), поэтому я закрепил его эпоксидкой. Что же касается аккумулятора, то я не знал, куда его вставить, поэтому подумал, что просто приклею его на двустороннюю ленту.
На печать корпуса потребовалось довольно много времени, в основном из-за того, что я не понимал, что фотополимер не сможет застывать в нужной форме, когда он слишком холодный. Кроме того, я выбрал цвет, который раньше не использовал, и не хотел, чтобы опоры портили красивую поверхность, поэтому получилось слишком много бракованных напечатанных моделей. Ниже показаны окончательные, сделанные в тёплом помещении и отпечатанные с достаточным временем выдержки.
Базовый цвет печати скорее всего немного не совпал, потому что на большинстве фотографий PDP11 он более желтоватый, но этот фотополимер был ближе всего и сложно найти краску, лучше передающие цвет и текстуру. Однако от фирменной чёрной рамки вокруг ЭЛТ-дисплея я отказаться не мог. Вопрос решился малярным скотчем, аккуратной подрезкой и баллоном чёрной краски.
Итак, всё собрано и закрыто. На самом деле, спереди есть небольшой логотип «Digital VT102», для создания которого потребовалось немного чёрной краски, но надпись в результате получилась почти читаемой. Клавиатура перед устройством — это крошечная Bluetooth-клавиатура размером с кредитную карту, она немного велика относительно масштаба VT-100, но работает замечательно.
И на этом проект завершён.
В конечном итоге у меня получилась довольно красивая, созданная на 3D-принтере модель, на которой работает надёжный эмулятор PDP11 с ОС, к которому даже можно получать доступ через Интернет. Если вы хотите создать подобное устройство, то исходный код, проект печатной платы и файлы модели корпуса находятся на Github.