Андре ламот программирование игр для windows 1 издание

Скачать книгу

Содержание

  1. Андре Ламот: Программирование игр для Windows
  2. Комментарии
  3. Андре Ламот «Программирование игр для Windows» (примеры)
  4. Программирование игр. Сборник (4 книги+ 4 CD)

Андре Ламот: Программирование игр для Windows

216

booksСкачать книгу

greenb

commentsКомментарии

Fiw, 10.11.2010 21:25

Большое спасибо от студента!

Toster, 15.05.2011 06:24

Полезная книжка для начинающих, очень не плохие примеры

Batonsu, 09.10.2011 12:01

А есть у кого-нибудь содержимое CD примерами из этой книги?

Timur, 17.11.2012 12:19

u, 24.10.2013 18:15

Artem, 25.04.2015 05:10

Год оригинального издания — 1999. Вообще-то в сети уже не первый год гуляет второе издание (оригинал — 2002, перевод — 2003).

Евгений Москва 1974, 12.04.2016 03:59

У меня была эта книга и компакт диск, но потерял CD, если найду напишу, если кто-нибудь найдёт раньше, напишите пожалуйста [email protected]

Сергей, 18.01.2022 14:33

Книга не скачивается. После нажатия меня перенаправляет на эту же страницу без загрузки книги.

Источник

Андре Ламот «Программирование игр для Windows» (примеры)

Компилятор: Visual Studio 2008

Решил откомпилировать код примера из книги Андре Ламот «Программирование игр для Windows». После исправления мелких ошибок с приведением типа получил код(см. ниже). Смысл в том что отображается bmp файл. При запуске выскакивает ошибка. Вопрос такой, я считаю проблема с загрузкой bmp, должен ли он быть явно включён в проект ( в source files), нужно ли файлы дублировать в соответствующие папки debug/release?
заранее спасибо, только начинаю изучать DDraw.

Открою жуткую правду ты начал изучат DirectDraw а не DX и даже не D3D

Спасибо за мою опечатку, буду рад, если поможешь с проблемой

Lazer
пройдись пошагово по програме(в дебаге) F5 и посмотри что у тебя в переменных.
F9 брейкпоинт
F10/F11 шаг.

А Novartis Games таки прав Лямота можно читать в обучающих целях )

>А Novartis Games таки прав Лямота можно читать в обучающих целях )
имхо в обучающих целях лучше почитать теорию по комп графике, и подучить геометрию)))
directidraw уже сто лет как деприкейтед))) нада брать литературу посвежее)

ну во всяком случае, не стоит сильно парится с этими примерами, главное понять суть, ковырять лучше уже d3d

а чем DirectDraw так плох? по-мойму для 2D-игр достаточно функционален.

Frank D. Luna
Introduction to 3D Game Programming with DirectX 9.0

Снука лучше читать после этих книг.

Novartis Games
Direct-мамонт-Draw. мммм, вкуснятина! 🙂 чуствую себя в ДОС-е 🙂

Lazer
Попробую помочь, если надо помощь. Ламот у меня кстати бумажный есть.

Судя по
>OpenFile(filename,&file_data,OF_READ))
и
>if (!Load_Bitmap_File(&bitmap,»bitmap24.bmp»))
файл-картинка читается не из ресурсов, а с диска. Может попробуй задать полный путь, типа «c:\folder\bitmap24.bmp», хотя это скорее всего не причина ошибки.

soflot, огромное спасибо за отзыв. Спустя долгое время пока я покопался в поисках ответа по интернету и нашёл топик в зарубежном форуме.
Как я понял суть в том, что надо заменить OpenFile, _lread, _lclose, and _lseek на fopen, fread, fclose and fseek. А также «write to a back buffer and then flip it». В этом посте есть собственно отредактированные исходники
(ссылка топика на форуме: http://www.xgamestation.com/phpbb/viewtopic.php?p=6616&sid=457ce2… 097830ca35f94).

WhiteWolf, спасибо за перечень книг, обязательно куплю при возможности. Начинать что-то новое всегда сложно, особенно когда не знаешь что именно при таком быстром развитии технологий.

на на счет ошибки ищи ее в файле lseek.c на строке 66 =))

Ооо), спасибо что не смотря на то, что тема была создана давно и затерялась среди других, пишите.
Да. Смогли меня отговорить начать с неё начинать осваивать DirectX 🙂

concialed
надо же, не знал.

kloun Полностью с тобой согласен. Сам начинал программировать игры вместе с этой книгой. В действительности автор книги охватывает множество важных вещей связаных с разработкой игр и принципах работы. Первое и самое главное, он даёт понять устройство игр, конструкцию, скелет. В книге затрагиваются основы физического моделирования, искусственного интеллекта.

В D3D намного проще реализовать различные эффекты с цветом, поворот картинки, масштабирование с интерполяцией. Но книга «Программирование игр для Windows» не только про DDraw, хотя он занимает в ней солидную часть.
Итог: книгу читать обязательно!

Источник

Программирование игр. Сборник (4 книги+ 4 CD)

1470244523 bez imeni 2.jpg9
Название: Программирование игр. Сборник (4 книги+ 4 CD)
Издательство: Вильямс, Питер
Автор: Андрэ Ламот
Год: 1995-2007
Количество страниц:498+1424+718+877
Язык:русский
Формат:pdf+CD
Размер:1,2 Gb

Эти книги написаны выдающимся специалистом в области программирования игр с многолетним стажем, и полезны как начинающим, так и профессиональным разработчикам игр.

Секреты программирования игр

Первое отечественное издание, представляющее полное руководство по программированию игр любого уровня сложности, включая сложные динамические игры со звуковыми эффектами (типа DOOM). Книга написана известными специалистами и содержит массу ценнейших советов по программированию различных игровых эффектов и ведению проекта от идеи до ее практической реализации с использованием всего спектра аппаратного обеспечения персонального компьютера (различных видеокарт, звуковых карт и т. п.). Для чтения книги необходимо знакомство с языком С. На прилагаемой дискете содержатся все исходные тексты

Программирование игр для Windows. Советы профессионала

Книга представляет собой введение в DirectX для разработчиков игр для
Windows. Определенные знания в области программирования (в частности, языка программирования C или C++), математики и физики облегчат изучение материала.

Программирование трехмерных игр для Windows

Книга написана выдающимся специалистом в области программирования игр с многолетним стажем, и полезна как начинающим, так и профессиональным разработчикам игр.
Книга рассчитана на опытного специалиста, владеющего языком программирования C++, и имеющего определенную математическую подготовку. Желательно приступать к ней после ознакомления с упомянутыми ранее книгами.
В книге освещены различные аспекты разработки трехмерных игр, однако основное внимание уделяется вопросам представления трехмерных объектов, их визуализации с учетом свойств материала объектов, освещения, перспективы.

Источник

Обновлено 23.06.2022

Просмотров 2018

Tricks of the Windows Game Programming Gurus Second Edition

Книга предназначена для читателей, интересующихся вопросами разработки игр в операционной системе Windows. В ней освещены разнообразные аспекты программирования игр — от азов программирования до серьезного рассмотрения различных компонентов DirectX, от простейших физических моделей до сложных вопросов искусственного интеллекта. Книга будет полезна как начинающим, так и профессиональным разработчикам игр для Windows, хотя определенные знания в области программирования (в частности, языка программирования C или C++), математики и физики существенно облегчат изучение материала.

Автор: Andre LaMothe

Скачать (PDF)

Видео

Делаю РАБОЧИЕ ИГРЫ в PowerPoint, MS Word (2048)

Делаю РАБОЧИЕ ИГРЫ в PowerPoint, MS Word (2048)

Microsoft: Создание игр на DirectX для Windows 8 и Windows Phone 8

Microsoft: Создание игр на DirectX для Windows 8 и Windows Phone 8

Как Сделать Игру Без Программирования

Как Сделать Игру Без Программирования

С чего начать делать игры | Разработка для новичков

С чего начать делать игры | Разработка для новичков

Делаю игру на ОЧЕНЬ СТАРОМ ноутбуке

Делаю игру на ОЧЕНЬ СТАРОМ ноутбуке

Игра Gems Of War — PvP и новая неделя [Stream 7/2023]

Игра Gems Of War - PvP и новая неделя [Stream   7/2023]

КАК СДЕЛАТЬ ИГРУ НА C# + WinForms ЗА 30 МИНУТ? 👾 #1

КАК СДЕЛАТЬ ИГРУ НА C# + WinForms ЗА 30 МИНУТ? 👾 #1

ДУМ И ЗЕЛЬДА В ТАБЛИЦАХ! Игры в Microsoft Excel

ДУМ И ЗЕЛЬДА В ТАБЛИЦАХ! Игры в Microsoft Excel

Программирование на Java: создание игры Змейка. Часть 1.

Программирование на Java: создание игры Змейка. Часть 1.

Игры, которые обучат вас программированию

Игры, которые обучат вас программированию

5 популярных игр и их языки программирования

5 популярных игр и их языки программирования

Написал 3D Игру в Консоли!

Написал 3D Игру в Консоли!

Я пытался изучить программирование с нуля за 7 дней и вот что получилось в итоге

Я пытался изучить программирование с нуля за 7 дней и вот что получилось в итоге

Windows 18! Microsoft, что ты сделала??!!

Windows 18! Microsoft, что ты сделала??!!

Игры для программистов, о которых вы наверняка не слышали. GeekBrains

Игры для программистов, о которых вы наверняка не слышали. GeekBrains

Как сделать Flappy Bird на С++

Как сделать Flappy Bird на С++

ТОП 5 ИГР, которые учат программированию | ИГРЫ ДЛЯ ПРОГРАММИСТОВ

ТОП 5 ИГР, которые учат программированию | ИГРЫ ДЛЯ ПРОГРАММИСТОВ

Как научиться программировать с нуля играя в игры. Java, JavaScript, C++, C#, Python, PHP, Ruby

Как научиться программировать с нуля играя в игры. Java, JavaScript, C++, C#, Python, PHP, Ruby

Разработка игры на Python | Pygame. Урок #1

Разработка игры на Python | Pygame. Урок #1

Учим Unity за 1 час! #От Профессионала

Учим Unity за 1 час! #От Профессионала

Программирование игр для Windows. Советы профессионала

Автор: kotmatros255 от 8-07-2020, 07:07, Коментариев: 0

Категория: КНИГИ » ПРОГРАММИРОВАНИЕ

Название: Программирование игр для Windows. Советы профессионала
Автор: Андре Ламот
Издательство: Вильямс
Год: 2003
ISBN: 5-8459-0422-6
Формат: pdf
Страниц: 877
Размер: 14,1 Mb
Язык: Русский

Книга предназначена для читателей, интересующихся вопросами разработки игр в операционной системе Windows. В ней освещены разнообразные аспекты программирования игр — от азов программирования до серьезного рассмотрения различных компонентов DirectX, от простейших физических моделей до сложных вопросов искусственного интеллекта. Книга будет полезна как начинающим, так и профессиональным разработчикам игр для Windows, хотя определенные знания в области программирования (в частности, языка программирования C или C++), математики и физики существенно облегчат изучение материала.


Нашел ошибку? Есть жалоба? Жми!
Пожаловаться администрации

Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.

Скриншот одного из портированных примеров
Скриншот одного из портированных примеров

Мотивация к написанию статьи

Уважаемые коллеги, доброго времени суток!

Этой статьей я хочу показать приёмы портирования программ между аппаратно-программными платформами, и привлечь внимание к книге «Секреты программирования игр» Андрэ Ламота, 1995, которую вспомнят добрым словом многие разработчики компьютерных игр, и другим не менее полезным книгам этого автора.

Нам интересны приёмы портации, но мы также проведём ревью, проверим насколько долговечен код 25-тилетней давности, и насколько сложно его портировать на любые современные платформы.

Я обосную и некоторые случаи примененного «ненормального программирования», а изложенный план, как и полученный микро-движок, вы можете использовать при создании своих портов.

Мотивация к созданию порта

В 1996 году, когда с массовыми компьютерами всё только начиналось, мне в руки попала книга «Секреты программирования игр» Андре Ламота. Ему было 22 года, а мне 20. Я был студентом и изучал в университете физику и компьютерные науки, а также плотно «висел» с 14 лет на программировании на всевозможных языках и математике.

Книга меня впечатлила, она была понятна. Это книга о складывании высоконагруженных движков, выжимающих всё, что можно, из математики, алгоритмов и железа, и я рекомендую её к прочтению и пониманию всем инженерам, причастным к сфере разработки программ.

Книга учит «с нуля» писать такие движки и игры

Книга учит «с нуля» писать такие движки и игры

Когда спустя почти 20 лет, мне понадобился учебный материал для моих студентов, я вновь обратился к данной книге. Однако, возникли проблемы – её примеры разработаны под DOS, и они не работали под Windows либо Linux.

Кто такой Андре Ламот и почему вам следует прочесть его книги?

Андре Ламот – культовая фигура в мире разработки игр. Им созданы и портированы сотни видеоигр под всевозможные платформы.

Андре работал с idSoftware в момент создания ими Wolfenstein и Doom. Чтобы оценить влияние книг Андре на мир разработки, достаточно прочесть отзывы под его постами в Linkedin

Первоначально, я рекомендую к чтению «Секреты программирования игр» от 1995 г., как фундаментальную, но простую для понимания книгу, охватывающую полноту разработки 2D и 3D игровых движков и игр на их базе. Эту книгу осилят и новички, и школьники, а изложенные там методы оптимизации, к примеру, позволили мне снизить время выполнения вычислений при моделировании физических процессов – с часов и дней – до реалтайма!

Но главное, книга учить грамотно структурировать код, и прививает функциональный стиль в проектировании и оформлении алгоритмов, чего не хватает многим разработчикам.

Легкий путь не сработал

Конечно, я не искал трудных путей, и попробовал обойтись без порта, ограничившись DOSBox и Borland C++ 3.1 от 1992 года. Но отладчик в DOS неудобен, а сам DOSBox часто закрывался с ошибками. Watcom C/C++ (Open Watcom 1.9) слёг с теми же симптомами.

Тогда я решил портировать примеры из книги в Windows. Порт занял неделю (в свободное от работы свободы время). Все примеры из книги работают, все проверены.

Постановка задачи

  1. Примеры включают в себя 45 программ: от простейших – до полноценного 3D движка вроде Doom-а.

  2. Необходимо внести минимальные изменения в исходный код, а лучше – не вносить вообще – ведь примеры привязаны к тексту и листингам книги.

  3. Хотелось бы автоматизировать внесение изменений в 62 .c и 12 .h файлов и отделаться минимальным программированием.

  4. Основная цель: программы должны работать; у студентов должна быть возможность их отладки и модификации.

Идея

  1. Сделать аналоги (обёртки) функций DOS и микродвижок-прослойку между OS Windows и портируемыми программами.

  2. Микродвижок выполнить на WinAPI и разместить в двух файлах: DOSEmu.cpp и DOSEmu.h. Последний содержит «подменённые» функции DOS, и при подключении к собираемым программам, они «подхватят» эти функции.

  3. Требуемые куски кода стараться вырезать из кодовой базы эмуляторов DOS и программ с Github.

  4. Максимально избавится от ручного труда, для чего вносить изменения в исходники всех портируемых программ автоматически и одновременно, инструментом «Search->Replace in files» в IDE Code::Blocks.

Первичная оценка

Следовало оценить сработает ли идея, и стоит ли браться за работу вообще.
Листинги программ показали, что идея реализуема – код включает математику, функции C standard library (libc), немногие ассемблерные вставки и немногие вызовы DOS API.
Главное же – кода мало. Ибо когда вы видите многотомную кодовую «лапшу», это верный признак того, что нужно бежать: и от этого кода и от его создателей.

План реализации

Особенность книги в том, что примеры идут «от малого к большому»: по мере прохождения глав, к существующим алгоритмам добавляются новые.
Отсюда решено портировать программы подряд, обогащая функционал движка и добавляя лишь задействованные функции DOS, что исключит любые кодовые «излишества».

Демонстрация алгоритма трассировки лучей ближе к концу книги

Демонстрация алгоритма трассировки лучей ближе к концу книги

Реализация

Я не буду приводить подробный листинг, а остановлюсь на некоторых интересных деталях DOS и моментах, связанных с портированием.

Шаг 1. Унифицируем грамматику С

В программах есть ключевые слова «доисторической» грамматики С: _far, __far, __huge, итд., специфичные для модели памяти ранних процессоров Intel с 16 битной адресацией, и не поддерживаемые современными компиляторами. Они исключены так:

//------------------------------------------
// OBSOLETE COMPILER DIRECTIVES
//------------------------------------------
#define _far
#define __far
#define __huge
#define __near
#define _interrupt
//------------------------------------------

Шаг 2. Унифицируем библиотеки С

Часть задействованных в портируемых программах функций имеет прямые аналоги в современных библиотеках, что дало такой код:

//------------------------------------------
// MEMORY FUNC ALIASES
//------------------------------------------
#define _fmalloc		malloc
#define _ffree			free
#define _fmemcpy		memcpy
#define _fmemset		memset
#define _fmemmove		memmove
//------------------------------------------

Так же везде закомментированы два устаревших заголовочных файла:

// #include <graph.h>
// #include <bios.h>

Шаг 3. Избавляемся от несовместимых ассемблерных вставок

Хороший программист знает, что алгоритмы пишут и отлаживают на доменных языках, а затем, редкие, критические к скорости участки, переписывают на ассемблере.
Именно такова кодовая база примеров книги, и «избавление» от несовместимого с современными процессорами ассемблера шло раскомментированием С-кода и комментированием ассемблерных вставок (ведь исходники необходимо сохранить в оригинале).

Шаг 4. Эмулируем «программные прерывания» OS DOS

В DOS нет многопоточности, и в каждый момент времени выполняется единственный процесс. Нет в DOS и «подгружаемых библиотек», но есть весьма неглупый механизм «прерываний» для «межпроцессного» взаимодействия программ.

Чтобы вызвать функцию другого процесса, необходимо установить в регистрах микропроцессора передаваемые ей аргументы, и вызвать команду процессора «прерывание». Тогда процесс, установивший функцию-«обработчик» данного «прерывания», получит выполнение, и вернёт в регистрах результат своего выполнения тому процессу, который его вызвал. То есть «перывание» – это аналог «межпроцессной» ассемблерной инструкции call.

Значительную часть функционала DOS инкапсулируют несколько прерываний, закрепленных за функциями процессов DOS.

Свою функцию за «прерыванием» закрепляют через:

void _dos_setvect(int interrupt_id, void* fn)

А для вызова «прерывания» служит:

int int86(int interrupt_id, union REGS *r_in, union REGS *r_out)

где поля структуры REGS содержат данные, помещаемые в (из) регистры микропроцессора:

Структура REGS

//------------------------------------------
// <dos.h> <i86.h> STRUCTS AND STUBES
//------------------------------------------
// INTERRUTPS HANDING

/* word registers */

struct WORDREGS {
        unsigned short ax;
        unsigned short bx;
        unsigned short cx;
        unsigned short dx;
        unsigned short si;
        unsigned short di;
        unsigned short cflag;
};
struct BYTEREGS {
        unsigned char al, ah;
        unsigned char bl, bh;
        unsigned char cl, ch;
        unsigned char dl, dh;
};

union REGS {
        struct WORDREGS  x;
        struct WORDREGS  w;
        struct BYTEREGS  h;
};

Поэтому достаточно написать такую «подмену» функции int86(), чтобы эмулировать большую часть специфичного функционала DOS (изменение видеорежима, чтение состояния мыши, клавиатуры, итд.).

Функция int86()

int int86(int interrupt_id, union REGS *r, union REGS *r_out)
{
	switch(interrupt_id)
	{
	case VIDEOBIOS_INT__:
		{
			switch(r->w.ax)
			{
			case VGA_VIDEOMODE_13h__:
				_setvideomode(_MRES256COLOR);
				break;
			case VIDEOBIOS_FUNC_SETPALETTE__:
				{
					BYTE* raw_palette = (BYTE*)MK_FP(s->es, r->w.dx);
					_setpalette(raw_palette);
				}
				break;
			}
		}
		break;

	case KEYBOARD_INT_16h__:
		{
			switch(r->h.ah)
			{
			case KEYBOARD_INT_16h_AH_01:
				r_out->w.cflag = (PRESSED_RAW_DOS_KEY == 0) ? I386_FLAG_ZF : 0;
				break;
			case KEYBOARD_INT_16h_AH_00:
				r->h.ah = PRESSED_RAW_DOS_KEY;
				break;
			}
		}
		break;

	case MOUSE_INT__:
		{
			switch(r->h.al)
			{
			case MOUSE_SHOW__:
				ShowCursor(TRUE);
				break;
			case MOUSE_HIDE__:
				ShowCursor(FALSE);//hides the cursor
				break;
		}
		break;

		// …
		// …
		// …
	}

}

Казалось бы, чем в 2022 году интересен механизм прерываний 1980-х? Обоснованно полагаю, что разработчикам многотомных API следует брать на вооружение приёмы, укладывающие API в 2-3 функции.

Шаг 5. Эмулируем «порты ввода-вывода»

«Порты ввода-вывода» – это разновидность HAL (Hardware Abstraction Level), механизм пользуемый для унификации работы с оборудованием.

Запись последовательности байт в закрепленный за устройством «порт», – это команда, которую следует выполнить устройству.

Для программы «порт» – это ячейка, куда (откуда) можно писать (читать) данные (обычно байты).

В DOS для записи и чтения данных в «порт» служат функции:

void _outp(int port, int value);
unsigned char _inp(int port);

В портируемых программах они использованы для изменения цветовой палитры экрана, пересылки данных по сети и чтения состояния клавиатуры.

Вот моя эмуляция функции _inp() (функция _outp() выглядит похоже):

_inp()

unsigned char _inp(int port)
{
	AUTOLOCK(*vgaScreenPrimary);
	VGAScreen* scr = vgaScreenPrimary;

	unsigned char ret = 0;

	switch(port)
	{
	case PALETTE_DATA__:
		{
			ret = vgaScreenPrimary->m_screen->m_surfacePalette[scr->INOUT_palette_offset*3 + scr->INOUT_palette_write_count];
			scr->INOUT_palette_write_count++;

			if(vgaScreenPrimary->m_VGA_RENDER_state & RENDER_CORRECT_PALETTE)
			{
				ret = ((unsigned int)ret) >> 2;
			}
		}
		break;
	case VGA_INPUT_STATUS_1__:
		{
			static unsigned int state = 0;
			ret = (state++ % 2) ? VGA_VSYNC_MASK__ : 0;
		}
		break;
	case KEY_BUFFER__:
		{ // LOCK START
			AUTOLOCK(emuKernel);
			ret = PRESSED_RAW_DOS_KEY;
		} // LOCK END
		break;

	//------------------------------------------
	// SERIAL WORK
	//------------------------------------------
	case COM_1__SER_LSR:
	case COM_2__SER_LSR:
		ret = 0x20; // TELL COM WRITE READY
		break;
	case COM_1__SER_RBF:
	case COM_2__SER_RBF:
		ret = m_curr_income_serial_port_byte;
		break;
	//------------------------------------------

	default:
		ret = 0;
	}

	return ret;
}

Шаг 6. Эмулируем сеть

В книге есть единственная сетевая игра «Танки». Обмен состояниями между экземплярами игры идёт через устройство «COM порт» по API «порты ввода-вывода». Можно эмулировать эту связь через TCP/IP, но это займёт время и не повлияет на основную цель проекта (дать учебный материал студентам). Поэтому я использовал «ненормальное программирование»: «сетевой трафик» между экземплярами игры идёт через WinAPI функцию SendMessage(), и их следует запускать на одном компьютере.

Вы будете писать сетевую игру «Танки»

Вы будете писать сетевую игру «Танки»

Шаг 7. Эмулируем видео субсистемы

В портируемых программах задействованы все основные виды вывода изображений DOS:

  1. Символьный вывод в консоль функциями из <stdio.h> (print(), _settextposition(), …).

  2. Вывод графических примитивов (точка, линия, эллипс, прямоугольник …) функциями из <graph.h>.

  3. Запись массивов пикселей в видеопамять напрямую, для чего видеоадаптер переводят в «графический режим».

Возиться совмещением консоли и графики в едином окне не хотелось, поэтому опять трюк из «ненормального программирования»: программа начинает работу в консольном режиме, а при попытке перевода видеоадаптера в «графический режим» открывается дополнительное окно. Таким образом, консольный вывод идёт в окно консоли, а графика выводится в окно графики.

Алгоритмы для графических примитивов (_rectangle(), _ellipse(),_lineto() …) найдены и взяты с Github.

Видеопамять в DOS находится в ОЗУ, начиная с адреса 0xA0000000. Поэтому в исходниках программ встречается запись

unsigned char far *video_buffer = 0xA0000000;

Я выделил буфер unsigned char* MEMORY_0xA0000000 = malloc(…) и заменил в исходниках 0xA0000000 на MEMORY_0xA0000000

unsigned char far *video_buffer = MEMORY_0xA0000000;

После записи в такую «видеопамять» необходимо вызвать _redraw_screen(), для переноса изменений буфера на реальный экран. К слову, это 1-2 места на портируемую программу.

Шаг 8. BIOS

По мере портирования выяснилось, что алгоритмы программ используют фрагменты BIOS, а именно шрифты, «зашитые» по адресу 0xF000FA6E. Поэтому я заменил в исходниках адрес 0xF000FA6E на глобальную переменную MEMORY_0xF000FA6E, которую определил так:

unsigned char far *MEMORY_0xF000FA6E = VGA_FONT_8X8;

А сам шрифт позаимствовал (конечно, с указанием авторства) из библиотеки SDL (Simple DirectMedia Layer):

Цифры 5, 6, 7 шрифта (заданы битовой маской)

unsigned char VGA_FONT_8X8[8*256] = {

// …
// …
// …

    /*
    * 53 0x35 '5'
    */
    0xfe,           /* 11111110 */
    0xc0,           /* 11000000 */
    0xc0,           /* 11000000 */
    0xfc,           /* 11111100 */
    0x06,           /* 00000110 */
    0xc6,           /* 11000110 */
    0x7c,           /* 01111100 */
    0x00,           /* 00000000 */

    /*
    * 54 0x36 '6'
    */
    0x38,           /* 00111000 */
    0x60,           /* 01100000 */
    0xc0,           /* 11000000 */
    0xfc,           /* 11111100 */
    0xc6,           /* 11000110 */
    0xc6,           /* 11000110 */
    0x7c,           /* 01111100 */
    0x00,           /* 00000000 */

    /*
    * 55 0x37 '7'
    */
    0xfe,           /* 11111110 */
    0xc6,           /* 11000110 */
    0x0c,           /* 00001100 */
    0x18,           /* 00011000 */
    0x30,           /* 00110000 */
    0x30,           /* 00110000 */
    0x30,           /* 00110000 */
    0x00,           /* 00000000 */

// …
// …
// …

}

Шаг 9. Немного фетиша

В ОЗУ DOS по адресу 0x0000046C расположена ячейка счётчика (таймера), инкрементируемого 18.2 раз в секунду. Он используется в вычислениях интервалов времени, например, так:

unsigned int far *clock = (unsigned int far *)0x0000046C; // pointer, 18.2 clicks/sec

while(abs(*clock - now) < clicks){} }

Логично создать переменную PMEM_0x0000046C, и обновлять её в отдельном потоке с частотой 18.2 раз в секунду, но тут возникают коллизии при доступе к ней из разных потоков.

Поэтому я добавил потокобезопасную функцию с именем ULONG PMEM_0x0000046C(), возвращающую значение таймера, и заменил в исходниках строку

 unsigned int far *clock = (unsigned int far )0x0000046C;

на

ULONG (*_clock)() = PMEM_0x0000046C;

Дальнейшее «переписывание» исходников свелось к автоматической замене *clock на обрамленную в скобки *_clock:

while(abs((*_clock)() - now) < clicks){} }

Шаг 10. Звук

Со звуком в DOS дела обстоят так: в начале 1990-х было несколько основных звуковых карт со специфическими, достаточно корявыми API. Поэтому уже авторами портируемых программ была написана микро-библиотека из функций-обёрток (wrappers), инкапсулирующих работу со звуком.

Сюрпризом оказались и сами звуковые файлы – формат VOC ушёл в небытие уже к концу 1990-х.

Поэтому с кодом звуковой библиотеки я обошёлся наиболее жестоко – я удалил всё содержимое функций-обёрток, превратив их в заглушки, всегда возвращающие статус «выполнено успешно» (кстати, подобным образом я «заглушил» и часть функций DOS в эмуляции COM порта и клавиатуры), сконвертировал VOC файлы в WAV-формат и свёл всю эмуляцию звуковой субсистемы DOS к вызову единственной функции WinAPI – PlaySound(). Всё это заняло, наверное, ~20 минут работы.

Движок

Получившийся микро-движок «заводит» в среде Windows программы DOS с минимальным изменением кода последних (вам будет интересно сравнить исходный и портированный код, например, в Total Commander).

Посчитаем основные WinAPI функции, задействованные в движке, и посмотрим на их применение.

  1. CreateThread – для запуска потока, эмулирующего таймер DOS и потока обновляющего графическое окно.

  2. EnterCriticalSection, LeaveCriticalSection – для синхронизации потоков.

  3. MapVirtualKey, GetAsyncKeyState – для работы с клавиатурой и мышью.

  4. StretchDIBits, SetDIBitsToDevice – для, говоря математическим языком, «отображения множества» из буфера видеопамяти DOS в «множество» в видеопамяти графического окна Windows. Менее поэтическим языком, это значит, что эти функции масштабируют изображение и отрисовывают видеобуфер DOS на экране.

  5. PlaySound – для эмуляции звука.

С полтора десятка оставшихся WinAPI функций, впрочем, как и бОльшая часть кода микро-движка, – это «накладные» расходы для запуска оконного приложения в Windows. За что следует поблагодарить, как гласят слухи и легенды, наших собратьев из Мексики, приложивших усилия к разработке WinAPI в 1990-х.

«Помарки» в исходниках или несколько слов в рубрику «Хозяйке на заметку»

На все портируемые исходники было лишь несколько незначительных «помарок» с поучительным разбором вызванных ими происшествий.

  1. С ошибкой «нарушение доступа к памяти» падала процедура трассировки лучей. Полагаю, причина в разной реализации арифметики с плавающей запятой в старых и новых процессорах (см., например, Floating-point migration issues). Казалось бы, незначительные погрешности в точности из-за различий в операциях над float приводят к некорректной работе математических алгоритмов и падению программ!
    Глубоко вникать в процедуру трассировки лучей я не стал, а доработал алгортим в месте падения «напильником»:

     	//------------------------------------------
     	// ALGORITHM ERRORS FIXING
     	//------------------------------------------
     	if(cell_y > (WORLD_ROWS-1)) cell_y = (WORLD_ROWS-1);
     	if(cell_y < 0) cell_y = 0;
     	if(cell_x > (WORLD_COLUMNS-1)) cell_x = (WORLD_COLUMNS-1);
     	if(cell_x < 0) cell_x = 0;
     	//------------------------------------------
  2. В зависимости от опций компиляторы или присваивают не инициализированным контейнерам типа int нулевые значения, или нет. Несколько программ падали именно из-за этого. Установите правилом инициализировать переменные при их объявлении!

  3. Корректная работа при компиляции в Debug и «зависания» в Release.
    Компиляторы при оптимизации (и не только), способны привнести ошибки в алгоритмы. И GCC, и Clang при оптимизации -O2 делают непредсказуемым поведение функции, если в её сигнатуре указано, что функция возвращает значение, но в реализации она ничего не возвращает.

Рассмотрим функцию:

Render_Sliver_32()
{
	Render_Sliver(sliver_object, sliver_scale, sliver_column);
}

По правилам С, если не указан тип возвращаемого значения функции, то таковым считается int, однако Render_Sliver_32() ничего не возвращает.
Поэтому вызов Render_Sliver_32() – намертво повесит программу, скомпилированную GCC или Clang с опцией –O2. А если сделать объявление так:

void Render_Sliver_32()
{
	Render_Sliver(sliver_object, sliver_scale, sliver_column);
}

то всё заработает корректно.

Как вы узнаете из книг Андре Ламота, при создании критически важных программ, оптимизацию отключают, но данном случае я добавил void в сигнатуры немногих «недообъявленных» функций.

Выводы и впечатления от работы

Портация шла легко и быстро: программы «заводились» мгновенно – стоило добавить в движок обёртки для задействованных функций DOS. Изменения в коде программ почти не требовались, за исключением упомянутых мелких правок. (Например, сетевая игра «Танки» «завелась» сразу, как только была добавлена нехитрая эмуляция COM порта.)
Реализация этого проекта ещё раз подтвердила известное правило: хорошие инженеры пишут краткий, внятный, кросплатформенный код, а их работы не устаревают. Чего желаю всем!

Исходники

Исходники портированных программ и движок можно взять с Github. Возможно, это пригодится тем, кому требуется с минимальными тратами времени и сил «заводить» DOS программы.

Продолжая добрую традицию, заданную @8street в статье «Как я портировал DOS игру», оставляю следующий постскриптум:

P.S. Джун нужен кому? Просьба в личку.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
//Sunday - Main.cpp
 
#define WIN32_LEAN_AND_MEAN 
#define INITGUID 
 
#include <windows.h>
#include <time.h>
#include <ddraw.h>
 
 
// DEFINES ////////////////////////////////////////////////
 
#define WINDOW_CLASS_NAME "WINCLASS" 
#define WINDOW_TITLE      "Graphics Console"
#define WINDOW_WIDTH      800   
#define WINDOW_HEIGHT     600
 
#define WINDOW_BPP        16    // bitdepth of window (8,16,24,32)
                                // note: if windowed and not
                                // fullscreen then bitdepth must
                                // be same as system bitdepth
                                // also if 8-bit the a pallete
                                // is created and attached
 
#define WINDOWED_APP      0     // 0 not windowed, 1 windowed
 
#define MSG_ERROR 
                MessageBox(NULL, "ERROR", "MSG_ERROR", MB_OK)
 
#define _RGB16BIT565(r, g, b)
        ((b & 31) + ((g & 63) << 5) + ((r & 31) << 11))
 
#define _RGB32BIT(a, r, g, b)
                ((b) + ((g) << 8) + ((r) << 16) + ((a) << 24))
 
// PROTOTYPES /////////////////////////////////////////////
 
int Game_Init(void *parms=NULL);
int Game_Shutdown(void *parms=NULL);
int Game_Main(void *parms=NULL);
 
// GLOBALS ////////////////////////////////////////////////
 
LPDIRECTDRAW7 lpdd = NULL;
LPDIRECTDRAWSURFACE7 lpddsprimary = NULL;
LPDIRECTDRAWSURFACE7 lpddsback = NULL;
 
DDSURFACEDESC2 ddsd ;
 
 
HWND main_window_handle = NULL; // save the window handle
HINSTANCE main_instance = NULL; // save the instance
char buffer[256];               // used to print text
int window_closed;
 
 
// FUNCTIONS //////////////////////////////////////////////
 
void Plot16(int x, int y, int red, int green, int blue, UINT *buffer, int mempitch)
{
  buffer[x+y*(mempitch >> 1)] = _RGB16BIT565(red, green, blue);
}
void Plot32(int x, int y, int alpha, int red, int green, int blue,UINT *buffer, int mempitch)
{
    buffer[x+y*(mempitch >> 2)] = _RGB32BIT(alpha, red, green, blue);
}
int Game_Init(void *parms)
{
    if(FAILED(DirectDrawCreateEx(NULL, (void**)&lpdd, IID_IDirectDraw7, NULL)))
        return(0);
 
    if(FAILED(lpdd -> SetCooperativeLevel(main_window_handle, DDSCL_FULLSCREEN | DDSCL_EXCLUSIVE | DDSCL_ALLOWREBOOT)))
        return(0);
 
    if(FAILED(lpdd -> SetDisplayMode(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_BPP,0,0)))
        return(0);
    
    ZeroMemory(&ddsd, sizeof(ddsd));
    ddsd.dwSize     = sizeof(ddsd);
    ddsd.dwFlags        = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
    ddsd.dwBackBufferCount      = 1;
    ddsd.ddsCaps.dwCaps     = DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX |DDSCAPS_FLIP;
 
    if(FAILED(lpdd -> CreateSurface(&ddsd, &lpddsprimary, NULL)))
        return(0);
 
    ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
 
    if(FAILED(lpddsprimary -> GetAttachedSurface(&ddsd.ddsCaps, &lpddsback)))
        return(0);
    
    return(1);
}
int Game_Main(void *parms)
{
    if(window_closed)
        return(0);
 
    if(GetAsyncKeyState(VK_ESCAPE))
  {
        PostMessage(main_window_handle, WM_DESTROY,0,0);
        window_closed = 1;
  } 
    
    
    ZeroMemory(&ddsd, sizeof(ddsd));
    ddsd.dwSize = sizeof(ddsd);
 
    if(FAILED(lpddsprimary -> Lock(NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR |  DDLOCK_WAIT, NULL)))
        return(0);
 
    UCHAR *back_buffer = (UCHAR *)ddsd.lpSurface;
 
    if (ddsd.lPitch == WINDOW_WIDTH)
        memset(back_buffer, 0, WINDOW_WIDTH*WINDOW_HEIGHT);
    else
    {
        UCHAR *dest_ptr = back_buffer;
 
        for (int y=0; y<WINDOW_HEIGHT; y++)
        {
            memset(dest_ptr,0,WINDOW_WIDTH);
      dest_ptr+=ddsd.lPitch;
        }
    }
    for(int i=0; i<500; i++)
    {
        int x=rand()%WINDOW_WIDTH;
        int y=rand()%WINDOW_HEIGHT;
 
        back_buffer[x+y*ddsd.lPitch] = _RGB16BIT565(255, 255, 255);
    }
                     
    if(FAILED(lpddsprimary -> Unlock(NULL)))
        return(0);
 
    while (FAILED(lpddsprimary->Flip(NULL, DDFLIP_WAIT)));
 
    Sleep(500);
 
    return(1);
}
int Game_Shutdown(void *parms)
{   
    if(lpddsback)
    {
        lpddsback -> Release();
        lpddsback = NULL;
    }
    if(lpddsprimary)
    {
        lpddsprimary -> Release();
        lpddsprimary = NULL;
    }
    if(lpdd)
    {
        lpdd -> Release();
        lpdd = NULL;
    }
 
    return(1);
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
    switch(msg)
    {   
    case WM_CREATE: 
        {
 
            return(0);
        } break;
   
    case WM_PAINT: 
        {
 
            return(0);
        } break;
 
    case WM_DESTROY: 
        { 
            PostQuitMessage(0);
            return(0);
        } break;
 
    default:break;
 
    }
    return (DefWindowProc(hwnd, msg, wparam, lparam));
}
// WINMAIN ////////////////////////////////////////////////
 
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hprevinstance,LPSTR lpcmdline, int nshowcmd)
{
    WNDCLASSEX winclass1;
    HWND hwnd;
    MSG msg;
    srand(1);
 
    ZeroMemory(&winclass1, sizeof(winclass1));
    winclass1.lpszClassName     = WINDOW_CLASS_NAME;
    winclass1.lpfnWndProc       = WindowProc;
    winclass1.cbSize        = sizeof(WNDCLASSEX);
    winclass1.hInstance     = hinstance;
    winclass1.hbrBackground     = (HBRUSH)GetStockObject(BLACK_BRUSH);
    winclass1.hCursor       = LoadCursor(NULL, IDC_ARROW);
    winclass1.hIcon     = LoadIcon(NULL, IDI_APPLICATION);
    winclass1.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);
    winclass1.style     = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
 
    if(!RegisterClassEx(&winclass1))
        return(MSG_ERROR);
 
    if (!(hwnd = CreateWindowEx(NULL, WINDOW_CLASS_NAME,    
WINDOW_TITLE, WS_POPUP | WS_VISIBLE,0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 
NULL, NULL, hinstance, NULL)))
        return(MSG_ERROR);
 
    main_window_handle = hwnd;
    main_instance      = hinstance;
    
    Game_Init();
 
    while(TRUE)
    {
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if (msg.message == WM_QUIT)
                break;
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        Game_Main();
    }
    Game_Shutdown();
    return(msg.wParam);
}

Понравилась статья? Поделить с друзьями:
  • Андеграунд скачать бесплатно на компьютер на русском языке для windows
  • Английский языковой пакет для windows 10 phasmophobia
  • Английский язык на экране приветствия windows 10
  • Английский язык для windows 7 sp1
  • Английский язык для windows 7 service pack 1 64 bit