PunchEU proposes law curbing social media for children ThursdayESPNThese Broncos sure have a striking resemblance to Denver's last Super Bowl-winning team ...Daily MaverickReimagining agricultural education as AI transforms farming and future jobsוואלה7 שנות מאסר לסייעת בגן ילדים בנתניה שהורשעה בהתעללות ב-12 פעוטותInquirerMarcos welcomes Imelda acquittal after 40-year graft legal battleRTP DesportoDjokovic fora do top 10 ATP, Zverev ameaça SinnerThe Jerusalem PostIsrael's oldest Holocaust survivor dies at at 107 on Rosh HashanahUOLDireita vê Moraes blindado; esquerda, Flávio protegidoBollywood HungamaEXCLUSIVE: NO visual cuts in Kareena Kapoor Khan-starrer Daayra; CBFC replaces just one word with ‘Minister’20 MinutenChemie-Alarm: Bevölkerung darf Fenster wegen Gestank nicht öffnenObservador DesportoMeghan partilha imagens da vida familiar no Reino UnidoVariety‘Club Kid’ Wins Top Prize at Deauville American Film Festival
The Daily Newsstand · Free, Always
Monday, September 14, 2026

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

Translate
Ассемблирование будущего загрузчика внутри RT-11

Вот он, таймлайн 1975 года. Объясняю, как я заставил эмулятор родить этот экран самостоятельно.

Представьте компьютер, который после включения не делает вообще ничего. Буквально. В 1975 году это было нормой: постоянная память стоила дорого, и операторы вручную щелкали тумблерами, чтобы оживить машину.

Как я докатился до рекурсивной компиляции?

Если вы попали сюда случайно, вот краткая хронология моего помешательства:

  1. В начале была магия (Часть 1): Мы просто запустили PDP-11 в браузере. Играли в Moon Lander, слушали стрекот принтера. Читать первую часть →

  2. Затем пришла суровая проза (Часть 2): Пришлось объяснять читателям, почему WebView ненавидит CRT-эффекты, а Tauri заставляет танцевать с бубном вокруг драйверов Windows. Читать вторую часть →

  3. А потом… (Текущая статья): Я понял, что собирать код хостовым кросс-компилятором — это моветон. Пусть эмулятор мучается сам.

Но что, если пойти дальше обычной эмуляции? Зачем собирать загрузчик для ретро-компьютера на современном ПК через кросс-компиляторы, если внутри самого эмулятора крутится настоящая операционная система RT-11 с аутентичным компилятором от DEC?

В этой статье мы провернем инженерный инсепшен: заставим эмулятор PDP-11/70 в браузере собрать свой собственный бутлоадер «родным» инструментарием полувековой давности. Будет чистый ретро-хардкор: передача файлов через виртуальные перфоленты, битва за каноничный консольный промпт @ вместо исторической ереси и суровая экономика золотого века IT. Змея, кусающая себя за хвост — но в самом лучшем смысле.

В первой статье я рассказывал, как yaPDP — мой эмулятор PDP-11/70 в браузере — начал «помогать себя разрабатывать»: я взял boot.mac, скормил его host-сторонним macro11.exe и получил свежий бутлоадер. Работало, но оставался осадок: ассемблер-то был неродной, эмуляция DEC-инструментария на хосте. Как-то это… не по-канону.

А потом я понял: стоп. У меня же в браузере крутится настоящая PDP-11/70. На ней можно запустить настоящий RT-11. А на RT-11 живёт настоящий DEC MACRO-11. Зачем мне host-ассемблер, если у меня в кармане целый машинный зал?

Так родилась идея, от которой у любого ретро-энтузиаста загораются глаза: собрать новый бутлоадер оригинальным инструментарием DEC, запущенным внутри эмулятора, который этот самый бутлоадер и загружает. Змея, кусающая себя за хвост, — но в хорошем смысле.

Канон против «Boot>»

Сначала — зачем вообще пересобирать загрузчик. Доставшийся мне в наследство bootstrap (вместе с ядром эмулятора) был… скажем так, творческим. Это был монстр на 2008 слов: сам загрузчик, прилинкованный ODT11 (DEC On-line Debugging Tool), демонстрационные процедуры LIGHTS и CHASER для мигания лампочками на панели, MMU-демо и приглашение Boot>.

И вот тут — главная ересь. Настоящие загрузчики DEC никогда не печатали Boot>. Канонический промпт bootstrap-ромов PDP-11 — это лаконичное @. Именно его ждал оператор, именно под него написана вся документация той эпохи.

Кстати, забавный факт: приведя загрузчик к канону, я впервые увидел на экране два @ подряд (@boot rk0 → @unix) и на секунду задумался — не смутит ли это пользователя. Не смутит: это ровно та картина, которую видел оператор настоящей машины. «Boot>» — это как если бы в книжке по MS-DOS переименовали C:> в «Готов к работе!».

Почему именно @?

Символ `@`, известный сегодня как «собака», имеет долгую историю. В торговле он использовался для обозначения предлога *at* («по [цене]»), что отражено, например, в записи: «3 lbs. bananas @ $.50/lb.» — три фунта бананов по цене $0,5 за фунт. В операционной системе **RT‑11**, разработанной компанией DEC для PDP‑11, этот символ стал стандартом для приглашения загрузчика. Он был лаконичным, легко распознаваемым на экране и не занимал много места в памяти, что было критично в те времена.

Когда Деннис Ричи и Кен Томпсон переносили Unix на PDP‑11, они сохранили многие элементы интерфейса от RT‑11.

Для Кена Томпсона, который стремился сэкономить каждый байт в крошечном 512-байтном первичном загрузчике, символ @ был идеален с точки зрения ассемблера PDP-11.

  • В восьмеричной системе счисления (которая была родной для PDP-11) ASCII-код символа @ равен 0100 (в шестнадцатеричной — 0x40).

  • Круглое число 0100 в ассемблере обрабатывать и проверять гораздо проще и «дешевле» по объему кода, чем, например, знак доллара $ (044) или процента % (045).

Интересно, что была и “аппаратная” причина: телетайпы подключались к PDP-11 через последовательные интерфейсы (токовая петля 20 мА). При включении компьютера, перезагрузке или сбросе линии в линию связи часто проскакивал электрический шум — «мусорные» байты.

  • Код 0100 (@) имеет очень специфическую битовую маску в ASCII (ровно один установленный бит в седьмом регистре: 01000000 в двоичной системе).

  • На фоне случайных шумов, которые могли принимать значения 0177 (ручной сброс/DEL) или 0000 (NULL), символ @ был максимально четким сигналом готовности, который сложно перепутать с помехами.

Кстати, В самих Unix V1–V5 символ @ имел фундаментальное значение — он использовался как управляющий символ Line Kill (стереть всю строку). Если пользователь ошибался при вводе команды, он нажимал @, и драйвер терминала понимал, что всю текущую строку нужно аннулировать и начать ввод заново.

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

План канонизации:

  • Промпт @ вместо Boot>

  • Команды только BOOT и HELP — без LIGHTS/CHASER/ODT

  • ODT11 — отдельным модулем (он ещё пригодится, я его сохранил)

  • Пустой баннер — тихий старт, без пафосных надписей

  • Размер: 2008 слов → 673 слова

История человеческой лени (и почему мы здесь)

Сейчас трудно представить, что включённый компьютер сразу после старта… не делал ничего, но это было именно так.

В те времена сделать память небольшого объёма было можно, но она была банально дорога, а часто и ненадёжна.

Первое решение — набор переключателей или диодная матрица.

Минимальный загрузчик с перфоленты Model 33 ASR занимает от 12 до 16 слов. Это 256 переключателей плюс обвязка. Вся эта дорогая плата почти не использовалась — компьютер стоил как крыло самолета, его старались не перезагружать без нужды.

Время техника, который изредка вбивает пару десятков слов с инженерного пульта и заправляет перфоленту со вторичным загрузчиком, стоило дешевле для бизнеса. Позже появились ёмкие микросхемы ROM/PROM (платы BM873, M9312), которые решали ту же задачу аппаратно, используя арбитраж шины UNIBUS.

Но операторы 70-х всё равно ленились. Они автоматизировали ввод второго промпта двумя путями:

  • Патч исходника: правили цикл ожидания ввода в файле boot.s Unix V5/V6. Если строка пустая — подставлять имя ядра unix.

  • Аппаратный обман: модифицировали вторичный загрузчик так, чтобы он читал состояние тумблеров Switch Register (адрес 177570). Определенная комбинация битов означала автозапуск файла /unix.

Гимн лени

Честно говоря, мне эта идея нравится настолько, что я её реализую в будущем. Когда-нибудь.

Позже в модифицированный исходник загрузчика стали добавлять тайм-аут, после которого автоматически “нажимался Enter” и процесс загрузки стартовал сам.

Это уже напоминает современные нам загрузчики (привет, GRUB!).

Перфолента — мост между эпохами

Осталась техническая проблема: как передавать файлы между хостом и гостевым RT-11?

В 1975 году ответ был один — перфолента. И я уже добавил в эмулятор честный перфоратор и перфосчитыватель.

Ленты, экспортированные из эмулятора, можно заправить и в симулированный, и в настоящий телетайп. Значит, и в обратную сторону — тоже.

Получился конвейер, до смешного похожий на то, что делали операторы полвека назад:

  • Исходник boot.mac на хосте превращается в ленту (файл .ptap)

  • Лента «заправляется» в перфосчитыватель эмулятора

  • Гостевая RT-11 читает её: COPY PC: BOOT.MAC

  • Дальше всё делает настоящий DEC-инструментарий — MACRO-11 V04.00C и LINK V06.01H

  • Готовый .SAV выводится на перфоратор

  • Лента экспортируется на хост и превращается в bootcode.js

Мой терминальный клиент получил команды нового поколения: :mount, :export, :raw. Связь с внешним миром — через дырочки.

headless-term.js — консольный мост

Вся магия управляется одной утилитой — tools/headless-term.js. По сути это мост между моим терминалом и консолью гостевой RT-11. Строки, начинающиеся с :, утилита забирает себе:

  • :mount <file> — заправить перфоленту

  • :rewind — перемотать ленту

  • :export <file> — вынуть накопленное с перфоратора

  • :wait <marker> — ждать маркер

  • :raw <hex> — отправить сырые байты (например, :raw 03 — Ctrl+C)

Промпт распознаётся хитро: точка в начале строки плюс ~300 мс тишины. Оператор 1975 года оценил бы эту автопаузу.

Конвейер сборки

Вот полный листинг того самого скрипта, который я скормил утилите пайпом:

:mount boot.mac.ptap
COPY PC: BOOT.MAC
R MACRO
DK:BOOT.OBJ,DK:BOOT.LST=DK:BOOT.MAC
R LINK
DK:BOOT.SAV=DK:BOOT/B:120000
COPY DK:BOOT.SAV PC:
:export boot.sav.bin

Красота в том, что ассемблирует и линкует не эмуляция, а оригинальные бинарники DEC. Я даже получил настоящий листинг .LST — и по нему проверил все 17 адресных целей макроса LDA. PRTAST=120514, INPAST=120620, EOL=120273 — красота, а не код.

Скрипт tools/bin2bootdump.js превращает бинарник в массив восьмеричных слов для памяти по адресу 0120000.

Естественно, чтобы не вводить эту последовательность каждый раз руками, есть команда npm run rebuild-boot, которая вызывает достаточно немудрящий скрипт tools/rebuild-bootcode.js, автоматизирующий операцию. tools/headless-term.js в основном используется для интерактивной работы и исследований.

Грабли, на которые я наступил

Разумеется, без них никуда. Три истории, достойные мемуаров.

Перфоратор резал байты до 7 бит. Первый экспортированный .SAV упорно не декодировался: вместо C1 15 я видел 41 15. Оказалось, хук экспорта ленты делал byte & 0x7f — для красоты визуала телетайпа Model 33 ASR. Но физически-то лента 8-дорожечная! Фикс — одна строчка (byte & 0xff), зато теперь понятно, почему наши предки трепетно относились к переключателю чётности.

Формат .SAV от LINK V06.01H. Оказалось, это чистый образ памяти без заголовка: смещение в файле = адрес в памяти. Никакого 6-словного заголовка — вот тебе и «документация» сорокалетней давности.

ODT11 и символы с точкой. Когда я вынес ODT11 в отдельный модуль, ассемблер начал выдавать загадочное EQ 0 на символе ODT11S. Виновник найден — forward-ссылки на символы с точкой в имени (вроде s.t), которые MACRO-11 прощал не всегда. Знание, за которое я заплатил парой часов и которое теперь живет в macro-asm/README-odt11.md.

И напоследок — мелочь, но показательная: при склейке шапки файла я умудрился получить дважды var bootcode=[. node --check поймал мгновенно, но осадочек остался: даже в 2026-м синтаксические ошибки случаются из-за tail -n +2.

Боевой приём

Новый загрузчик прошёл полный цикл проверки: юнит-тесты (35 файлов), e2e-снапшоты, и — самое вкусное — живой прогон. Вот он, канон, на экране:

@
VALID COMMANDS ARE BOOT OR HELP
@boot rk0
@unix
login: root

Видите эти два @? Это не дубликат и не баг. Это 1975 год собственной персоной: первый @ — загрузчик DEC ждёт команду, второй — Unix V5 спрашивает имя ядра. Всё как на настоящей машине.

А по дороге я заодно:

  • починил перфоратор — теперь из эмулятора можно вывозить любые бинарники;

  • выяснил, что в RT-11 V04 .TTYOUT — это EMT 341, а не EMT 1;

  • получил инструмент bin2bootdump.js для любых образов PDP-11.

Итог

2008 слов → 673. Промпт @ вместо Boot>. ODT11 — отдельным модулем. Тихий старт без баннера. Всё собрано оригинальным DEC MACRO-11 V04.00C, запущенным внутри эмулятора, а наружу вывезено через перфоленту — как и положено.

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

Если хотите попробовать прямо сейчас — перейдите по ссылке https://amesk.github.io/yaPDP/pdp11.html, нажмите “Bootstrap now!” и увидите канонический @.

Исходники: github.com/amesk/yaPDPmacro-asm/boot.mac, macro-asm/README-odt11.md, tools/bin2bootdump.js, tools/headless-term.js, tools/rebuild-bootcode.js. Сайт проекта: https://amesk.github.io/yaPDP.

Путеводитель по циклу эмулятора yaPDP

Чтобы вы не заблудились в машинном зале семидесятых, вот хронология проекта:

Часть 1. Магия запуска «Эмулятор PDP-11/70 в браузере: запуск UNIX V5, телетайп и Lunar Lander» ➡️ Перейти к первой части
Здесь мы впервые оживили старую железку прямо в Chrome.

Часть 2. Суровая правда фронтенда «Как построить прокуренную серверную 70-х внутри браузера: Tauri, WASM и SharedWorker» ➡️ Перейти ко второй части
Архитектурные компромиссы, ад WebView и борьба за миллисекунды звука.

Часть 3. Змея кусает хвост (Текущая статья) «Эмулятор, который собрал свой загрузчик сам: оригинальный MACRO-11 внутри RT-11» ➡️ Вы уже здесь
Рекурсивная компиляция через виртуальную перфоленту и канонический промпт @.

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.