Разработка 16-битной игры - особенности, инструменты, код с пояснениями
Картинка для привлечения внимания
По заявке пользователя Niko_de_Andjelo
публикую этот пост. Основные темы:
Пост в основном предназначен для разработчиков, хотя и люди, интересующиеся 16-битными играми, смогут найти полезные факты.
На чем и как вообще можно делать 16-битную игру в 2020-м году
В целом, так же и на том же, на чем и 25 лет назад. Родные компиляторы для MS-DOS, генерирующие настоящий 16-битный код и ассемблер для x86. В качестве машины для разработки, можно использовать как машину с DOS/FreeDOS, так и эмуляторы вроде DOSBox. Однако, большая часть перечисленных инструментов не являлись FreeWare, и их использования даже в личных некоммерческих целях — по-прежнему является пиратством. Кое-что всё-таки удалось найти, с возможностью легального использования.
Ниже приведены список инструментов, с указанием возможностей и лицензии.
TurboPascal 5.5 — недавно был отдан компанией-правообладателем в свободное пользование (но только для личных и некоммерческих целей), это хороший компилятор с IDE, поддерживает линковку объектных файлов (но не встроенный ассемблер) и элементы ООП. Для целей любительской разработки 16-битной игры — подходит.
Ссылка для статью: edn.embarcadero.com/article/20803
TurboC 2.01 — аналогично был отдан компанией-правообладателем в свободное пользование (но только для личных и некоммерческих целей), это хороший компилятор с IDE, поддерживает линковку объектных файлов (но не встроенный ассемблер) и элементы ООП. Для целей любительской разработки 16-битной игры — подходит.
Ссылка для статью: edn.embarcadero.com/article/20841
OpenWatcom — форк компилятора Watcom. IDE нет, но компилятор один из самых лучших, поддерживает как подключения объектных файлов, так и встроенный ассемблер, все возможности С и С++ (стандарт С99). Лицензия допускает некоммерческое использование частными лицами. Для целей любительской разработки 16-битной игры — подходит, но без IDE, работать чуть менее удобно.
Сайт: www.openwatcom.org/, а также есть неофициальные форки на open-watcom.github.io/
NASM — современный компилятор ассемблера, имеет среди прочих версий — 16-битную. Его можно применять для сборки ассемблерного кода совместно с теми компиляторами, которые сами не имеют встроенного ассемблера. Лицензия — BSD, так что можно спокойно использовать.
Сайт: nasm.us/
Наконец, последний в списке, в связи со спорностью ситуации — это QuickBasic 4.5. Из всех приведенных выше инструментов, он имеет наилучшую IDE со встроенным отладчиком и редактором, эффективную реализацию языка Бейсик со структурами, процедурами и динамическими массивами, отличная поддержка графики, возможность подключать объектные файлы (правда, собственным способом).
Засада в том, что у меня нет строгой уверенности насчет правомерности его использования. На сайте Microsoft информация о нем отсутствует. В общении с техподдержкой мне несколько раз отвечали, что для личного некоммерческого применения он допустим, но позже я нарыл, что он доступен для подписчиков MSDN, а она стоит сотни долларов.
Если у кого-то есть опция корпоративной поддержки Microsoft и он сможет задать этот вопрос в службу — я буду благодарен.
Информация: ru.wikipedia.org/wiki/QuickBASIC
Также стоит упомянуть наличие кросс-компиляции у Free Pascal Compiler 3.04 — он действительно генерирует 16-битный код из-под 32-битной системы, имея при том всю мощь современного ObjectPascal. Почитать можно тут wiki.lazarus.freepascal.org/DOS
Я сам еще не использовал плотно, потому рекомендовать или отвергать не стану, но выглядит очень удобно. Хотя размер выходного файла получается очень жирным для 16-битной системы, это небольшой минус.
Ограничения 16-битного режима
Что есть 16-битный режим? Это когда процессор работает в реальном режиме, адресует только 1Мб памяти, работает с сегментной моделью памяти (адрес памяти — это пара «сегмент: смещение»), и доступны только регистры размера «слово» (16бит).
Подробнее здесь
ru.wikipedia.org/wiki/%D0%A0%D0%B5%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D1%80%D0%B5%D0%B6%D0%B8%D0%BC
Сие означает, что нам нужно уместить код, графику и все элементы игры в 640Кб доступной прикладной программе памяти (реально нужно рассчитывать на 550-600 Кб, иначе могут начаться проблемы).
С точки зрения графики, мы не можем использовать в этом режиме возможности видеокарты для аппаратного ускорения, высокого разрешения и т.д. Всё, что нам доступно — это видеорежимы BIOS, лучшим из которых для игр является 13h. Это размер экрана 320x200 при палитре на 256 цветов (палитра RGB, по 6 бит на каждый компонент цвета).
Какие инструменты были использованы
Для реализации быстрых операций с памятью (спрайты и буфера) — ассемблер NASM
Для всего остального (загрузчики спрайтов, игровая логика, вывод графики) — компилятор QuickBasic 4.5
Реализация графической библиотеки
Мы хотим сделать библиотеку, которая позволит:
Зачем вообще нам применять ассемблер в таком случае? Только ради производительности. Частота таймера DOS — 18.25Гц, обновлять графику желательно с такой же частотой. А операции с массивами в QuickBasic (как и почти во всех языках высокого уровня) — очень медленные.
Пример: мы хотим заполнить буфер 10x10 зеленым цветом (код палитры 10).
Код на Бейсике:
Увы, данный код в процессе выполнения ровно сто раз вызывает внутреннюю функцию Бейсика, которая проверяет корректность индекса, устанавливает сегменты, сохраняет стек и прочее. Ничего этого нам не нужно. Потому напишем аналогичное решение на ассемблере (суть передачи данных, сохранение стека и возврат пока опустим, просто примем, что сегмент и смещение установлены на буфер)
Такая конструкция куда выгодней — для каждой записи, выполняются всего две команды — загрузка байта в память и проверка условия цикла. Можно записать короче:
если мы уверены, что размер буфера кратен двум байтам, то можно еще более ускорить, записывая по 2 байта (слово).
Теперь, если снабдить этот код нормальной инициализацией, передачей переменных в соответствии с принятыми соглашениями и описать процедуру на языке Бейсика — то можно сделать эффективную процедуру подготовки буфера.
Чтобы выводить буфер на экран, есть хороший подход — надо выделить буфер, копировать спрайты из буферов спрайтов в этот буфер, а потом одной командой вывести этот буфер на экран. По сути, это кустарный вариант реализации приёма «Двойная буферизация».
ru.wikipedia.org/wiki/%D0%94%D0%B2%D0%BE%D0%B9%D0%BD%D0%B0%D1%8F_%D0%B1%D1%83%D1%84%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F
Как же этого достичь, чтобы скорость была приемлемой? Аналогично — написать процедуру на ассемблере, которая будет копировать один буфер в другой (команда movsb копирует байт памяти из источника в приемник). Поскольку буфер назначения больше, чем буфер для спрайта, то нужно использовать построчное копирование (копируем байты в количестве ширины спрайта), а потом двигать указатель в буфере на ширину буфера минус ширина спрайта. Так мы быстро скопируем спрайт в буфер, сохранив геометрию. Остальные решения работают точно так же — например, при выводе спрайта с прозрачностью копируем байты, но проверяя при этом неравенство цвета условному цвету «прозрачный» (я использую цвет 255 для прозрачного).
Основные процедуры
В библиотеке реализованы основные процедуры:
Все эти процедуры имеют ассемблерную основу и декларативную обертку для Бейсика. Поскольку передать напрямую массив в ассемблерный код нельзя, мы передаем в процедуры сегменты и смещение буфера. Подробнее — в следующем разделе.
Примеры кода
Наконец, приведу полный пример кода для одной конкретной, частично описанной выше задачи — инициализация буфера спрайта заданного размера заданным цветом.
Сначала ассемблерный код. В данную процедуру передаются процедуры через стек.
Остальное достаточно просто. В NASM делаем объектный файл, в QuickBasic собираем из него модуль qlb для среды разработки и модуль lib для сборки exe-файла, и определяем процедуру на языке Бейсик:
DECLARE SUB setUpBufferAsm (BufAddr AS INTEGER, BufSeg AS INTEGER, BYVAL w AS INTEGER, BYVAL h AS INTEGER, BYVAL c AS INTEGER)
где первые две переменные — для передачи адреса, а указание BYVAL означает, что надо загнать в стек сами значения w,h,c вместо их адресов. Теперь можно создать программу, которая зальет буфер зеленым цветом и выведет его в заданной позиции.
Результат запуска
Вот и всё. Если кому понадобятся более подробные пояснения, примеры работ с другими процедурами (например, загрузка спрайта или вывод с прозрачностью), а также аналогичные постановки, но с другими языками (Pascal/C) — пишем в комментариях, покажу и расскажу.
По заявке пользователя Niko_de_Andjelo
А можно для нубов написать пост с историей разработки?
Ну, какая архитектура, показать, какой кусок кода что делает и т.д. Можно не очень подробно, просто, приобщить народ к Asm?
публикую этот пост. Основные темы:
- На чем и как вообще можно делать 16-битную игру в 2020-м году
- Ограничения 16-битного режима
- Какие инструменты были использованы
- Реализация графической библиотеки
- Основные процедуры
- Примеры кода
Пост в основном предназначен для разработчиков, хотя и люди, интересующиеся 16-битными играми, смогут найти полезные факты.
На чем и как вообще можно делать 16-битную игру в 2020-м году
В целом, так же и на том же, на чем и 25 лет назад. Родные компиляторы для MS-DOS, генерирующие настоящий 16-битный код и ассемблер для x86. В качестве машины для разработки, можно использовать как машину с DOS/FreeDOS, так и эмуляторы вроде DOSBox. Однако, большая часть перечисленных инструментов не являлись FreeWare, и их использования даже в личных некоммерческих целях — по-прежнему является пиратством. Кое-что всё-таки удалось найти, с возможностью легального использования.
Ниже приведены список инструментов, с указанием возможностей и лицензии.
TurboPascal 5.5 — недавно был отдан компанией-правообладателем в свободное пользование (но только для личных и некоммерческих целей), это хороший компилятор с IDE, поддерживает линковку объектных файлов (но не встроенный ассемблер) и элементы ООП. Для целей любительской разработки 16-битной игры — подходит.
Ссылка для статью: edn.embarcadero.com/article/20803
TurboC 2.01 — аналогично был отдан компанией-правообладателем в свободное пользование (но только для личных и некоммерческих целей), это хороший компилятор с IDE, поддерживает линковку объектных файлов (но не встроенный ассемблер) и элементы ООП. Для целей любительской разработки 16-битной игры — подходит.
Ссылка для статью: edn.embarcadero.com/article/20841
OpenWatcom — форк компилятора Watcom. IDE нет, но компилятор один из самых лучших, поддерживает как подключения объектных файлов, так и встроенный ассемблер, все возможности С и С++ (стандарт С99). Лицензия допускает некоммерческое использование частными лицами. Для целей любительской разработки 16-битной игры — подходит, но без IDE, работать чуть менее удобно.
Сайт: www.openwatcom.org/, а также есть неофициальные форки на open-watcom.github.io/
NASM — современный компилятор ассемблера, имеет среди прочих версий — 16-битную. Его можно применять для сборки ассемблерного кода совместно с теми компиляторами, которые сами не имеют встроенного ассемблера. Лицензия — BSD, так что можно спокойно использовать.
Сайт: nasm.us/
Наконец, последний в списке, в связи со спорностью ситуации — это QuickBasic 4.5. Из всех приведенных выше инструментов, он имеет наилучшую IDE со встроенным отладчиком и редактором, эффективную реализацию языка Бейсик со структурами, процедурами и динамическими массивами, отличная поддержка графики, возможность подключать объектные файлы (правда, собственным способом).
Засада в том, что у меня нет строгой уверенности насчет правомерности его использования. На сайте Microsoft информация о нем отсутствует. В общении с техподдержкой мне несколько раз отвечали, что для личного некоммерческого применения он допустим, но позже я нарыл, что он доступен для подписчиков MSDN, а она стоит сотни долларов.
Если у кого-то есть опция корпоративной поддержки Microsoft и он сможет задать этот вопрос в службу — я буду благодарен.
Информация: ru.wikipedia.org/wiki/QuickBASIC
Также стоит упомянуть наличие кросс-компиляции у Free Pascal Compiler 3.04 — он действительно генерирует 16-битный код из-под 32-битной системы, имея при том всю мощь современного ObjectPascal. Почитать можно тут wiki.lazarus.freepascal.org/DOS
Я сам еще не использовал плотно, потому рекомендовать или отвергать не стану, но выглядит очень удобно. Хотя размер выходного файла получается очень жирным для 16-битной системы, это небольшой минус.
Ограничения 16-битного режима
Что есть 16-битный режим? Это когда процессор работает в реальном режиме, адресует только 1Мб памяти, работает с сегментной моделью памяти (адрес памяти — это пара «сегмент: смещение»), и доступны только регистры размера «слово» (16бит).
Подробнее здесь
ru.wikipedia.org/wiki/%D0%A0%D0%B5%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D1%80%D0%B5%D0%B6%D0%B8%D0%BC
Сие означает, что нам нужно уместить код, графику и все элементы игры в 640Кб доступной прикладной программе памяти (реально нужно рассчитывать на 550-600 Кб, иначе могут начаться проблемы).
С точки зрения графики, мы не можем использовать в этом режиме возможности видеокарты для аппаратного ускорения, высокого разрешения и т.д. Всё, что нам доступно — это видеорежимы BIOS, лучшим из которых для игр является 13h. Это размер экрана 320x200 при палитре на 256 цветов (палитра RGB, по 6 бит на каждый компонент цвета).
Какие инструменты были использованы
Для реализации быстрых операций с памятью (спрайты и буфера) — ассемблер NASM
Для всего остального (загрузчики спрайтов, игровая логика, вывод графики) — компилятор QuickBasic 4.5
Реализация графической библиотеки
Мы хотим сделать библиотеку, которая позволит:
- Загружать спрайты из файлов в память
- Выводить их на экран с прозрачностью, поворотом и т.д.
- Делать это всё очень быстро
Зачем вообще нам применять ассемблер в таком случае? Только ради производительности. Частота таймера DOS — 18.25Гц, обновлять графику желательно с такой же частотой. А операции с массивами в QuickBasic (как и почти во всех языках высокого уровня) — очень медленные.
Пример: мы хотим заполнить буфер 10x10 зеленым цветом (код палитры 10).
Код на Бейсике:
for i=0 to 99
buf(i)=10
next i
Увы, данный код в процессе выполнения ровно сто раз вызывает внутреннюю функцию Бейсика, которая проверяет корректность индекса, устанавливает сегменты, сохраняет стек и прочее. Ничего этого нам не нужно. Потому напишем аналогичное решение на ассемблере (суть передачи данных, сохранение стека и возврат пока опустим, просто примем, что сегмент и смещение установлены на буфер)
mov cx,100 ; указываем, что цикл на 100 раз mov al,10 ; значение цвета cld ; говорим, что при записи байта, двигаем указатель вперед cicle1: stosb ; пишем байт по адресу и двигает указатель вперед loop cicle1 ; идем на следующий цикл, пока cx>0
Такая конструкция куда выгодней — для каждой записи, выполняются всего две команды — загрузка байта в память и проверка условия цикла. Можно записать короче:
mov cx,100 ; указываем, что цикл на 100 раз mov al,10 ; значение цвета cld ; говорим, что при записи байта, двигаем указатель вперед rep stosb ; пишем байт по адресу и двигает указатель вперед, пока cx>0
если мы уверены, что размер буфера кратен двум байтам, то можно еще более ускорить, записывая по 2 байта (слово).
mov cx,50 ; указываем, что цикл на 50 раз mov al,10 ; значение цвета mov ah,10 ; значение цвета cld ; говорим, что при записи слова, двигаем указатель вперед rep stosw ; пишем слово по адресу и двигает указатель вперед, пока cx>0
Теперь, если снабдить этот код нормальной инициализацией, передачей переменных в соответствии с принятыми соглашениями и описать процедуру на языке Бейсика — то можно сделать эффективную процедуру подготовки буфера.
Чтобы выводить буфер на экран, есть хороший подход — надо выделить буфер, копировать спрайты из буферов спрайтов в этот буфер, а потом одной командой вывести этот буфер на экран. По сути, это кустарный вариант реализации приёма «Двойная буферизация».
ru.wikipedia.org/wiki/%D0%94%D0%B2%D0%BE%D0%B9%D0%BD%D0%B0%D1%8F_%D0%B1%D1%83%D1%84%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F
Как же этого достичь, чтобы скорость была приемлемой? Аналогично — написать процедуру на ассемблере, которая будет копировать один буфер в другой (команда movsb копирует байт памяти из источника в приемник). Поскольку буфер назначения больше, чем буфер для спрайта, то нужно использовать построчное копирование (копируем байты в количестве ширины спрайта), а потом двигать указатель в буфере на ширину буфера минус ширина спрайта. Так мы быстро скопируем спрайт в буфер, сохранив геометрию. Остальные решения работают точно так же — например, при выводе спрайта с прозрачностью копируем байты, но проверяя при этом неравенство цвета условному цвету «прозрачный» (я использую цвет 255 для прозрачного).
Основные процедуры
В библиотеке реализованы основные процедуры:
- Подготовка буфера графики
- Копирование буфера графики
- Чтение файла формата «Индексированный PCX» в буфер спрайтов
- Чтение файла формата «Индексированный PCX» на экран
- Вывод спрайта в буфер в позицию
- Вывод спрайта в буфер в позицию с прозрачностью (поскольку прозрачности нет как таковой, то просто считаем, что цвет с кодом 255 будет прозрачным).
- Вывод фрагмент буфера в позицию (позволяет реализовать анимацию, то же для разворота и прозрачности)
- Вывод линии в буфер
- Заливка области в буфере
- Установка палитры, полученной из файлов графики
- Загрузка пиксельного шрифта
- Вывод текста пиксельным шрифтом
Все эти процедуры имеют ассемблерную основу и декларативную обертку для Бейсика. Поскольку передать напрямую массив в ассемблерный код нельзя, мы передаем в процедуры сегменты и смещение буфера. Подробнее — в следующем разделе.
Примеры кода
Наконец, приведу полный пример кода для одной конкретной, частично описанной выше задачи — инициализация буфера спрайта заданного размера заданным цветом.
Сначала ассемблерный код. В данную процедуру передаются процедуры через стек.
SECTION .TEXT BUFFER_WIDTH equ 10 ; сдвиг в стеке переменной ширины BUFFER_HEIGHT equ 8 ; сдвиг в стеке переменной высоты BUFFER_COLOR equ 6 ; сдвиг в стеке переменной цвета global setUpBufferAsm ; объявление процедуры как глобальной setUpBufferAsm: ; сохранение регистров в стек. В bp сохраняем смещение стека. push bp mov bp,sp push di push es push si push ds push ax push bx push cx push dx mov bx,[bp+14] ; считываем из стека смещение и сегмент переданного массива mov di,[bx] mov bx,[bp+12] mov es,[bx] cld ; установка движения вперед при записи в массив mov ax,[bp+BUFFER_WIDTH] ; считываем из стека ширину спрайта shl ax, 3 ; умножаем на три, чтобы перевести в биты stosw ; пишем ax в массив mov ax,[bp+BUFFER_HEIGHT] ; считываем из стека высоту спрайта stosw ; пишем ax в массив mov bx,[bp+BUFFER_WIDTH] ; вычисляем размер спрайта в словах - это высота*ширина/2 mul bx mov bx,2 div bx mov cx,ax ; и пишем в счетчик mov ax,[bp+BUFFER_COLOR] ; наконец, помещаем в ax два байта и оба равны цвету mov ah,al rep stosw ; пишем цвет в массив буфера по 2 байта за раз. ; восстанавливаем регистры pop dx pop cx pop bx pop ax pop ds pop si pop es pop di mov sp,bp pop bp retf 10 ; идем обратно в программу - число равно общей длине параметров в байтах, место, где в стеке адрес возврата.
Остальное достаточно просто. В NASM делаем объектный файл, в QuickBasic собираем из него модуль qlb для среды разработки и модуль lib для сборки exe-файла, и определяем процедуру на языке Бейсик:
DECLARE SUB setUpBufferAsm (BufAddr AS INTEGER, BufSeg AS INTEGER, BYVAL w AS INTEGER, BYVAL h AS INTEGER, BYVAL c AS INTEGER)
где первые две переменные — для передачи адреса, а указание BYVAL означает, что надо загнать в стек сами значения w,h,c вместо их адресов. Теперь можно создать программу, которая зальет буфер зеленым цветом и выведет его в заданной позиции.
DEFINT A-Z '$DYNAMIC '$INCLUDE: 'gratools.bi' CLS SCREEN 13 DIM sprbuf(1024) AS INTEGER CALL setUpBufferAsm(VARPTR(sprbuf(0)), VARSEG(sprbuf(0)), 40, 20, 10) PUT (200, 100), sprbuf(0), PSET END
Результат запуска
Вот и всё. Если кому понадобятся более подробные пояснения, примеры работ с другими процедурами (например, загрузка спрайта или вывод с прозрачностью), а также аналогичные постановки, но с другими языками (Pascal/C) — пишем в комментариях, покажу и расскажу.
64 комментария
Через VESA и переключение банков можно вплоть до 1280х1024х24бита/цвет
Это уже не совсем функции BIOS, задействована видеокарта. У меня на 486-ом, к примеру, 13h поддерживается, а вот включить VESA уже невозможно, тот же WarCraft 2 не запускается.
В ДОСе вроде были модели памяти EMS и XMS (кажется). Просто помню, во времена 98-й, изучая Pascal, я с какой-то из них немного возился. Там конечно заморочки с картированием сегментов куда-то в область между 640к и 1М, но использование этих моделей может дать ещё несколько Мб под спрайты. Не рассматривал такой вариант? На перспективу, скажем.
Так-то, я планирую делать игры на современных системах — OpenGL, SFML, Qt и подобное.
Если и бахну еще разок что-то под 16 бит, то опробую уже Turbo Pascal 5.5 или кросс-компиляцию для FreePascal.
Но меня попросили это сделать в теме про понную игру, так что я просто выполняю пожелания пользователей.
Нет, ну может конечно чисто по кайфу, хотя смахивает на мазохизм.
Это.
Ну вроде как идти под веслами по Большой Воде две морские мили на полутора узлах, хотя на катере можно за 4 минуты, без воды в морду.
Катер всегда удобней, но весла никуда не делись.
Free Pascal Compiler самый что ни на есть современный, а поддержку 16 бит RTL — оставили как раз для психов, на всякий случай.
А почему тебе нужно было именно 16 бит, вот я чего не могу понять? Некоторые делают игры, стилизованные под старые, но которые хотя бы под винду запустятся без досбокса
Я вроде бы раскрывал эту тему, но могу кратко повторить тезисно:
1) В конце 90-х я хотел написать игру, знал тогда только QuickBasic и немного TurboPascal с Delphi под win 3.11
2) Идея провалилась по причинам, раскрытым в этом посте — ну нельзя эффективно работать с памятью на 30 МГц без знания основ Ассемблера.
3) В 2000-м я поступил в ВУЗ, пошел работать, женился, завел детей, открыл бизнес, залез в долги, довел бизнес до ума, дети подросли, долги уменьшились до разумных пределов, дела немного наладились.
4) В 2020-м я доделал то, что не смог в 90-х.
5) Счастливый конец.
Но сейчас я рад, что если я захочу сделать игру, то мне уже не надо будет трахаться с байтами, ассемблером и прочей хренотой, поэтому и в голову бы не пришло возвращаться к софту тех времён. Да и забыл я уже давно, как там это работает.
Но то я, а то ты, у всех разные потребности
«Сначала было шампанское и девки, мы выпили всё, что горело и перетрахали всех курей, но тут приехал поручик с ведром керосина и...»
Вот я как тот поручик, у меня есть шампанское, и делать крупный проект на ассемблере — это явно за пределами разумного, но иногда хочется именно ведра керосина, особенно если в молодые годы его так и не выпил.
PS: А так-то, что ты, что я — мы оба сделали достойные проекты на относительно современных системах, за что нам взаимное увожение.
Вообще многое, что одни сочтут за тяжёлый неблагодарный труд, для других — классное развлечение. Я бы сам, было б у меня время, с радостью бы вспомнил те деньки с синим экранчиком кубейсика или третьих борландов.
Недельный карантин к вашим услугам.
Короче, покой мне только снится =(
Грех не упомянуть
Насчет миллионов, не уверен, всё-таки это довольно на любителя дело даже просто смотреть. Гляну при случае, спасибо.
Хм, а не у него ли я смотрел для одного из моих фанфиков, как делать японский бамбуковый лук?..
А, ну и никаких BSOD-ов не было, система просто тихо висла — а ты просто жал ресет, ждал, пока она перезагрузится, после чего брал отладчик и смотрел, где ты ошибся портом или адресом.
Как-то странно, 256 цветов — это же всего 8 бит. Сходится, если только у тебя 6 уровней каждого компонента цвета.
Относительно двойного буфера: разве тогда еще не применяли подмену буфера целиком, вместо копирования? Или это только со всяких директхов появилось?
Там индексированный цвет. То есть, всего может быть 64*64*64 цветов — но одновременно на экране активно не более 256 цветов, заданных текущей палитрой.
Были экранные страницы, очень удобный инструмент, но как раз в режиме 13h всего одна экранная страница, потому пришлось делать буферизацию руками.
Тогда так и уточни в посте, потому что режимов 8-битного цвета существует несколько.
Может быть был резон перекатиться на меньшее число цветов, но с несколькими экранными страницами?
Нет смысла, очень мало цветов будет, это уже бабушка-EGA выходит.
Вот относительно подробная таблица.
biosprog.narod.ru/real/ints/int10.htm
Это полностью автоматом, хотел руками сделать, но 2.8 гимп крашнулся, когда хотел размывание небу сделать.
Можно конечно, но реально, несложно сделать буфер на 64Кб и самому.
У меня под буфер 64Кб, под спрайты 120 и еще 80 на код+статические данные. В лимит 500 Кб спокойно влезаю.
А так-то тогда это вообще можно было бы на EGA сделать (и тогда бы я смог запустить это на своём 286-ом, а не доставать из шкафа лежаший где-то под грудой завалов 486-ой =)
Выводилось это как-то так (пишу чисто из головы, суть в алгоритме, а не его аутентичности):
Код упрощенный, не проверяем выходы за границы, переполнение dst и т.д.
Вообще, пробовал и такой способ представления данных, но выигрыша не заметил существенного.
У меня основная операция по времени — как раз копирование фоновой части буфера 320x200
и даже мухлеж со словом вместо байта особо не спасает
а операции вывода монстров и кристаллов занимают менее половины времени.
Тут такое дело, что если аппаратно двойная буферизация (как в OpenGL) не поддерживается, то её приходится сооружать самому, делая второй буфер в обычной памяти и переключаясь между ними не командой железа, а копированием.
Но суть та же — используем ДВА буфера, один на экране статичен, другой в памяти рисуется.
Заодно можно было бы проверить, работает ли это в DOSBox =) Вот видимо, терминологическая путаница возникает как раз по вопросу, что понимать под буфером: вообще всё, содержащее данные, или блок, в который можно писать при рендеринге кадра.
Это ещё не самая дичь: в электронике, например, иногда мультиплексором называют обычный мультиплексор из пачки AND-ов, объединённых OR-ом, а селектором — его же, но с дешифратором на адресных входах. А иногда наоборот, селектор называют мультиплексором, а селектором не называют вообще ничего, лол. Дешифратором называют часто то, что преобразует представление вида 1010 в 0000010000000000, но есть и дешифраторы команд и дешифраторы виртуальных адресов (которые на самом деле LUT, с микрокодом и TLB соответственно), то есть фактически ни капельки не дешифраторы… И так далее.
Так много дел, ага.
По времени, уже надо делать обновление «Героев Эквестрии», новые кампании, герои, сюжетка и т.д.
Про OpenWatcom уже писал, у него даже фанатская версия 2.0 недавно вышла.
Сейчас пробую Digital Mars, там для запуска нужен win32, но 16-битный crt0 и либы для четырех моделей памяти лежат рядом.
Кое-что есть, но это реально как бриться каменным топором.
Попробовал, и всё работает как положено, даже встроенный нормальный ассемблер в стиле asm { } есть.
Еще один инструмент в копилку.
Брать можно отсюда www.digitalmars.com/download/freecompiler.html
Тоже запомню на всякий случай.
Меня в целом С как-то смущает, вроде один и тот же код нормально работает в Паскале, но через раз падает в Си.
Но обычно бывает так — вот пишу какую-то лабу, страничка, две, три. А потом начинает сыпаться. Через раз. Или через два. И пока не выкинешь и не перепишешь заново — толку нет.
Потом делаю на Паскале или C#, алгоритм аналогичной сложности — и всё ОК с первого раза, максимум, выходные данные нужно исправить, если именно реализация неверная.
А, ну и да, если это просто прилинкованная реализация printf(), то это означает, что размер бинарника почти не будет расти, если ты используешь этот printf() не один раз, а сотню в разных местах.
А, ну и всегда есть всякие UPX (правда, это снизит размер бинарника на диске, а в памяти он будет таким же немаленьким).
Верно, я и забыл про эту функцию.
OpenWatcom и Turbo C по умолчанию тянут дополнительные зависимости.
Если буду обновлять игру, то часть движка, которая была на QuickBasic — перенесу на DigitalMarsCompiler, а на Бейсике оставлю только игровую логику.