У нас было 640Кб памяти, CGA-экран, 20-мегабайтовый диск и целых четыре мегагерца тактовой частоты. А еще старые пятидюймовые дискеты на 360кб. Не то чтобы это был необходимый набор для современного разработчика C++, но если уж начали коллекционировать дичь, то сложно остановиться..

Весь сетап целиком.
Весь сетап целиком.

Проклятая выдача

Наверное все уже в курсе, что социальные сети и поисковые системы постоянно подкручивают ленту выдачи под каждого пользователя на основе предыдущих запросов и интересов.

На меня эти алгоритмы тоже начали влиять, но.. особенным образом:

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

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

Так появилась и эта замечательная статья:

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

Если серьезно, то конечно же этот материал появился не на пустом месте и не за один день, это снова результат довольно долгого изучения матчасти, ковыряния исходников и темных ритуалов из Некрономикона.

Все ради того чтобы в очередной раз показать читателям невозможное, по мнению ИИ и обитателей StackOverflow.

Технический ультрахардкор

Коль уж читаете эту статью — скорее всего и так знаете через какое место как именно программы появляются на свет и какова роль компилятора в этом сложном процессе.

Для простых обывателей поясняю:

чтобы получить тот самый steam.exe, которым вы жадными ручками запускаете любимые игры, его необходимо собрать из исходников.

Сотрудник компании Valve вместо работы над Half-Life 3 запускает компилятор, который собирает из набора файлов с исходниками «финальный билд», который затем упаковывается в инсталлятор и выкладывается на официальный сайт Steam.

Откуда его потом скачивают ушастые простые пользователи.

Теперь представьте:

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

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

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

И находите на развалинах бывшего офиса Valve ту самую флешку с исходниками альфа-версии Half-Life 3.

Конечно прогресс в разработке программ к тому времени успел уйти далеко вперед, у вас в ходу на лунной базе теперь какие-нибудь C++-157 и clang-777, с встроенным Rust, тремя слоями виртуализации и сборщиком мусора, работающим через промпты LLM.

А про язык C вы узнали лишь из лекции по истории, про исторический период между египетскими пирамидами и вторжением инопланетян.

Встает очевидный вопрос:

как же все-таки насладиться шедевром далеких предков, так и не увидевшим свет в свое время?

Многое можно отдать за запуск этой красоты, даже спустя пару сотен лет после зомби-апокалипсиса:

Нет, это не релизная версия.
Нет, это не релизная ��ерсия.

Несмотря на весь этот «апокалиптический треш», ситуация сама по себе — более чем реальная:

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

В первую очередь это мейнфреймы, которые до сих пор живее всех живых, вроде такого красавца от Unisys:

Далеко не только IBM занимается мейнфреймами, до сих пор.
Далеко не только IBM занимается мейнфреймами, до сих пор.

Если немного снизить накал технического фетишизма экстрима и обратить взор к современной коммерческой разработке — можно внезапно обнаружить, что фактически вся ее продуктовая часть (где создается приложение, устанавливаемое у конечного пользователя) работает с обязательной поддержкой устаревшего окружения:

поддержка Windows 7/10, поддержка старых версий Android и iOS, обязательная работа на JRE 1.8+ и так далее и тому подобное.

Разумеется на машинах разработчиков используется современный софт, но собирает он так чтобы конечное приложение без проблем запускалось и работало в устаревшем окружении.

Обратное портирование

Процесс, с помощью которого в окружении 21го века создается нечто работающее на оборудовании из века 20го называется обратным портированием (backporting).

И мы уже неоднократно о нем рассказывали, например тут или тут.

Обычно бекпорт создается для окружения с не очень большим устареванием — лет на 5-10 с момента релиза, поскольку на таком промежутке и проходят все основные обновления пользовательского окружения.

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

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

приложение на современном C++, с современными фичами, созданное в современной ОС, которое работает в окружении из 1986 года!

Операционной системой, которую выпустили 40 лет назад (практически мой ровесник) выступит известная MS-DOS 3.20.

Именно эта версия была выбрана ввиду интересной исторической особенности:

Version 3.20 – First retail release (non-OEM); Release date: July 1986

Это первая версия MS-DOS, продаваемая конечным пользователям напрямую от самой Microsoft, с полок магазинов.

До нее Microsoft продавала свою операционную систему только производителям компьютеров, которые включали ее в поставку своих продуктов и под своим собственным брендом:

"Заводной Абрикос"

Наконец показываю, как выглядит компьютер тех лет, для которого мы будем сейчас писать код на современном C++:

Фото современное, таких машин в живом состоянии сохранилось довольно много.
Фото современное, таких машин в живом состоянии сохранилось довольно много.

Комната жениха

Для начала, опишу тестовое окружение, которое будет использовано для столь задорной задачи.

Три ключевых компонента, необходимых для осуществления задуманного злодейства:

  • Компилятор C++ DigitalMars, который до сих пор официально поддерживает сборку под DOS;

  • Эмулятор 86Box для запуска DOS из 1986 года;

  • Утилиты GNU Mtools для работы с образами старых дискет.

И все это под FreeBSD 14, в качестве своеобразного гаранта портируемости:

если что-то работает на FreeBSD, оно однозначно будет работать и на Windows и в MacOS и в Linux.

Цифровой Марс

Главный герой сегодняшнего праздника жизни — набор компиляторов от Digital Mars:

Digital Mars is an American computer software company founded by Walter Bright and based in Vienna, Virginia. It makes C, C++, and D compilers, and associated utilities such as an integrated development environment (IDE) for Windows and MS-DOS, which Digital Mars calls an integrated development and debugging environment (IDDE)

Думаю уже по одному этому абзацу можно догадаться о накале предстоящей дичи, хотя ряд нюансов все же стоит пояснить.

Во-первых Вена это не только столица Австрии, но еще и небольшой городок на севере США, на 15к жителей, где (внезапно) находится штаб-квартира серьезной софтверной компании, выпускающей эпические компиляторы аж с 1988 года:

In 1988, Zortech was the first C++ compiler to ship for Windows. PC Magazine ran a graphics benchmark and reported that most executables produced by Zortech ran faster than executables produced by Microsoft C 5.1 and by Watcom C 6.5.[3]

Вот так, парни переплюнули официальный компилятор от самого Microsoft, несмотря на весь инсайд и поддержку.

Кстати «Zortech» — старое название компилятора (если вдруг будете искать в архивах), продукт по какой-то причине несколько раз переименовывался.

Вот тут кто-то оцифровал старое VHS-видео с демонстрацией работы компилятора, снятое в стилистике первого «Робокопа», которое отлично погружает в атмосферу тех замечательных лет:

Есть еще один удивительный факт, связанный с этой компанией:

The company gained notice in the software development community for creating the D programming language. D resulted from Bright's frustration with the direction of the C++ language and from his experience implementing it.

Ребята до такой степени угорели по С++, что создали свой собственный язык, «по мотивам»:

resulted from Bright's frustration with the direction of the C++ language

Язык кстати оказался вполне живой и активно развивается, так выглядит пример кода:

#!/usr/bin/env dub
/+ dub.sdl:
dependency "vibe-d" version="~>0.9.0"
+/
void main()
{
    import vibe.d;
    listenHTTP(":8080", (req, res) {
        res.writeBody("Hello, World: " ~ req.path);
    });
    runApplication();
}

Про язык D тоже будет статья, но это уже полностью современный тулчейн, собрать которым что-то работающее под DOS (тем более из 1986 года) врядли получится.

Так что вернемся к другим талантам этой замечательной компании — к разработкам для совсем старой школы:

  • Includes C++ templates, exception handling and rtti.

  • Most other compilers for 16 bit code were abandoned nearly a decade ago. Digital Mars has a modern compiler for 16 bits.

Все перечисленные выше радости — для официально поддерживаемой сборки под DOS и 16-битных приложений.

Догадываюсь, что современным программистам тяжело воспринимать реалии разработки тех лет и технические ограничения софта из далекого прошлого, поэтому в качестве иллюстрации, покажу таблицу моделей памяти создаваемых приложений:

Сравните это с современными "запускаемыми архивами" с играми, по 100Гб в одном .EXE
Сравните это с современными "запускаемыми архивами" с играми, по 100Гб в одном .EXE

Это предельные размеры создаваемого приложения, в случае MS-DOS 3.20 мы будем использовать «s» модель памяти, поэтому размер финальной сборки не должен превышать 64Кб.

Сборка и запуск

Компиляторы C и C++ от DigitalMars изначально были коммерческим продуктом и до сих пор существует коммерческая версия, включающая помимо компиляторов еще и собственную среду разработки (IDDE). Однако не так давно компиляторы стали бесплатным и открытым проектом, с исходниками на Github.

К сожалению проект пока не портируемый — собирается и работает исключительно под Windows.

Тем не менее, поскольку создавали его очень опытные люди — все отлично работает в Wine, что мы и будем использовать для запуска на FreeBSD.

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

Инсталлятора тут нет, в архиве сразу готовые бинарники, поэтому распаковываем и запускаем:

mkdir -p /opt/src/digitalmars/dm
cd opt/src/digitalmars/dm
wget http://ftp.digitalmars.com/Digital_Mars_C++/Patch/dm857c.zip
unzip dm857c.zip
wineconsole bin/dmc.exe

Так выглядит отображаемая версия компилятора:

Для поддержки сборки под DOS нужно скачать и распаковать дополнительный пакет:

cd opt/src/digitalmars
wget http://ftp.digitalmars.com/Digital_Mars_C++/Patch/dm850dos.zip
unzip dm850dos.zip

Внутри будут дополнительные бинарники, главный из которых это довольно известный EXE2BIN.COM, используемый для генерации COM-файлов из EXE.

С EXE2BIN есть один важный нюанс — он не запустится с помощью Wine, поскольку является DOS-приложением.

Чтобы использовать эту утилиту для создания COM-файлов — необходимо запускать ее с помощью DOSbox:

Теперь переходим к основному эмулятору.

Официальный логотип 86Box
Официальный логотип 86Box

86Box

Достаточно новый проект (разрабатывается с 2016 года), который сейчас активно развивается и позволяет эмулировать огромное множество x86-совместимых систем:

86Box is an IBM PC system emulator that specializes in running old operating systems and software designed for IBM PC systems and compatibles from 1981 through fairly recent system designs based on the PCI bus.

Готовой сборки для FreeBSD пока нет (как и присутствия в портах) и еще год назад были проблемы, требующие ручных правок исходников:

Initially exclusive to Windows, it was ported to Linux in version 3.2[2][3] and macOS in version 3.4.[4]

Но на момент написания статьи все поправлено и эмулятор отлично собирается из исходников без каких-либо проблем, а в инструкции по сборке появился раздел про FreeBSD.

Сборка

Забираем исходники:

git clone --depth 1 https://github.com/86Box/86Box.git

Репозиторий довольно большой, поэтому стоит использовать ограничение на историю выгружаемых коммитов (ключ --depth 1).

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

Сборка построена на cmake, но также использует ninja (для модулей).

Следующие пакеты должны быть установлены в системе для того чтобы сборка отработала:

 pkg install cmake pkgconf freetype-gl sdl2 libspng openal-soft rtmidi qt5 libslirp fluidsynth libsndfile

Запуск вполне классический для cmake-проектов:

mkdir build
cd build
cmake ..

Готовое для запуска приложение эмулятора будет в build/src/86Box.

Но это еще не конец.

Для работы нужен набор ROM-файлов (да, опять), который для текущей версии (из master-ветки) необходимо загружать из отдельного репозитория:

git clone --depth 1 https://github.com/86Box/roms.git

Для релизных версий эмулятора — ROM-файлы надо брать в другом месте, со страницы releases.

Запускается эмулятор с указанием путей к каталогу с ROM-файлами следующим образом:

./src/86Box --rompath /opt/src/roms

После запуска эмулятора (по прямой аналогии с каким-нибудь VirtualBox) необходимо создать новую виртуальную машину, задав ей вот такие настройки:

Это не единственный возможный сетап эмулируемого оборудования, но максимально приближенный к реалиям 1986 года.

Так выглядела обложка коробочной версии с MS-DOS. Помимо префикса, слово "Microsoft" встречается трижды ;)
Так выглядела обложка коробочной версии с MS-DOS. Помимо префикса, слово "Microsoft" встречается трижды ;)

MS-DOS

Образы самого MS-DOS достать в сети очень легко, например отсюда. Все они уже давно находятся в публичном пользовании (public domain), поэтому за их использование Microsoft вас не съест засудит.

Более того, даже исходный код некоторых версий MS-DOS был выложен в публичный доступ:

The original sources of MS-DOS 1.25, 2.0, and 4.0 for reference purposes

Коробочная версия поставлялась на 5-дюймовых дискетах, размером в 360Кб, собственно их образы и выложены в сети.

Нам нужен лишь первый:

Этот файл нужно выбрать в меню после запуска эмулятора:

После чего перезагрузить эмулируемую систему.

При запуске пойдет проверка памяти, затем MS-DOS будет просить ввести ей дату и время, достаточно нажать пару раз <Enter> для пропуска:

MS-DOS из 1986 года готов к работе.
MS-DOS из 1986 года готов к работе.

Mtools

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

с помощью генерации образа floppy-диска на хосте и подключения его на эмулируемой системе, в виртуальный floppy-привод.

Тот самый, с которого мы выше загружали образ MS-DOS.

Для генерации образов флоппи я использовал известный пакет GNU Mtools,

In addition to file access, it supports many FAT-specific features: volume labels, FAT-specific file attributes (hidden, system, ...), "bad block" map maintenance, access to remote floppy drives, Iomega ZIP disk protection, "secure" erase, display of file's on-disk layout, etc.

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

pkg install mtools
Так выглядят те самые пятидюймовые дискеты (5.25").
Так выглядят те самые пятидюймовые дискеты (5.25").

Работа с образом дискеты

Создаем пустой образ дискеты на 360кб:

mformat -C -t 40 -h 2 -n 9 -i floppy360k.img

Копируем файл с хоста внутрь этого образа:

mcopy -i floppy360k.img /opt/src/digitalmars/dm/bin/test.exe ::test.exe

Запускаем эмулятор 86box и указываем созданный образ в качестве второго в эмулируемой системе:

Переходим на диск B:

Обратите внимание на даты на файлах.
Обратите внимание на даты на файлах.

Тесты

А теперь будут танцы:

показываю функционал 21 века, работающий под MS-DOS из 20 века.

Напоминаю, что система в эмулятора была выпущена 40 лет тому назад — возможно старше вашего папы.

Первым делом я попробовал что-то собрать с RTTI и классами, вот такой код:

#include <iostream.h>

class Base {
    public:    
	virtual void test() {
        	cout << "this is base" << endl;	
    	}
};

class Derived : public Base {
	public:    
		void test()    {
			Base::test();        		
			cout << "this is derived" << endl;
   		} 
};

int main()
{

Base* b = new Derived();
Derived* d = dynamic_cast<Derived*>(b); 
if (d != NULL) {
    // Successful cast
    cout << "works" << endl;;
	d -> test();
} else {
    // Cast failed
    cout << "cannot cast B* to D*" << endl;;
}
    return 0;
}

Сборка осуществляется с помощью wineconsole:

dmc.exe -0 -ms -Ar -NV -o+space  ..\..\test.cpp

Дальше добавление полученного EXE-файла в образ дискеты:

mcopy -i floppy360k.img /opt/src/digitalmars/dm/bin/test.exe ::test.exe

Как видите все отлично работает в операционной системе 40-летней давности:

Покажите вашему преподу по C++.
Покажите вашему преподу по C++.

Следующим шагом проверил обработку ошибок:

#include <iostream.h>
#include <exception>

int main() {
	try {
  		int age = 15;
  		if (age >= 18) {
    			cout << "Access granted - you are old enough.";
  		} else {
    			throw 505;
  		}
	} catch (int myNum) {
  			cout << "Access denied - You must be at least 18 years old.\n";
  			cout << "Error number: " << myNum;
	}
    	return 0;
}

Тут стоит уточнить, что поддержку обработки ошибок надо включать отдельно, ключом -Ae:

dmc -0 -ms -Ae -NV -o+space ex.cpp

Не поверите, но оно тоже работает:

Вот такие дела, конструкция try-catch на языке высокого уровня спокойно работает в MS-DOS образца 1986 года.

Еще больше балета

Разумеется я показал лишь малую часть возможностей этого компилятора, в первую очередь потому что уже декабрь а статья опять превратилась в простыню не успел закончить историю с STL:

STLport 4.5.3 ported to Digital Mars C++ (requires Digital Mars C++ 8.32 or later)

Это отдельная, весьма своеобразная версия STL, совместимая с DigitalMars, доступная для скачивания в собранном виде (плюс исходники).

Но собрана она для целевой платформы Win32, поэтому для сборки под DOS ее готовую версию использовать не получится, надо пересобирать самостоятельно.

Сама возможность такой пересборки под DOS существует, но необходима либо платная версия DigitalMars, которая содержит собственную систему сборки (smake), либо очередной «кровавый патчинг», чтобы собрать STLport вручную.

Не поверите, но еще существует Boost (хоть и очень старый) с поддержкой компилятора DigitalMars:

Boost 1.30.0 for Digital Mars C++

Вот уж где раздолье и настоящий угар: писать под DOS c помощью самой жирной библиотеки для C++ на свете!

Чтобы окончательно добить вашу психику и чувство прекрасного, показываю несколько проектов «обратного портирования» STL — как раз для особых случаев, вроде истории с DigitalMars.

Первый проект:

A recreation of much of the modern C++ standard library in ISO C++98

https://github.com/DryPerspective/Cpp98_Library

Что именно из SDL реализовано и примеры использования есть в Wiki проекта. Собирать с помощью dmc попробовать не успел, но выглядит многообещающе.

Второй проект:

A backport of C++11/14/17 features to earlier versions

https://github.com/chaossky/cppbackport

Тут основная цель это C++11, который DigitalMars поддерживает очень слабо, плюс будут проблемы с Windows-платформой:

Does it work on Windows? Sorry, POSIX mostly. Would love for some Windows devs to help.

Но проект сам по себе весьма интересный и ничего не мешает надергать отсюда необходимых кусков в свою реализацию.

Возвращение на Землю

Несмотря на описанное выше, дела с компиляторами C/C++ от DigitalMars обстоят несколько печальней чем кажется.

Во-первых этот компилятор очень сильно устарел и совместимость со стандартом языка осталась на уровне C++98.

Для более новых версий стандарта есть поддержка лишь отдельных фич и планов по улучшению ситуации я не видел.

Фактически активная разработка остановилась в 2004м году, с тех пор выпускаются лишь мелкие патчи и обновления, поскольку фокус авторов сместился на поддержку языка D.

Во-вторых этот компилятор по прежнему имеет серьезные проблемы с лицензированием:

несмотря на открытие исходного кода, для полноценной сборки требуется закрытый инструментарий.

Так что использовать DigitalMars без цели угара для обычных проектов наверное мало осмыслено, хотя и привнесет определенную толику веселья в тяжелые будни C++ разработки.

Добавлю, что компилятор для наших краев довольно редкий, по крайней мере лично я не встречал проектов, где бы он использовался.

Но для целей кросс‑компиляции под DOS (если вас жизнь заставит) — инструмент более чем интересный.

P.S.

Оригинал как обычно в нашем блоге, процесс всех этих изысканий и экспериментов выкладываем в нашем Телеграм канале.

В качестве промо к новой части «отбитых приключений»:

Кросс-компиляция на 8-битный Apple II и ProDOS.
Кросс-компиляция на 8-битный Apple II и ProDOS.