Марсианские страсти: DOS, любовь и С++
У нас было 640Кб памяти, CGA-экран, 20-мегабайтовый диск и целых четыре мегагерца тактовой частоты. А также старые пятидюймовые дискеты на 360кб. Не то чтобы это был необходимый набор для современного разработчика на C++, но если уж начал коллекционировать дичь, то сложно остановиться..
Проклятая выдача
Наверное все уже в курсе, что социальные сети и поисковые системы постоянно подкручивают ленту выдачи под каждого пользователя на основе предыдущих запросов и интересов.
На меня эти алгоритмы тоже начали влиять, но.. в особенном ключе:
формируя персональную ленту из самой отмороженной компьютерной дичи, которая только есть на свете.
Когда я уже не мог выдерживать накал выдаваемого проклятыми алгоритмами — начал про всю эту дичь писать.
На свет стали появляться статьи, испортившие безвозвратно психику, взгляды на жизнь и карьеру многим несчастным, случайно забредающим в этот бложег.
Так появилась и эта замечательная статья:
несмотря на кучу дел, предновогоднюю суету и горящие сроки, я просто бл#ть не могу остановиться и перестать доставлять.
Если серьезно, то конечно же этот материал появился не на пустом месте и не за один день, это снова результат довольно долгого изучения матчасти, ковыряния исходников и темных ритуалов из Некрономикрона.
Все ради того чтобы в очередной раз показать читателям невозможное, по мнению ИИ и обитателей StackOverflow.
Технический ультрахардкор
Коль уж читаете эту статью — скорее всего и так знаете через какое место как именно программы появляются на свет и какова роль компилятора в этом сложном процессе.
Для простых обывателей поясняю:
чтобы получить тот самыйsteam.exe, которым выжадными ручкамизапускаете любимые игоры, его необходимо собрать из исходников.
Сотрудник компании Valve вместо работы над Half-Life 3 запускает компилятор, который собирает из набора файлов с исходниками «финальный билд», который затем упаковывается в инсталлятор и выкладывается на официальный сайт Steam.
Откуда его потом скачивают ушастые юзеры.
внезапно настал зомби-апокалипсис, всех программистов, которые занимались разработкой игор в компании Valve сожрали зомби, после чего процесс сборки, документация и тулчейны были утеряны.
Все что осталось — несколько старых бекапов на забытых носителях, с трудом извлеченные из жопы зубов голодного зомби.
Спустя пару сотен лет, вы — представитель жалких остатков человечества, выживших на небольшой колонии с обратной стороны Луны, вновь ступаете на опустевшую Землю в поисках артефактов погибшей цивилизации.
И находите на развалинах бывшего офиса Valve ту самую флешку с исходниками альфа-версии Half-Life 3.
Конечно прогресс в разработке программ к тому времени успел уйти далеко вперед, у вас в ходу на лунной базе теперь какие-нибудь C++-157 и clang-777, с встроенным Rust, тремя слоями виртуализации и сборщиком мусора, работающим через промпты LLM.
А про язык C вы узнали лишь на лекции по истории, про исторический период где-то между египетскими пирамидами и вторжением инопланетян.
как же все-таки насладиться шедевром далеких предков, так и не увидевшим свет в свое время?
Многое можно отдать за запуск этой красоты, даже спустя пару сотен лет после зомби-апокалипсиса:
Несмотря на весь этот «апокалиптический угар», ситуация сама по себе — более чем реальная:
прямо сейчас эксплуатируются полностью виртуальные системы, собственных средств разработки для которых либо уже нет совсем, либо такая разработка чрезвычайно затруднена.
В первую очередь это мейнфреймы, которые до сих пор живее всех живых, вроде такого красавца от Unisys:
Если немного снизить накал технического фетишизма экстрима и обратить взор к современной коммерческой разработке — можно внезапно обнаружить, что фактически вся ее продуктовая часть (где создается приложение, устанавливаемое у конечного пользователя) работает с обязательной поддержкой устаревшего окружения:
поддержка Windows 7/10/11, поддержка старых версий 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++:
Комната жениха
Опишу тестовое окружение, которое будет использовано для столь задорной задачи.
К сожалению придется опустить ряд необходимых шагов и темных ритуалов ввиду требований РКН, просто для информации:
вызов демонов начинается примерно на 12 странице одной известной книги, но использовать стоит оригинальную версию а не перевод.
Три ключевых компонента, необходимых для осуществления задуманного злодейства:
- Компилятор 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]
«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
Язык кстати вполне живой и активно развивается, так выглядит пример кода на D:
#!/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-битных приложений.
Догадываюсь, что современным разработчикам тяжело воспринимать реалии разработки тех лет и технические ограничения для софта из далекого прошлого, поэтому в качестве иллюстрации, вот так выглядит таблица поддерживаемых форматов (моделей памяти) создаваемых приложений:
Эти размеры — предельные размеры создаваемого приложения, в случае 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
Достаточно новый проект (разрабатывается с 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
Запуск сборки вполне классический:
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
Образы самого 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> для пропуска:
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
Работа с образом дискеты
Создаем пустой образ дискеты на 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 и указываем созданный образ в качестве второго в эмулируемой системе:
Тесты
показываю функционал 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;
}dmc -0 -ms -Ar -NV -o+space ..\..\test.cpp
Дальше добавление полученного EXE-файла в образ дискеты:
mcopy -i floppy360k.img /opt/src/digitalmars/dm/bin/test.exe ::test.exe
Как видите все отлично работает в операционной системе 40-летней давности:
Следующим шагом я проверил обработку ошибок:
#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 ее использовать не получится, надо пересобирать.
Сама возможность такой сборки существует, но необходима либо платная версия DigitalMars, которая содержит их собственную систему сборки (smake), либо очередной кровавый патчинг, чтобы собрать версию STLport ручками под DOS.
Не поверите, существует Boost (хоть и очень старый) с поддержкой DigitalMars:
Boost 1.30.0 for Digital Mars C++
Вот уж где раздолье и настоящий угар: писать под ДОС c помощью самой жирной библиотеки для C++ на свете!
Чтобы окончательно добить вашу психику и чувство прекрасного, показываю несколько проектов «обратного портирования» STL — как раз для особых случаев, вроде истории с DigitalMars.
A recreation of much of the modern C++ standard library in ISO C++98
https://github.com/DryPerspective/Cpp98_Library
Что именно реализовано и примеры использования есть в Wiki проекта.
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 без цели угара для обычных проектов наверное мало осмысленно, хотя и весело.