Оживляем легенду: Как я написал кроссплатформенный эмулятор PDP-11 на C++ и Qt6 с дизассемблером и прерываниями
Привет, Пикабу!
Наверняка многие из вас, кто изучал в университете «Архитектуру ЭВМ» или системное программирование, сталкивались с архитектурой PDP-11. Эта машина стала настоящей классикой: её элегантная система команд и ортогональные методы адресации стали основой для обучения целых поколений программистов.
Однако софта для эмуляции PDP-11, который был бы удобным, современным и работал "из коробки" на современных ОС, критически не хватает. Часто это консольные утилиты из 90-х или софт, требующий бубнов при установке.
Поэтому я решил написать свой собственный эмулятор PDP-11 — с графическим интерфейсом, интерактивной памятью, виртуальной периферией и встроенным справочником.
Главное окно эмулятора: таблица оперативной памяти с мгновенным дизассемблером (загружена программа Hello World), панель регистров процессора и интерактивный контекстный справочник.
Что под капотом?
Эмулятор написан на C++17 с использованием фреймворка Qt6. Я ставил перед собой цель сделать не просто «выполнялку» кода, а именно наглядный инструмент для студентов и энтузиастов, чтобы каждый такт процессора был понятен.
Что умеет эмулятор на данный момент:
Интерактивная память и дизассемблер "на лету"
Сердце интерфейса — таблица оперативной памяти. Как только вы вводите восьмеричный код в ячейку (например, 005203), эмулятор мгновенно дизассемблирует его и показывает мнемонику — INC R3. И наоборот, ошиблись цифрой — увидите UNKNOWN или другую команду. Это невероятно ускоряет процесс понимания машинного кода.Полный набор регистров и флаги PSW
Внизу окна всегда видны состояния регистров R0-R7 и слово состояния процессора (PSW). При пошаговом выполнении (Step Mode) можно наблюдать, как меняются флаги N, Z, V, C в зависимости от результатов арифметики.Memory-Mapped I/O (Внешние устройства)
Какая же ЭВМ без периферии? Я реализовал виртуальные устройства, отображаемые на память по классическим адресам PDP-11:Дисплей терминала (регистры TPS 177564 и TPB 177566)
Клавиатура (регистры TKS 177560 и TKB 177562)
Принтер (регистры 177514 и 177516)
Виртуальный терминал, работающий через классический Memory-Mapped I/O. Процессор посимвольно вывел строку в регистр данных дисплея по адресу 177566.
Аппаратные прерывания и таймер
Отдельная гордость — это честная реализация работы с прерываниями. Это не просто скрипт, который выполняет команды по списку. Процессор умеет:
Обрабатывать команду WAIT (переход в режим энергосбережения).
Генерировать аппаратные прерывания. Например, встроенный таймер дергает вектор 100(8) каждые 100 мс.
В комплекте с эмулятором идет программа «Stopwatch» (Секундомер). Она показывает, как можно отсчитывать реальные секунды в регистре R0, перехватывая тики таймера и отправляя процессор в спячку (WAIT) между ними. Всё как в настоящем железе!
Сборка и DevOps (чтобы работало у всех)
Как разработчик, я ненавижу, когда для запуска опенсорс-проекта нужно потратить полдня на настройку окружения. Поэтому я заморочился с инфраструктурой:
Для Windows: Написан "умный" скрипт compile.bat. Вы просто запускаете его на чистой Windows. Скрипт сам скачает MSYS2, установит нужный GCC toolchain, подтянет статическую версию Qt6 и соберет независимый PDP11.exe весом в пару десятков мегабайт (без необходимости таскать за собой гору .dll).
Для Linux (Arch/Manjaro): Написан скрипт setup_package.sh, который на лету генерирует PKGBUILD, ресайзит иконки через ImageMagick, создает .desktop файл и устанавливает эмулятор как полноценный нативный пакет в систему.
Кросс-компиляция: Поддерживается сборка Windows .exe файла прямо из-под Linux через MinGW.
Примеры программ
Чтобы пользователям не пришлось начинать с чистого листа, я подготовил библиотеку дампов .pdp, которые идут в комплекте:
Hello World — классика с циклом и проверкой флага готовности дисплея.
Keyboard — программа эхо-ввода (выводит на экран то, что набирается на клавиатуре).
16-bit Integer Calculator — полноценный калькулятор (+, -, *, /) с программной реализацией алгоритмов умножения и деления (которых нет в базовой архитектуре PDP-11) и конвертацией бинарного результата в ASCII-строку для вывода на экран.
Но просто выложить код — это полдела. Поскольку проект задумывался как образовательный, я уделил огромное внимание документации. Каждая программа из примеров разобрана буквально по строчкам: с указанием адресов, машинных кодов, мнемоник и подробным описанием логики.
Кроме того, в репозиторий вшита подробнейшая документация по самой архитектуре PDP-11. Я перевел классические справочники из старых PDF-файлов в современный, аккуратно сверстанный Markdown (с поддержкой Obsidian-ссылок).
В нем расписана работа каждой машинной инструкции, особенности адресации и, самое главное для написания эмуляторов — точное влияние каждой команды на флаги регистра состояния (N V Z C).
Пример страницы из справочника: описание работы команды сложения и её влияния на регистр состояния процессора (PSW).
Итоги и планы
Проект полностью открытый, распространяется под свободной лицензией MIT и доступен на GitHub. Если вы преподаете архитектуру ЭВМ или просто хотите поностальгировать по временам, когда программы писались на машинном коде — заходите, пробуйте, делитесь идеями!
Что можно улучшить в будущем:
Внедрить поддержку макроассемблера (чтобы можно было писать код текстом, а не только вводить восьмеричные числа).
Добавить поддержку дисковых накопителей.
🔗 Исходный код, релизы и инструкции: Github
📩 Почта для связи и предложений: Email
Буду рад любой критике, код-ревью и предложениям в комментариях. Если есть идеи, чего не хватает для идеального учебного эмулятора — пишите!























