unix
August 17

Теряем невинность с Таненбаумом: Amsterdam Compiler Kit

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

Именно о нем я сейчас расскажу.

Компиляция консольного приложения на Си из-под FreeBSD в.. MS-DOS. В 2025м году.

Статья была опубликована на Хабре.

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х, для примерно таких компьютеров:

Обратите внимание на ширину дисковода, это еще 3'5 дискеты, которые использовались до формата 1'44

Как выглядит 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х (старше автора) а компьютеры, на которых она работала выглядели примерно так:

Kaypro II

Тут надо сделать небольшое отступление и рассказать про эмулятор 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, история будет уже в отдельной статье.

Следите за анонсами, как говорится.