В начале 1990-х самым популярным расширителем DOS был DOS/4GW. Во время разработки Windows 95 я очень много времени тратил на решение задачи совместимости с играми под MS-DOS, поэтому видел много баннеров расширителей DOS, и чаще всего это был DOS/4GW.
Вы можете задаться вопросом: «Как эти игры вообще запускались в Windows 95, если они поставлялись с расширителем DOS? Разве расширитель не пытался бы безуспешно перейти в защищённый режим, потому что Windows уже управляла защищённым режимом?»
Хитрость заключалась в том, что эти расширители на самом деле были двумя связанными друг с другом программами. Одна использовалась как сервер защищённого режима, а другая была клиентской библиотекой защищённого режима.
В начале существовал Virtual Control Program Interface (VCPI), который поддерживался менеджерами расширенной памяти наподобие EMM386. Эти менеджеры расширенной памяти практически не использовали защищённый режим: единственное, что им было важно — получение доступа к памяти выше границы в 1МБ, чтобы они могли создать таблицы страниц для отображения дополненной памяти (extended memory) в кадр страницы расширенной памяти (expanded memory), но никакой другой виртуализации они не выполняли. MS-DOS работала в виртуальной машине с полным доступом к оборудованию, а интерфейс VCPI позволял приложению MS-DOS сказать: «Сейчас мне нужно получить полный контроль над системой», после чего VCPI отвечал: «Конечно, без проблем!»
Интерфейс VCPI быстро потерял свою популярность, потому что ни одна операционная система защищённого режима (например, Windows 3.0 в расширенном режиме) не позволила бы какой-то программе получать полный контроль над системой. По сути, это бы приостанавливало работу старой операционной системы, чтобы позволить программе MS-DOS перехватить управление как новой. В Windows 3.0 появился новый интерфейс под названием DOS Protected Mode Interface (DPMI), позволявший программам MS-DOS запрашивать исполнение их кода в защищённом режиме, но только в пользовательском режиме. Драйвер доступа DPMI при этом продолжал управлять режимом ядра.
Итак, вернёмся к DOS/4GW. При запуске расширитель DOS/4GW искал запущенный DPMI-сервер. Если он не находил его, то устанавливался сам в качестве DPMI-сервера. Но если DPMI-сервер уже работал, то он позволял этому DPMI-серверу оставаться в работе.
Игра общалась только с частью библиотеки, относящейся к DPMI-клиенту, которую использовала для перехода в 32-битный режим, распределения памяти и выполнения всех 32-битных задач, которые требовались этой 32-битной игре от 32 битов.
Иными словами, получается следующая блок-схема:
И DPMI-серверы, и Windows, и DOS/4GW реализуют интерфейс DPMI, поэтому DPMI-клиент DOS/4GW использовал для общения с обоими серверами стандартные вызовы DPMI.
Это прекрасно обеспечивало совместимость приложений, потому что в случае возникновения проблем в общении клиента DOS/4GW с сервером DOS/4GW нам достаточно было устранить их один раз и сразу для всех игр. С другой стороны, если проблему нельзя было устранить, это бы сломало множество игр.
Высокие риски, большая награда.
Чудесным образом большинство игр просто работало, несмотря на то, что они запускались не под тем DPMI-сервером, для которого разрабатывались изначально. Иногда возникали проблемы с отдельными играми. Самыми частыми оказывались проблемы с играми, предполагавшими, что вся память была физической и с играми, предполагавшими, что флаг прерываний был невиртуализированным, однако по большей мере всё работало достаточно хорошо, чтобы оставшиеся проблемы можно было решать как баги конкретных приложений.