Причуды обратной совместимости
Вряд ли кто-нибудь слышал об операционной системе DOS-777. А такая реально существовала. Правда, в действительности это была самая обычная MS DOS с незначительными изменениями, предназначенная для работы на единственном компьютере и выполнения одной единственной программы. Своим появлением этот клон в некотором роде обязан проблеме обратной совместимости. Но обо всем по порядку.
Новая и важная программа вдруг повела себя совершенно загадочным образом. Это настолько взбесило ее авторов, что было решено дисассемблировать MS DOS, разобраться в нюансах ее работы и найти причины странных ошибок. Сейчас, даже в запальчивости, вряд ли кто-то решится дисассемблировать, например, Windows-10 и разобраться во всех ее особенностях. Но тогда, в начале 90-х, когда ОС представляла собой три сравнительно небольших файла, их дисассемблирование и анализ заняли недели две.
Было обнаружено множество мелких особенностей, которые вряд ли кому-то сейчас интересны. Но в те времена по результатам этого анализа можно было даже заполнить недостающими названиями поля внутренних таблиц MS DOS, которые в одной из книжек серии «Библиотека системного программиста» под редакцией Фроловых (издательство «Диалог МИФИ») были обозначены как «зарезервированные».
Повторюсь, вряд ли все эти тонкости кому-то сейчас интересны, но о трех найденных нюансах, так или иначе связанных с проблемой обратной совместимости, пожалуй, стоит упомянуть.
Нюанс первый. Самые ранние «резидентные» программы, например, «SideKick», прямо по контексту команд внутри MS DOS искали адрес внутреннего флага «занятости» ОС. В следующих версиях этот контекст изменился, да и появилась документированная функция запроса флага, но несколько «старых» команд были специально перенесены в сегмент данных (на них управление не попадало), чтобы древние программы по-прежнему находили нужный контекст. На ум приходит аналогия со спуском нового корабля, получающего имя легендарного предшественника. Тогда по традиции из старого корпуса вырезается плита и крепится как мемориальная доска на новом корабле. Так вот, все версии MS DOS несли на борту маленький кусочек самой первой версии 1981 года.
Следующий нюанс был любопытнее. Оказалось, что перед запуском любого EXE-файла MS DOS проверяет среди его команд определенный контекст в определенном месте. Если он там находится — ОС слегка «раздвигает» соседние команды и добавляет команды PUSH CX и POP CX.
Поскольку в контексте присутствовала команда LOOP, очевидно, это исправление ляпа какого-то разини из MicroSoft, забывшего, что цикл портит этот регистр. Рекламный слоган: «в новой версии MS DOS даже старые программы работают лучше!» заиграл неожиданными красками. Мы не пробовали искать, какая поделка MicroSoft исправляется таким образом, зато были предложения написать свою программу с ассемблерной вставкой так, чтобы нужный код попал в нужное место. По мысли автора предложения, далее нужно всего лишь иметь зависимость от содержимого CX. Когда MS DOS вставит ненужную POP CX, поведение программы изменится, можно тащить MicroSoft в суд за вредоносные действия и трясти с нее миллионы. Но поскольку предлагавший это по судам никогда не ходил и адвокатов (тем более, американских) не нанимал, дальше трепа в курилке дело не пошло.
И, наконец, третий нюанс, который, как выяснилось, и был причиной ошибок. Это работа с двадцатой адресной линией. Для молодого поколения, которое, к счастью для себя, не сталкивалось с подобными вещами, вероятно, потребуется объяснение. Первоначальный вариант персонального компьютера с процессором 8086 имел соединение с оперативной памятью двадцатью адресными линиями. Сам адрес вычислялся в команде сложением двух частей: т.н. сегмента (16 старших бит из 20) и т.н. смещения (16 младших бит из 20). Таким образом, формально можно задать две части, сумма которых больше, чем два в двадцатой степени (максимального адреса). Но ничего страшного не происходило. Перенос из последней 19-ой линии (они нумеруются с нуля) просто пропадал и получался адрес в первых 64 килобайтах.
Когда примерно в 1986 году появились 32-х разрядные процессоры и компьютеры, число адресных линий увеличились до 30 (а не до 32, поскольку нулевая и первая линии стали внутренними). Короче, появилась нормальная двадцатая адресная линия и никакого «заворота» в младшие адреса уже не получалось.
Лично я никогда не видел в программах использования трюка с «заворотом». Если такие и были, это уж совсем какая-то седая древность и дурной стиль. Но в Америке неизвестно кто для совместимости с неизвестно чем, вдруг так озаботился надуманной проблемой, что для 20-ой линии в компьютеры была добавлена электрическая схема, позволяющая ее обнулять! Прямо так и говорилось: включить и выключить двадцатую линию.
А это было время, когда дурость одного деятеля, заявившего, что »640 Кбайт хватит на все задачи» становилась все яснее и яснее. Появились всякие памяти «expanded», а как раз из-за двадцатой линии появился 64-х килобайтный уютный «чуланчик» в памяти выше мегабайта, на который первой лапу наложила сама MicroSoft и поместила туда MS DOS, немного освобождая, тем самым, пресловутые 640 Кбайт.
При этом работа с двадцатой линией внутри MS DOS заставила вспоминать поговорку про дурака и писаную торбу. При обращении к функции DOS эта адресная линия, естественно, включалась самой ОС, чтобы получить управление выше мегабайта. А при выходе из функции DOS эта линия… Думаете, восстанавливала исходное состояние? Выключалась? Включалась? Мы тоже так думали и не угадали. Оказалось, что при первом обращении к DOS и возврате в программу двадцатая линия отключалась, а при последующих обращениях (т.е. обращениях из программы с тем же PSP) оставалась включенной. Это и сбило всех с толку и при отладке (а отладчик сам обращался к функциям DOS) осталось незамеченным. А поскольку важная программа требовала много памяти для команд и данных и сама располагалась выше мегабайта, то неожиданное отключение у нее одного бита в адресе приводило к самым разнообразным эффектам.
Выход был простой: при старте один раз обратиться к DOS, например, запросить номер версии, затем включить двадцатую линию и забыть о ней как о страшном сне.
Поэтому никакой специально исправленной DOS, как мы сначала опасались, создавать не потребовалось. Но поскольку было жаль уже потраченных двух недель, вместо штатной DOS все же был использован ее клон, который вообще не дергал эту чертову двадцатую линию и имел еще несколько мелких улучшений. Этот клон и получил название DOS-777 по номеру отдела, где все это происходило.
На мой взгляд, есть и еще худшее решение о совместимости, чем фокусы с двадцатой линией. Это регистры команд MMX, совмещенные с регистрами FPU. Я читал такое объяснение этому архитектурному решению: при переключении задач, ОС запоминает контекст остановленной задачи, в том числе регистры FPU. При использовании MMX, они автоматически также будут запоминаться и восстанавливаться.
По-моему, это решение не выдерживает никакой критики: к моменту, когда реально появились программы с MMX, сменилось несколько версий (чуть не поколений) ОС, например, той же Windows. Проще было объявить, что до такого-то номера версии одновременно несколько задач с MMX работать не будут. Вместо этого теперь на веки-вечные MMX и FPU мешают друг другу. И всего лишь из-за попытки совместимости с какими-то старыми Windows времен, когда еще не было MMX, и про которые все уже давно забыли.
Таким образом, проблема обратной совместимости объективно существует, и решать ее иногда приходится. Но если в первых двух приведенных примерах решение было остроумным, и главное, скрытым для пользователей, которым эта совместимость ни к чему, то в двух последних примерах попытка совместимости (на мой взгляд, надуманная!) привела к вычурным аппаратным решениям и породила проблемы. Причем совместимость уже тогда гораздо лучше и проще обеспечивалась специально разработанным режимом виртуального 8086.
Главной же бедой любой обратной совместимости является то, что с каждым годом пользователей, которым она нужна, все меньше и меньше. А учитывать ее приходится всем.