О разработке 16-битных игр для реального режима процессора x86 и операционной системы DOS
Всем добрый день.
Прошлый мой пост про обновление 486DX2для старых игр собрал довольно много плюсов, из чего делаю вывод, что здесь немало ретробояр, интересующихся данной тематикой. Так получилось, что у меня написана неплохая статья по ретрокодингу для 16-битного реального режима x86(с примерами и готовыми прототипами игр), которую могу опубликовать по своему усмотрению, ибо заказчику она не пригодилась. Возможно, кому-то будет интересно, или даже полезно.
Итак, статья. В ней речь о том, как можно разрабатывать в настоящее время минималистские 16-битные игры для реального режима процессора x86,ограничиваясь только базовыми функциями DOS и BIOS, с использованием так называемых кросс-компиляторов. Статья предназначена для людей,хотя бы немного знакомых с программированием под DOS или для тех, кто интересуется ретротематикой и хотел бы узнать, как создаются подобные приложения, если не с нуля, то хотя бы на простом примере.
Для начала - естественные ограничения реального режима процессора:
- процессор адресует только 1Мб памяти
- модель памяти — сегментная (адрес памяти— это пара «сегмент: смещение»)
Соответственно,в рамках поставленной задачи мы не будем использовать расширители памяти вродеDOS4GW или CWSDPMI, а также при создании ассемблерного кода и вставок, избегать использования32-битных регистров, ограничиваясь16-битными регистрами.
Наши возможности для разработки:
1)Набор прерываний DOS и BIOS для работы с экраном и клавиатурой
2)640 Кб оперативной памяти (на практике меньше), разбитые на сегменты
3)Создание приложения в формате EXE (модели памяти Small/Medium) или COM (модель памяти Tiny)
4)Использование системного динамика для создания звуковых эффектов
5)Встроенные видеорежимы BIOS, а также стандартный текстовый режим, который загружается при старте DOS или DOSBox
6)Наконец, использование системного таймера, работающего на частоте 18.25 Гц,применение которого позволяет сделать программы независящими от частоты процессора.
Зачемэто может понадобиться?
Первый очевидный ответ — забавы ради. Некоторые из нас начинали разрабатывать программы и игры ещё под MS-DOS, всегда приятно вспомнить былое.
Второе— тренировка и очистка разума. Легкора ботать, когда в твоём распоряжении 64 гигабайта оперативки, 16 ядер процессора,независимая звуковая система и возможность грузить текстуры в FullHD. Когда доступна видеопамять 320 на 200 пикселей,оперативная память заканчивается на десятой текстуре, звук нужно извлекать путем включения и выключения динамика на заданной частоте, а события отслеживаются по таймеру на два десятка герц — задача становится немного иной. Постоянно так жить невозможно, но изредка — может даже оказаться полезным.
Ну и наконец, эстетическая компонента.Игры, созданные с использованием технологий тридцатилетней давности —выглядят по-особому. Да, в настоящее время существуют движки, которые позволяют воссоздать этот вид на современных средах разработки, но оригинальные методы всё ещё имеют ценность, на любителя.
Теперь о кросс-компиляторах. В чем сложность классического программирования непосредственно для таких систем в «железе» или эмуляторе? Просто очень неудобно работать. У тебя в лучшем случае 640 Кб памяти для запуска IDE или набора текста, и еще нужно запускать саму игру, которая тоже занимает память. В случае работы на железе — на это железо нужно как-то перекидывать данные, равно как и сохранять с него. В случае использования эмуляторов— тот же DOSBox может работать с каталогами хост-машины, монтируя их внутри себя, но делает это не очень хорошо, из-за чего бывает рассинхронизация файлов (справедливости ради, DOSBox предназначен для запуска игр, и делает это отлично, а остальные способы его использования — уже как получится).
Наконец,все прекрасные современные средства разработки, включая системы контроля версий и редакторы с подсветкой синтаксиса, поиском и дополнением ввода — доступны только на современных ОС. Работать без мультизадачности в текстовом окне 80 на25 – это ностальгично, но очень грустно.
Здесь есть два решения:
1)Использование кросс-компиляторов —это компиляторы, которые умеют генерировать код для 16-битного реального режима процессора (с сегментами, ближними и дальними указателями, несколько моделей памяти), при этом сами запускаются на современной ОС (Windows, Linux,MacOS). В этом случае, мы имеет всю мощь современной станции разработки, и на выходе получаем файл в формате COM или EXE, который уже можно запустить в эмуляторе или на железе.
2)Использование систем сборки, которые позволяют запустить 16-битную среду выполнения внутри себя, например,используя DOSBox внутри образа для Docker, и опять же на выходе получить готовый файл. В случае DOSBox можно обойтись без контейнеров, используя просто настроенный autoexec, который будет при старте монтировать диск,запускать компилятор, создавать исполнимый файл и отключаться.
В данной статье почти вся информация касается именно первого случая, за исключением последней программы, где вместо кросс-компилятора используется нативный компилятор для DOS, и там разработка велась внутри эмулятора.
Последний немаловажный фактор — лицензионная чистота используемых средств. Я стараюсь максимально ограничивать в своей работе,даже на уровне некоммерческой разработки,тех продуктов, которые не были разрешены к свободному использованию или не были мною приобретены. Таким образом, при выборе будем стараться использовать те средства разработки, для которых явно указана возможность их применения в некоммерческих личных целях, или которые как минимум являются свободными к распространению по модели «заплати,если понравилось».
Для тестирования и разработки были использованы эмуляторы DOSBox и 86Box, а также рабочие компьютеры с процессорами 386 и 486, но с минимизацией обращений к специальным возможностям этих процессоров.
Важно отметить, что ни один из проектов,представленных ниже, не является ни полноценной завершенной игрой, ни даже примером хорошего, качественного кода.Любители игр заметят, что процесс прохождения отсутствует, можно либо бесконечно играть, либо проиграть, а также слабый баланс самого процесса.Опытные разработчики — найдут слабые места в игре на Си, чрезмерное использование lodsb вместо адресации для игры на ассемблере, неважную структуру кода на Бейсике, и не вполне чистую архитектуру кода для FreePascal.
Также внимательные читатели могут заметить,что некоторые фрагменты ассемблерных вставок повторяются из проекта в проект и было бы замечательно оформить их в библиотеку, чего сейчас нет.
Эти проекты, за исключением, возможно, игры«Водопроводчик» - исключительно демонстрация концепции, основа, которая показывает, как реализуется код, игра и что нужно для её сборки.
Итак,приступим.
Наш номер один — кросс-компилятор FreePascalCompiler, позволяющий создавать полноценные приложения для реального режима процессора, включая COM-файлы. Для линковки файлов, ему нужен установленный линкер OpenWatcom, но это небольшой минус, не мешающий работе.
Сайт компилятора: https://www.freepascal.org/
Руководство по использованию кросс-компилятора:https://wiki.freepascal.org/DOS
В данном компиляторе даже в режиме кросс-компиляции доступна вся мощь современного ObjectPascal, включая полноценные классы, длинные строки,исключения, обобщенные типы и даже интерфейсы.
ВАЖНО:FreePascal имеет как кросс-компилятор для реального режима,так и непосредственно среду разработки и компилятор для DOS, но с использованием защищенного режима и расширителя памяти GO32V2. Они похожи, номы используем именно кросс-компилятор,который позволяет создавать приложения без расширителя памяти.
С использованием данного кросс-компилятора,когда-то был разработан проект игры«Водопроводчик» для конкурса 16-битныхCOM-игр.
Страница проекта: https://tav-developer.itch.io/water-way-game-16-bitРепозиторий проекта: https://github.com/tereshenkovav/WaterWayGame16bit
Возможности и используемые технологии:
- Полностью объектно-ориентированная программа с классами и архитектурой.
-Видеорежим 13 (320 на 200 пикселей, палитра на 256 цветов)
-Ассемблерные вставки для реализации работы с видеопамятью, звуком, клавиатурой и таймером
-Звук на основе динамика без блокировки игры
-Сборка бинарника как COM
Вывод графических примитивов полностью реализован на ассемблерных вставках,с их помощью также опрашивается клавиатура и обрабатывается системный таймер,чтобы скорость игры не зависела от частоты процессора. С целью уменьшения размера исполняемого кода, программа не использует ни один модуль из RTL,кроме System.
Команда сборки исходников под Windows выглядит так:
ppcross8086-Tmsdos -WmTiny -Wtcom -Mobjfpc -Rintel -FE../bin -FUlib/i286-dos16-FuC:\fpc\3.0.4-DOS\units\msdos\80286-tiny\rtl -CX -XX -Sg../src/WaterWay.ppгде мы указываем целевую платформу, модель памяти, формат выходного файла, синтаксис ассемблера, и пути к библиотекам кросс-компилятора, а также каталоги для объектных файлов и выходного файла.
Проект проверен на компьютерах 386, 486, эмуляторах DOSBox и 86Box.
Наш номер два — Digital Mars C/C++ Compiler,позволяющий создавать полноценные приложения для реального режима процессора, включая COM-файлы. Поддерживает как чистый C++, так и С++ с классами и стандартной библиотекой.Как и предыдущий компилятор, позволяет ассемблерные вставки.
Для работы кросс-компилятора нужно сначала скачать сам компилятор для Win32,а потом библиотеки для DOS.
Сайт компилятора: https://www.digitalmars.com/
С использованием данного кросс-компилятора,портировал одну свою игру-прототип,реализованную исходно на GCC для советского компьютера БК-0010.Страницы и репозитория у проекта пока нет, потому что игра носит демонстрационный,а не завершенный характер. Суть игры —уклонение от набегающих монстров,собирание бонусов очков, скорости и защиты от врагов, цель — продержаться как можно дольше, набирая очки за бонусы и уничтожение врагов в режиме защиты.
Ссылка на загрузку бинарника и исходников проекта: https://disk.yandex.ru/d/gRcduK1XuWnBng
Возможности и используемые технологии:
- Используем чистый C код без объектов и стандартной библиотеки(почти).
- Видеорежим 13 (320 на 200 пикселей, палитра на 256 цветов), но работаем только с выводом псевдографики
-Ассемблерные вставки для реализации работы с видеопамятью, звуком, клавиатурой и таймером
-Перехват прерывания 09 для обработки удержания клавиш, а не только нажатия(для этого используем функции работы с прерываниями из библиотеки компилятора)
-Звук на основе динамика с блокировкой игры
-Сборка бинарника как EXE с моделью памяти Medium
Команда сборки исходников под Windows выглядит так:
dmc.exe -mmd survive.c gameapi.cгде в командной строке указываем собирать проект для DOS с моделью памяти Medium.
Проект проверен на компьютерах 386, 486, эмуляторах DOSBox и 86Box. В данной реализации при запуске везде, кроме DOSBox – перехват клавиатуры работает не полностью корректно, скорее всего, не возвращая управление предыдущему обработчику,отчего переполняется буфер. Починить это можно путем реализации собственных функций сохранения и установки прерываний,не полагаясь на системную библиотеку.
Номер три — это The Netwide Assembler (NASM),компилятор ассемблера, который позволяет собирать программы для реального режима процессора.
Это тоже полноценный кросс-компилятор,включен в статью потому, что нужно затронуть максимум возможных языков программирования из доступных по условиям задачи.
Сайт компилятора: https://nasm.us/
С использованием данного кросс-компилятора,разработал очень примитивную, но всё же работающую игру, в жанре стрелялки из пушки по пролетающим объектам разных типов. В качестве изюминки, пушка имеет три ствола, а также вся реализация выполнена в псевдографике, с использованием драйвера ANSI.SYS, практически без использования прерываний BIOS(исключение — установка позиции курсора и конечно, работа с таймером).Страницы и репозитория у проекта, как и у предыдущего, пока нет, потому что игра аналогично носит демонстрационный,а не завершенный характер (нет условия победы или поражения, только подсчет удачных выстрелов).
Ссылка на загрузку бинарника и исходников проекта: https://disk.yandex.ru/d/6BFitvsS0VUv7g
Возможност ии используемые технологии:
-Используем ассемблер, без дополнительных библиотек и макросов
-Текстовый режим 3 (80 на 25 символов, 16цветов)
-Для установки цвета и частично позиций вывода используем драйвер ANSI.SYS
-Вывод строк через прерывание 21DOS
-Таймер для контроля частоты обновления игры
-Сборка бинарника как COM с моделью памяти Tiny
Команда сборки исходников под Windows выглядит так:
nasm.exe -o rocket16.com rocket16.asmгде в командной строке указываем собирать проект как COM
Проект проверен на компьютерах 386, 486, эмуляторах DOSBox и 86Box. При запуске везде, кроме DOSBox,требуется установленный драйвер ANSI.SYS. DOSBox же его эмулирует по умолчанию.
Итак,обзор практически завершен. Мы использовали Паскаль, Си, Ассемблер,всё на основе кросс-компиляторов,свободных к загрузке и применению. Игры работают, хотя по уму, все, кроме Водопроводчика, нужно улучшать.
Чего не хватает? Не хватает Бейсика. Это один из моих любимых языков программирования,на нём можно быстро и легко писать игры,а если использовать ассемблерные вставки, то они даже буду хорошо работать.Но здесь меня ожидало расстройство —для моих целей, компиляторов Бейсика не нашлось. Есть FreeBasic, он хорош и позволяет создавать бинарники для DOS, но использует расширитель памяти. Есть QuickBasic,но это, во-первых, не кросс-компилятор,а во-вторых, не является свободным для использования (его не продают в настоящее время, но и объявлений о допустимости применения в личных некоммерческих целях не было).
И тем не менее, для полноты обзора, я нашел компилятор, который удовлетворяет хотя бы одному условию из двух. Это Its AlmostBasic – ASIC, компилятор диалекта Бейсика, распространяемый как Shareware-софт. Сайта разработчика я не нашел, но он включен в репозиторий FreeDOS:
https://clasqm.github.io/freedos-repo/zip/asic.zip
Компилятор,к сожалению, не такой мощный, какQuickBasic, на исходные кодына ложено множество ограничений, в частности, невозможно использовать логические операции в условиях, нельзя делать вычисления сложнее одной операции справа от присваивания, нет структур и динамических массивов и еще множество мелких неудобств. С другой стороны, о нвключает в себя мощную библиотеку для работы с графикой и позволяет линковать ассемблерные модули OBJ, что в сочетании с ассемблером NASM позволит его существенно расширить.Намного лучше, чем ничего. Так же, как и прочие компиляторы, позволяет создавать исполнимые файлы COM и EXE
На основе этого компилятора, я создал небольшую игру-прототип в жанре горизонтальной стрелялки, с прокруткой мира, спавном врагов и перестрелками.Несмотря на сложности с реализацией,это, пожалуй, лучший прототип среди прочих (но всё ещё недостаточно хорош для выгрузки в отдельный репозиторий,ибо игра всё ещё не позволяет выиграть,только набить очки и проиграть).
Ссылка на загрузку бинарника и исходников проекта:
https://disk.yandex.ru/d/EXkHmVKUgkqSKg
Возможности и используемые технологии:
- Используем ассемблерный модуль только для быстрой процедуры очистки экрана
-Графический режим 7 (320 на 200 пикселей,16 цветов, две экранные страницы)
-Для рисования графических примитивов используем средства библиотеки самого компилятора
-Используем таймер для контроля частоты обновления игры
-Рисуем на одной экранной странице,выводим на другую, избегая неприятных эффектов перерисовки
-Сборка бинарника как EXE с моделью памяти Small
Исходники собираются под DOS или DOSBox. Команда сборки исходников: сначала собираем ассемблерный модуль, указывая,что нужно получить OBJ-файл:
nasm.exe -f obj helpers.asmПотом самим компилятором, указывая ему формат бинарной сборки, поддержку длинных целых, библиотеку ASI5LIB для работы с графикой и ранее собранный ассемблерный модуль HELPERS
ASICC GAME.ASI B/OBJ E LIB=ASI5LIB OBJ=HELPERS LNK=C:\ASICПроект проверен на компьютерах 386, 486, эмуляторах DOSBox и 86Box. Работает хорошо,проект почти без недочетов, за исключением, разумеется, незавершенности именно как игры и ограничений кода,вызванных особенностями диалекта Бейсика.
На этом всё. Для удобства тех, кто просто хочет опробовать прототипы игр, без сборки исходников и настройки эмуляторов,я сделал отдельный архив, в который включил настроенный DOSBox и все исполняемые файлы из статьи. Запускать файл DOSBox.exe, потом вводить команду game1, game2, game3 или game4, для запуска конкретной игры из статьи.
Ссылка на загрузку: https://disk.yandex.ru/d/dorde8pRZbn5lQ
PS: По завершении статьи, а особенно примеров к ней, я решил, что целесообразно делиться подобными наработками на регулярной основе, но для тех, кто этого желает. Так что вот вам новый авторский канал по тематике ретрокодинга, кого это интересует — подписываемся. Часто постить не обещаю, но раз в неделю-две точно полезная информация будет поступать.t.me/gamedev16bit