CNN TürkÖzgür Özel'in Cemil Tugay öfkesi dinmiyor: "Burnundan fitil fitil getireceğim"Bollywood HungamaSCOOP: Tukaram Mundhe's FDA crackdown reportedly shuts food counters at INOX R City Mall for 5 daysPunchExpert raises fuel tanker safety concerns following explosion at Edo marketZDF heuteAktuelle Pressemitteilungen des ZDFالنهارالحرس الثوري يعلن اشتعال ناقلة نفط في هرمز بعد اصطدامها بلغمMalay MailHome minister: Immigration depots facing capacity pressure amid daily enforcement operationsGhaflaKRA Issues Agency Notice To Recover KSh 623 Million Tax Debt From Mike SonkoTimes of India EntertainmentKamal Haasan mourns music composer Deva, recalls their film collaborationsCBS NewsIran's president denies nuclear ambitions, says he's ready for talks매일경제‘만리장성 코카콜라 소년’으로 유명해진 8살 아이… 47년 뒤 놀라운 근황Daily MailRio Ferdinand embroiled in fat-shaming row after telling Man United influencer to 'pick up your t**s' following Instagram digMeduzaВ Конгресс США внесли законопроект, блокирующий «дизельную сделку» Трампа и Путина
The Daily Newsstand · Free, Always
Saturday, October 17, 2026

Назад в машинный зал: свой PDP-11/70 — вопреки SIMH, нетерпению и здравому смыслу

Translate

Продолжение статьи «Назад в машинный зал: как собрать эмулятор PDP-11/70 с телетайпом, блэкджеком и без Rust» (первая часть).

Для тех, кто пропустил первую часть: посмотреть живой эмулятор PDP-11 в браузере можно здесь — amesk.github.io/yaPDP/pdp11.html.

Первая часть была про магию: как оно вообще заработало. Эта — про обычную инженерную прозу: нетерпение, компромиссы и их последствия.

Работа с BASIC-11 на Teletype Model 33 ASR

Выглядит оно примерно вот так

Вживую оно ещё лучше (под спойлером — видео)

Если интересно — добро пожаловать под cut.

Статья длинная, поэтому приведу оглавление:

Почему я не взял SIMH

«Зачем тебе свой эмулятор, если есть SIMH?» — спрашивали после первой статьи. Вопрос по делу.

SIMH Боба Супника — де-факто стандарт ретро-эмуляции: точность до такта, десятки машин под одним флагом, тысячи часов коллективной отладки. Если хочется копаться во внутренностях PDP-11, гонять XXDP-диагностику или писать свою ОС — SIMH вне конкуренции, тут спорить глупо. Лучшее, что тут может сделать одиночка, — перестать играть в ковбоя и присоединиться к сообществу.

После запуска SIMH мы получаем консоль, в точности эмулирующую работу того самого оборудования: можно грузить ОС и работать. Но погружения, которого я искал, эта консоль не даёт.

Инструменты есть, но для меня магия не сработала — долго, трудно, и всё равно не то

Утверждать, что в SIMH мы ограничены голым терминалом, — неправда.

Для SIMH сообщество сделало немало инструментов, которые сильно меняют ощущения от работы. Я попробовал вот эти:

BlinkenBone (проект Йорга Хоппе): это, пожалуй, самый известный и развитый проект. Он позволяет запускать реалистичные Java-панели для многих классических машин, включая PDP-11/40, PDP-11/70 и PDP-10.

Работа с PDP-11/70 в BlinkenBone

Работа с PDP-11/70 в BlinkenBone

Штука прекрасная, но решает только часть задачи с пультом, а не всей периферией.

MAME: мощная программа, название которой расшифровывается как Multiple Arcade Machine Emulator (эмулятор множества аркадных машин). Главная цель проекта — сохранить историю видеоигр, точно воссоздавая «железо» аркадных автоматов и домашних консолей на современных компьютерах.

Всё богатство MAME в плане выбора устройств

Всё богатство MAME в плане выбора устройств

Консоли и аркады меня не заинтересовали, но там есть визуально очень точная эмуляция VT52, VT240 и, наверное, десятка других терминалов.

Я воодушевился, полез ставить и настраивать, но упёрся лбом в проблемы.
Если коротко — стабильной работы интересных мне терминалов MAME совместно с SIMH добиться не удалось. ROM для них пришлось искать по всему интернету, и нашёлся только для похожего терминала — более современного, зато входящего в MAME. К установленной у меня версии он всё равно не подошёл, пришлось брать ещё менее привлекательный терминал, с ним завелось (кстати, очень красивое), а вся связка с SIMH стартовала с очень большим скрипом и требовала многошаговой подготовки, которая к моменту начала работы уже убивала желание идти дальше.

Самое печальное — не было готовых эмуляторов того, что нужно — хотелось нырнуть ещё лет на 20 глубже, в эпоху телетайпов и перфолент, когда рождались Unix и те паттерны, которые мы используем почти каждый день, часто даже не осознавая этого.

Моя цель — вернуть атмосферу того самого машинного зала и машин, которые там работали.

Посмотрите на живой пример работы yaPDP:

Самое цепляющее применение дисплея VT11, которое я смог получить

Самое цепляющее применение дисплея VT11, которое я смог получить

На видео под спойлером — загрузка системы, работа терминала и запуск классической игры Lunar Lander. Слышно телетайп, видно задержку возврата каретки, а на векторном дисплее VT11 идёт та самая посадка.

Вживую оно ещё больше цепляет

Скажу честно, посадить его мне не удалось ни разу, тут сказывается и отсутствие привычки работать световым пером (надо мышкой на элементы управления наводиться и ждать, нажимать ничего не надо, программа сама поймёт, какой манёвр пользователь хочет делать) и отсутствие понимания правильной организации «Suicidal burn» (это когда мы, максимально экономя топливо, включаем максимальную тягу в самый последний момент с расчётом, что скорость аппарата погасится почти в точке касания поверхности).

Но цепляет очень сильно, почти так же, как посадка в детстве лунного модуля на «Электронике МК-61».

Кстати, забавный факт — в игре почти через 60 лет нашли ошибку.

В итоге получилось то, что получилось.

Хотелось, чтобы задержки были видны, звуки слышны, а перфоратор, световое перо и принтер вели себя как настоящие.

В этом смысле мне есть с чем сравнивать — пусть и на исходе эпохи, но я застал эти машины работающими, когда в 14 лет бегал к родителям на работу, так что воспоминания есть свои, не из YouTube.

Добиться запуска «в один клик», без многочасовой медитации с настроечными файлами, для меня было принципиально важно. Пусть оборудования меньше, зато оно заработает сразу.

Так и появился yaPDP — со всеми его возможностями и ограничениями.

Что меня подтолкнуло к созданию собственного эмулятора

Идея собралась из трёх совершенно разных вещей. Отправной точкой стала неудача: пока я пытался выжать нужную атмосферу из SIMH и его окружения, стало окончательно ясно, что готового решения, которое устроило бы меня целиком, просто нет.

Лет десять у меня в закладках лежал google60 — проект Masswerk, где работа в машинном зале ощущается прямо в браузере. Там я впервые увидел, что так вообще можно, а позже его движок стал основой моей консоли.

Проект Алексея Морозова по сборке «Микро-80» впечатлил другим: отладкой прошивки и адаптацией CP/M под его компьютер прямо в браузере, на JavaScript. И тем, что после такой виртуальной подготовки программа почти без изменений заработала на реальном железе.

Последняя точка — отпуск в Москве и поход в Яндекс.Музей. Разговор со смотрителем о виртуальных экспонатах рядом с настоящим работающим железом окончательно убедил: все инструменты уже под рукой, пора делать.

Инженерные компромиссы

На протяжении всей моей карьеры я не помню ни одного проекта, который не был бы набором компромиссов, выбор которых определялся бы исходом войны хотелок и технических/организационных возможностей, а в худшем случае — особенностями внутренних нейронных сетей Заказчика и Исполнителя (чтобы не писать неприличное).

Надеюсь, что в таком небольшом проекте мне удастся остаться в рамках «хорошего случая».

Главный компромисс был всё тот же: между зудом сделать всё «здесь и сейчас» и трезвой оценкой, что на такой проект может не хватить ни сил, ни квалификации.

Осталось дело за малым — найти рабочий эмулятор процессора и периферии. После недолгих поисков я вышел на проект Пола Нанкервиса pdp11-js, который дал нужную виртуальную машину и пульт управления (пришлось сильно доработать).

Роковая ошибка?

Нельзя сказать, что я не видел [PCjs](https://github.com/jeffpar/pcjs) в части PDP-11, но реализация на тот момент показалась мне сильно более сложной… а у меня зудело.

В итоге принятое решение было частично ошибкой, частично удачей — я получил работающий аутентичный пульт, набор подготовленных образов дисков и лент, с которых можно было легко стартовать, понятный с первого взгляда код эмулятора (это на старте очень важно).

После этого костяк эмулятора прямо нарисовался перед глазами:

  • одностраничное веб-приложение, легко портируемое на десктоп (привет Яндекс.Музею!)

  • чистый JavaScript-эмулятор PDP-11/70 (заимствуем у Пола, PDP-11/45 выкидываем)

  • машинный пульт (заимствуем у Пола)

  • консоль (перелицовываем принтер Masswerk и добавляем ввод от перфоратора)

  • универсальный слой доступа к образам дисков (заимствуем у Пола)

  • страница управления устройствами ввода-вывода

  • страница конфигурации

  • использование Tauri как средства лёгкого создания standalone-приложения (до этого был опыт работы с Electron, технология была знакомая)

Первая сборка, имея такой богатый арсенал, была разработана буквально за две недели по вечерам — она успешно грузила имеющиеся операционные системы, позволила поиграть в Adventure, поработать в Unix V5 и 2.11BSD.

Но нужно понимать — за быстрый старт, serverless-архитектуру и богатую экосистему для веба приходится дорого платить, некоторых возможностей в таком приложении не будет никогда.

Ограничения платформы

  • Отдельного окна не будет. Оторвать терминал или принтер в своё окно и поставить их бок о бок с основным нельзя: весь backend эмулятора живёт в той же вкладке, что и интерфейс. В десктопной версии такое окно сделать можно, для этого и появится серверная часть на Rust, но тогда веб- и десктоп-версии начнут расходиться, а расхождение версий дороже выигранного окна.

  • Идею с множеством взаимодействующих машин тоже придётся «положить под сукно». Сетевой мост на Rust пытаться делать можно. Но Rust возьмёт на себя только разбор сетевых пакетов: поднимать интерфейсы TUN/TAP придётся руками, причём для каждого поддерживаемого окружения. Это пока без меня)

  • Я выбрал работу на чистом JavaScript + CSS, при разрастании кодовой базы сверх определённого предела это станет проблемой (React, Vue и остальное придумали умные люди не зря). Но это цена быстрого старта и простоты в начале разработки.

Дальше уже не «нельзя», а «может быть плохо». Первое: тайминги могут поплыть, если увлечься аудио- и видеоэффектами, вплоть до полной поломки. Второе я уже поймал руками — приложение живёт либо в браузере, либо в системном WebView, который использует Tauri, и на разных платформах интерфейс ведёт себя по-разному: разрабатывал я на Windows 11 + Chrome, а проверял на Windows 10 + Edge в VirtualBox. На Ubuntu 24.04 с Firefox проблем при этом не было.

Последнее ограничение вообще не про платформу, а про код, который я взял за основу. Эмулятор Пола Нанкервиса написан понятно и просто, за это я его и взял. Но у простоты есть цена: расширять его тяжелее, чем хотелось бы, архитектура этому не помогает (об этом ниже).

Сильные стороны

  • Установки нет вообще: открыл ссылку в браузере — эмулятор уже работает. Ни настроечных файлов, ни предварительной подготовки образов. Для школы, доклада или самостоятельного изучения — этого достаточно.

  • Если хочется поставить локально — простейший инсталлятор под нужную платформу, который не оставляет после себя лишнего в системе. То, что нужно, например, для виртуального экспоната в музее.

  • Внутри есть своеобразный «Быстрый старт» — тот, кто запустил эмулятор впервые, не остаётся один на один с чёрным экраном: начать работу можно в два клика мышкой.

  • Десктопная версия собирается из той же кодовой базы и с теми же образами. Отдельного продукта «для standalone» не существует, пользовательский опыт общий, код общий (пока что) — никаких сюрпризов.

Дальше — плюсы, которые вижу уже я как разработчик:

  • Готовая база под подходящими лицензиями, которая делает то, что мне нужно: систему команд, прерывания и диспетчер памяти с нуля писать не пришлось.

  • Выразительных средств для UI и звука хватает с запасом, и примеров в сети много (со своим Qt и QML на основной работе я тихо завидую).

  • Инсталлятор условно бесплатный, причём сразу нескольких видов. Обновления, судя по всему, тоже есть, но с ними я пока не разбирался — в Electron они были, и я даже пилотно их использовал.

  • Кросс-платформенность для десктопа достаётся почти бесплатно: Tauri делает 99% работы за нас. От меня требуется только тщательно проверить, что артефакты на заявленных платформах запускаются и ведут себя так же, как веб-версия. Для этого у меня есть VirtualBox.

  • Единый стек для основного кода, инструментальных утилит и сборки: всё на JavaScript, отдельного тулчейна для инструментов не понадобилось.

  • Возможности отладки эмулятора практически не ограничены (мне бы такие на основной работе). Об этом напишу ниже.

Будни разработчика

yaPDP — проект личный, работаю я над ним «в одну каску». Поэтому получить что-то работающее и не утонуть в бесконечной отладке и рассогласовании разных частей можно единственным способом: максимально всё автоматизировать.

Поэтому я использую целую пачку npm-скриптов из package.json: они закрывают всё, от сборки и валидации до служебной зачистки рабочего пространства. Для оркестрации задач я нашёл Wireit от Google — для моих целей он заменил привычные мне GNU make и CMake.

Если «это работает на твоей машине» — это совсем не значит, что процесс будет повторяемым (завтра может перестать работать и на твоей, плавали, знаем). Поэтому любой артефакт у меня собирается только в CI/CD-конвейере: так версии не перемешиваются и файлы не теряются. Очень выручает то, что я перенёс проект на GitHub — изначально я хостил его на GitVerse.ru, но именно на этапе налаживания CI/CD начались сбои, разбираться с которыми не было сил. На GitHub достаточно большие квоты на артефакты для open-source-проектов, да и раннеры есть для всех интересных мне платформ. Забот с кастомными раннерами мне и по основной работе хватает, тащить их поддержку сюда не хочу.

В итоге забесплатно получается:

  • Issue tracker

  • Хранилище релизов

  • Лендинг на GitHub Pages (вот здесь как раз стреляет serverless-природа эмулятора, иначе пришлось бы заморачиваться с хостингом)

  • GitHub workflows, которые позволяют мне автоматизировать проектные задачи. Сейчас задействованы вот такие:

    • ci.yml — проверка каждого PR

    • pages.yml — публикация лендинга и самого эмулятора на GitHub Pages (оказалось очень удобно)

    • release.yml — в одно движение делает draft релиза и собирает нужный набор артефактов

    • clean-history.yml — вместе с retention policy от GitHub держит историю сборок в обозримом состоянии (иначе эту бесконечную простыню от автоматических workflow смотреть невозможно).

Ещё один workflow — про видео

Если честно, есть ещё один workflow — [videos.yml](https://github.com/amesk/yaPDP/blob/master/.github/workflows/videos.yml). Он автоматизирует производство видеороликов о работе yaPDP, а тема эта настолько большая, что тянет на отдельную статью.

Откровенно поразило то, что для моего игрушечного проекта мне же потребовались формальные документы — я реально написал RELEASING.md раньше, чем отладил сам workflow, и им же пользовался, чтобы этот workflow собрать. Как-то незаметно хобби превращается во вторую работу))

Отладка

При таком стеке возможности для отладки и инструментирования практически неограниченные.

Самое главное — весь интерпретатор написан на JavaScript, поэтому его можно править прямо в процессе отладки, как того требует задача. Начиная от банального «напечатать в консоль» какое-то значение (например, при записи и чтении определённого набора регистров) и заканчивая хуком на вызов своего кода: он в нужный момент получает управление, что-то протоколирует и фиксирует, ПРАВИТ память и регистры эмулятора и возвращает управление обратно.

Вторая киллер-фича — возможность пользоваться инструментами для JavaScript. Например, Puppeteer — библиотека Node.js от команды Google, которая управляет браузерами Chrome и Chromium по протоколу DevTools. Работает она по умолчанию в headless-режиме, без графического интерфейса.

И вот тут дела принимают совсем другой оборот — я могу программно запустить браузер с развёрнутым внутри эмулятором (при необходимости модифицированным), посылать из сценария JavaScript команды управления, смотреть на DOM-модель страницы и консоль. О таких возможностях только мечтать можно.

В качестве иллюстрации — таким способом был починен случайно сломанный Lunar Lander. Поставил печать в консоль на выполнении команды отрисовки примитива (VT11 — векторный дисплей), дождался скриптом достаточно большого количества записей (это означает, что стартовая заставка ушла и пошёл отрисовываться процесс игры), остановил эмуляцию (просто программно выдав HALT на процессор) и сдампил содержимое памяти.

И этот же движок работает не только на отладку: им я снимаю скриншоты областей экрана для документации и записываю демонстрационные ролики. Подробнее об этом — в следующем подразделе.

Документирование

Поскольку в проекте я делаю упор на визуал, в пользовательской документации на лендинге всегда должны быть актуальные картинки. Обновлять их руками — не вариант, поэтому скриншоты загруженных ОС, интерфейса и иконок делаются прямо в Puppeteer и сразу вставляются в документацию.

Всё автоматизировано (набор скриншотов и поддерживаемых ОС мутирует медленно, а вот пользовательский интерфейс эмулятора меняется непрерывно):

npm run screenshots:os
npm run screenshots:manual

Запускаются сценарии tools/screenshots-os.js и tools/screenshots-manual.js, которые выполняют всю черновую работу. Можно сделать гранулярно, задав параметрами только то, что требуется, а можно и пакетно. В TODO остаётся нечёткое сравнение новых скриншотов с уже имеющимися, чтобы не плодить в git замены двоичных артефактов там, где картинка не изменилась. Пока этого нет, но текущему процессу отсутствие оптимизации не мешает.

Тем же Puppeteer снимается и видео: tools/record-video.js записывает прогон эмулятора в браузере, а дальше ролик собирается отдельным конвейером — тем самым videos.yml из спойлера выше. То есть документация и ролики делаются одной обвязкой.

Модификация эмулятора и архитектурные долги

Эмулятор Пола Нанкервиса я взял именно за его понятность и простоту. Быстренько набросал прототип, который заводится и делает что-то, напоминающее то, что мне нужно.

И попутно его испоганил, внеся в src/iopage.js знание о DOM-модели UI, какие-то хуки по месту и прочие предосудительные вещи. Почему так вышло? Отчасти из-за спешки, отчасти из-за отсутствия в эмуляторе понятия шины — там всё «пришито» по месту. Этим, кстати, он и отличается от PCjs, где этот вопрос решён.

И теперь уже моя очередь заниматься рефакторингом и исправлением ситуации. Без этого дальше двигаться никак нельзя.

Но опять меня спасает Puppeteer — ценой скорости выполнения я могу создать любой нужный мне e2e-тест, который будет смотреть на продукт «в целом». После получения достаточного количества таких e2e-тестов я могу рискнуть начать выполнять рефакторинг, не опасаясь слишком сильно, что на этом проект и закончится. У меня всегда есть работающий вариант, от которого можно «танцевать».

Когда модифицированный вариант заработает (работает бОльшая часть, если честно) — можно параллельно создавать второй набор e2e-тестов, который уже будет проверять исключительно headless-эмулятор на корректность.

И здесь у меня есть ещё один бонус от эпохи — это XXDP, специализированная диагностическая ОС от DEC. Запустив эмулятор и смонтировав диски, я настраиваю доступ к консоли для ввода-вывода (привет, SIMH!) и прогоняю родные диагностики DEC: сначала вручную, потом автоматизированно.

Первый камень преткновения

Первая диагностика прошла прекрасно, а вот на второй (`EKBBF0`) я получил сообщение:

LOOK AT CONSOLE LIGHTS: DATA LIGHTS SHOULD READ 166667, ADDRESS LIGHTS SHOULD READ 032236, CHANGE SWITCH 7 TO CONTINUE

В переводе: «Посмотрите на лампы пульта: на лампах данных должно быть 166667, на лампах адреса — 032236, для продолжения переключите тумблер 7».

Учитывая, сколько уже сделано, такое требование кодируется почти не задумываясь (для PDP-11 это всего лишь чтение и запись регистров по определённому адресу). Так что работа продолжится.

Для удобства я сделал утилиту tools/headless-term.js (наследника tools/rt11-term.js, который гонялся с включённым Puppeteer), как раз и позволяющую делать ввод-вывод, монтировать образы и ленты.

Так что задел тут хороший. Работа продолжается — диагностику теперь можно прогнать одной командой: tests/e2e-xxdp-ekbbf0.js.

Параллельно висел ещё один долг — персистентность дисков, она была не совсем честной: модифицированные блоки жили только в памяти JavaScript. Процессор можно было остановить и перезагрузить — изменения это переживали, а перезагрузка страницы их теряла. Сделать это, кстати, удалось быстро, ещё до окончания работ над рефакторингом: использовал локальное хранилище браузера.

А вот рефакторинг дал мне возможность сделать другую важную вещь — снапшоты. Это как в игровых ретро-консолях: нажал кнопку «Save», потом через неделю вернулся — а игра стартует с той точки, на которой завершилась. Вот так и yaPDP — загруженная месяц назад 2.11BSD продолжит играть в Adventure или с характерным грохотом печатать вторую половину документа.

Что дальше

Здесь у меня развилка.

  • Очень хочется получить MVP, который уже на 100% решает задачу погружения: параллельно осваивая плохо знакомый мне стек, я всё-таки добился нужного эффекта

Работа с BASIC-11 на Teletype Model 33 ASR

Ощущение уже другое, хотя и не полностью исторично: почти все кнопки нажимаются, локальный режим работает, перфоленты вводятся и выводятся, а иногда и дублируются (в 60-х так и делали)

  • Хочется продолжить работу над рефакторингом ядра, выпилить оригинал и добиться прохождения бОльшего количества XXDP-диагностик, которые позволят увеличить количество гарантированно работающего с yaPDP DEC-софта

Первые шаги уже видны в репозитории: src/core/bus.js, src/core/device.js и src/core/machine.js.

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.