Теряем невинность с Таненбаумом: Amsterdam Compiler Kit
Пока вы рождались, ходили в школу, заканчивали учебу и находили свою первую работу — на свете существовал совершенно особенный набор компиляторов, о котором крайне мало известно на просторах РФ.
Именно о нем я сейчас расскажу.
Статья была опубликована на Хабре.
Amsterdam Compiler Kit
Вряд ли среди читателей обнаружится аксакал живой пользователь этой интересной штуки:
The Amsterdam Compiler Kit is a venerable piece of software that dates back to the early 1980s. It was originally written by Andrew Tanenbaum and Ceriel Jacobs as a commercial product; for many years it was also used as Minix’ native toolchain. After eventually failing as a commercial project, it was made open source under a BSD license in 2003 when it looked like it was going to be abandoned and the code lost.
Сочетание «начало 80х» и «коммерческий продукт» оставляет мало шансов на появление пользователей ACK в родных краях, поскольку в 80е еще вовсю жил СССР, где вопрос покупки иностранного программного обеспечения был мягко говоря неактуальным.
Теперь расскажу подробнее, что там внутри и почему оно до сих пор представляет интерес:
The ACK contains compilers for ANSI C, K&R C, Pascal, Modula-2, Occam 1, and a primitive Basic. It contains code generators for a large number of architectures, mostly 8 and 16 bit machines; there are also a set of generic optimisation, linker and librarian tools.
В принципе стандартный набор языков для тех лет, но есть нюанс:
It contains assembler and linker support for: 6500, 6800, 6805, 6809, ARM, i80, Z80, Z8000, i86, i386, 68000, 68020, NS32016, S2650, SPARC, VAX, PDP11 and VideoCore IV.
Это уже несет определенный «вау-эффект», причем как для тех так и для этих лет, поскольку даже для популярных clang
и gcc
столь широкая поддержка различных архитектур решается путем форков и неофициальных патчей.
В мейнстриме и готовых пакетах столь дикого набора архитектур разумеется нет, при этом сильно устаревшие еще и регулярно удаляют.
Современная версия ACK, которую с 2003 года разрабатывается как открытый проект, поддерживает следующие платформы:
pc86, linux386, linux68k, linuxppc, linuxmips, cpm, rpi (VideoCore IV), pdpv7, msdos86 and msdos386.
Лично у меня глаз начал дергаться после cpm
и pdpv7
, msdos86
по сравнению с этим — мелочи.
CP/M это операционная система из 1970х, для примерно таких компьютеров:
Как выглядит PDP-7 уже показывал в предыдущей статье, но на всякий случай напомню:
Чтобы у вас не сложилось впечатление, будто ACK это только лишь про плешивых дедов и их древние игрушки, покажу как выглядит заявленный выше VideoCore IV:
Как видите это уже вполне себе современная железка, используемая в различных устройствах.
Minix
Отдельно стоит упомянуть историю с Minix — та самая операционная система, созданная Таненбаумом для обучения студентов.
В почтовой рассылке, посвященной этой ОС когда-то давно некий Линус Торвальдс впервые представил свой известный проект, вызвавший эпический архитектурный срач, ныне являющийся историческим событием.
Дело в том, что ACK когда-то был основным системным компилятором в Minix, при этом являясь коммерческим продуктом — поставлялся в виде готовых бинарников:
The ACK has been used as the standard Minix compiler for years. While the ACK was still commercial, this was done by distributing binaries; when it get opened, a version was forked off and is now used as part of the Minix base build.
Форк с поддержкой Minix мне был не особо интересен, поэтому искать не стал. Тем более что в современной Minix 3 в качестве системного компилятора используется вполне стандартный clang.
Однако на поддержке столь широкого набора архитектур возможности ACK не заканчиваются и чтобы добить окончательно нежную психику современных разработчиков, процитирую следующий абзац:
Each language comes with its own runtime, so if you’re a C programmer you also get a libc. Compared to gcc, it is far smaller, faster and easier to port.
Конечно стоило догадаться об этом прочитав список поддерживаемых архитектур и прикинув как оно вообще может работать, но тем не менее.
Так что ACK это уникальный, редкий и необычный проект, позволяющий творить запредельную дичь вроде кросс-компиляции из FreeBSD в MS-DOS, которую вы могли видеть в шапке статьи.
Ниже я опишу процесс сборки и использования этого необычного проекта.
Сборка
Собирать буду по традиции на FreeBSD, поэтому часть требуемых шагов несколько отличается от стандартных.
Проект старый, разработка в git ведется давно, поэтому внутри репозитория находится множество разных веток, не актуальных для обывателя.
Чтобы не выкачивать всю эту дичь, я использовал ключ --depth -1
, с которым будет вытащена только ветка по-умолчанию:
git clone --depth 1 https://github.com/davidgiven/ack.git
Таким образом собирать мы будем текущую 6.0 версию:
ACK 6.0 is a ground-up reworking of the whole compiler suite, with a lot of the more archaic features removed.
Сборка проекта.. весьма своеобразна, поскольку основана на скриптах Python и немного Lua.
The version 5.0 build mechanism has been completely rewritten (twice).
Для сборки нужен достаточно стандартный набор инструментов:
- любой ANSI C компилятор (автор использовал GCC)
- flex и yacc
- GNU make (gmake)
- Lua с библиотекой
lua-posix
- Python 3.4 и выше
- ~2Гб свободного места
Запускается сборка стандартным образом — вызовом GNU Make
в корне проекта:
gmake
Поскольку автор собирал на FreeBSD, которая имеет определенную специфику в именовании инструментов, появится такая ошибка:
Происходит это из-за того что бинарник lua
во FreeBSD имеет постфикс версии:
Так что надо открыть Makefile
в корне проекта и поменять значение переменной LUA=
, добавив версию:
Следующая ошибка также специфична для FreeBSD, поскольку gcc
у нас тоже с постфиксом версии:
Несмотря на документацию, которая утверждает что актуальный компилятор должен подхватываться через стандартную переменную окружения CC=
, нашлось место в скриптах сборки, где были прямо забиты названия используемых бинарников:
Нужный файл называется ack/build/ab.mk
и почему-то несмотря на название и расположение — не является генерируемым.
По аналогии с lua
, добавляем постфикс версии и сохраняем:
После этого заново запускаем сборку и ждем, никаких других ошибок при сборке замечено не было.
Итоговый размер после завершения сборки, со всеми временными файлами получился размером в 1.7Гб
, что несколько больше заявленного в требованиях:
По-умолчанию ACK устанавливается в каталог /opt/pkg/ack
, поэтому запускаем из корня проекта:
mkdir -p /opt/pkg/ack gmake install
Перед установкой будут запущены тесты, но далеко не все:
Итоговый каталог bin
выглядит следующим образом:
Хотя основные бинарники находятся в ack/lib/ack
:
Теперь переходим к самому интересному — к запуску и работе с ACK, это будет действительно весело.
В репозитории проекта находится каталог examples
, где лежат примеры более-менее сложной логики на Си, Паскале и Бейсике, которые точно собираются и работают с помощью ACK.
Один из таких примеров под названием mandelbrot.c , выводящий в консоль с помощью символа *
фрактал Мандельброта вы можете лицезреть в работе на заглавной картинке к статье.
Но поскольку мне был интереснее сам процесс компиляции и запуска приложений на разных экзотических архитектурах из древних времен нежели специфика каждой конкретной платформы, не стал заморачиваться сложной логикой, взяв в качестве эталона классический «Hello world!» на Си:
#include <stdio.h> int main(void) { printf("Hello, alex0x08 \n"); return 0; }
И собственно ниже покажу сборку и запуск этой нестареющей классики под крайне экзотические (по современным меркам) архитектуры.
Компиляция FreeBSD->MS-DOS
Компиляцию в COM-файл с последующим запуском можно увидеть на заглавной картинке, поэтому ниже покажу компиляцию в EXE
под DOS:
/opt/pkg/ack/bin/ack -mmsdos386 -O hello.c -o hello.exe
msdos386 produces i386 MS-DOS 32-bit DPMI .EXE files
Поэтому для работы нужен запущенный DPMI-резидент — т. н. «расширитель памяти», который можно взять например тут.
Как видите запуск осуществлялся в известном эмуляторе DOS под названием Dosbox, установленном из пакетов FreeBSD.
Компиляция для CP/M
Продолжая исторический угар, показываю сборку и запуск под CP/M, напоминаю что это операционная система из 1970х (старше автора) а компьютеры, на которых она работала выглядели примерно так:
Тут надо сделать небольшое отступление и рассказать про эмулятор CP/M, поскольку его в пакетах FreeBSD не нашлось — пришлось собирать руками.
RunCPM - Z80 CP/M emulator
Разработка ведется на Github, забираем исходники:
git clone https://github.com/MockbaTheBorg/RunCPM.git
При сборке будет описанная выше проблема с номером версии в названии исполняемых файлов компилятора GCC — стандартная для FreeBSD, поэтому необходимо в файле RunCPM/Makefile.posix
в переменную СС=
добавить номер версии:
Сама сборка запускается командой:
gmake posix build
Но это еще не все приключения, после сборки необходимо подготовить рабочее пространство — специальный каталог из которого будет запускаться эмулятор:
mkdir disk unzip ../../DISK/A0.ZIP cp ../RunCPM .
В результате появится каталог A/0
, внутри которого будут все управляющие команды CP/M, в корне будет запускаемый бинарник самого эмулятора.
Все готово к использованию, для проверки можно запустить эмулятор:
Должна быть отображена шапка с версией и работать команда DIR
. Для завершения работы эмулятора введите EXIT
.
Теперь можно переходить к сборке тестового приложения.
«Hello world» для CP/M
Для компиляции под эту систему достаточно указать ключ -mcpm
:
/opt/pkg/ack/bin/ack -mcpm -O hello.c -o hello.com
Следующим шагом копируем полученный бинарник, обязательно с именем в верхнем регистре:
cp ~/hello.com ./A/0/HELLO.COM
Запускаем эмулятор и вводим команду hello
:
Круто, но недостаточно, поскольку среди поддерживаемых ACK систем есть:
pdpv7 produces PDP/11 V7 Unix binaries
Думаю вы догадываетесь, что пройти мимо такого было невозможно, поэтому автор убил еще неделю показываю нечто действительно удивительное.
Компиляция для Unix v7 и PDP в 2025м году
Чтение этого абзаца прибавляет 100 баллов к инженерным навыкам и на 10см удлиняет известный половой орган.
Напоминаю как выглядел PDP-11:
Вот так выполняется компиляция из ACK для этого древнего монстра:
/opt/pkg/ack/bin/ack -mpdpv7 -O hello.c -o hello
Как ни странно и неожиданно, но стандартная утилита file, присутствующая во всех UNIX-системах с незапамятных времен честно показывает тип:
Apout — Simulate PDP-11 Unix a.out binaries
Для проверки я сначала запустил полученный бинарник на этом:
This program is a user-level simulator for UNIX a.out binaries. Binaries for V1, V2, V5, V6, V7, 2.9BSD and 2.11BSD can be run with this simulator. The user-mode PDP-11 instructions are simulated, and TRAP instructions are emulated by calling equivalent native-mode system calls.
Собирается оно под FreeBSD одной командой, поскольку внешних зависимостей нет:
export CC=gcc13 gmake
После сборки в корне проекта появится бинарник apout
, так выглядит в работе запуск нашего «Hello world»:
Но разумеется сильно круче было бы попробовать запустить в реальном симуляторе PDP с Unix v7 на борту, что я и сделал.
SIMH
Эмуляцию PDP как впрочем и множества других исторических систем обеспечивает известный проект Open SIMH. Нужный нам Unix v7 заявлен на главной странице проекта в качестве ключевого примера:
For example Version 7 Unix, released in 1979, runs unchanged today on SimH.
В этот раз эмулятор присутствовал в готовом виде среди пакетов FreeBSD, так что хотя-бы его не пришлось собирать из исходников.
Для начала забираем готовый образ диска с Unix v7 для PDP-11 отсюда, существует известная инструкция по полной установке с использованием образов установочной ленты:
Но автор решилне убивать еще неделючто это уже перебор, поэтому использовал готовый образ диска.
В архиве по ссылке будет файл unix_v7_rl.dsk
, который необходимо распаковать:
mkdir v7 cd v7 unzip ~/uv7swre.zip
Дальше необходимо создать конфигурационный файл эмулятора:
set cpu u18 set cpu idle attach rl0 unix_v7_rl.dsk attach rl1 hello.tar boot rl0
Сохраните файл как simh-pdp11.ini
, в том же самом каталоге v7
, куда был распакован образ диска.
Теперь надо создать tar-файл с собранным бинарником "Hello world" приложения:
cd ~ tar cvf hello.tar hello cp hello.tar ~/v7/
Итоговый набор файлов должен выглядеть как-то так:
pdp11 simh-pdp11.ini
После появления приглашения в виде символа @
вводим:
boot
Появится древний предок современного Grub, загрузчик:
rl(0,0)rl2unix
Появится приглашение в виде символа #
, что означает запуск Unix v7 в однопользовательском режиме:
Нажмите Ctrl
- D
для начала работы в многопользовательском режиме:
Появится хорошо знакомое любому юниксоиду приглашение авторизации.
Введите root
в качестве логина и пароля:
При первом запуске будет необходимо выполнить ряд дополнительных шагов.
Создаем каталог для временных файлов:
mkdir /tmp
cd /dev make rl
Таким образом со стороны запущенной в эмуляторе Unix v7 будет доступно устройство, эмулирующее ленту и можно будет добраться наконец до собранного на хосте бинарника:
cd /tmp tar xvf /dev/rrl1
В результате в файловой системе появится тот самый файл hello
, собранный на FreeBSD с помощью ACK:
Ну кто еще вам спрашивается покажет такую красоту?
Эпилог
ACK имеет отличную портируемость и расширяемость, поэтому существует столь интересный форк этого проекта:
This fork of the Amsterdam Compiler Kit supports the Cray X-MP supercomputer and the COS operating system platform.
Его тоже удалось собрать и запустить, но ввиду невероятной сложности самобытности COS, история будет уже в отдельной статье.