Программирование трехмерной графики под MS-DOS

Загрузить исходный код примеров на С++, для Borland C++ 3.1 и WATCOM 1.9 C++ ЗДЕСЬ. В архиве приложен файл ReadMe.txt с коротким описанием программ. В архиве предоставлены программы MS-DOS для Borland C++ 3.1, WATCOM 1.9, Microsoft Visual C++ 1.52.

Ниже перечислены адреса начала видеобуфера для разных сред разработки.


WATCOM video addr

char * video_buffer =(char*) 0xA0000L;


BORLAND C++ 3.1 video addr

unsigned char far * video_buffer =(unsigned char *) 0xA0000000L;

Для BORLAND C++ 3.1 важно что бы переменная video_buffer ниже была объявлена глобально ( была глобальной переменной) иначе может не работать программа.


Microsoft Visual C++ 1.52 video addr

unsigned char far *video_buffer = (unsigned char far *)0xA0000000L;

Для VS 1.52c важно что бы переменная video_buffer ниже была объявлена локально (не была глобальной переменной) иначе может не работать программа.

Если вы хотите с нуля создать самостоятельно проект для dos4gw.exe в среде WATCOM, нужно выполнить следующие шаги.

Установить среду WATCOM (например WATCOM для Windows, потому что есть еще версия для MS-DOS), при установке указать - Host operating system - Windows, и Target operating system - MS-DOS. После установки вы создаете новый проект для MS-DOS, указываете тип EXE 32 бита, и вам предлагаеться в этом окне выбор расширителя, выберите dos4gw.exe расширитель, нажмите ОК для создания пустого проекта, затем добавте свои файлы CPP. В данном случае имеется в виду что вы установите WATCOM под Win95/98/ME. Сначала был просто расширитель для MS-DOS назывался dos4g.exe - что значит MS-DOS это "dos", число 4 значит 4 Гигабайта, и буква "g" значит Гигабайт. Но потом этот расширитель добавили в среду разработки WATCOM и к названию расширителя добавили еще букву "w" что и значит WATCOM.

В чем смысл расширителей, и в том числе расширителя dos4gw.exe? MS-DOS может адресовать до 1 Мегабайта памяти, и использует 16ти битные регистры процессора- например ax,bx,cx,ds и т.д. То есть MS-DOS не может работать с регистрами процессора 32 бит, например эти регистры eax, ebx, ecx, edx. MS-DOS работает в режиме реальной адресации, который позволяет использовать только 16ти битные регистры процессора, и не может работать в защищенном режиме, в котором используются 32 битные регистры процессора.

Таким образом, расширитель dos4gw.exe (и другие), делают возможным запускать программы под MS-DOS в защищенном режиме, с использованием 32х битных регистров процессора. Использование 32х битных регистров процессора позволяет адресовать до 4 Гигабайт памяти под MS-DOS, как это делают ОС Windows 95/98/ME/XP/7, и т.д., например, которые является 32х битными ОС.

Как происходит переход в защищенный режим? После запуска компьютера, компьютер работает в режиме реальной адресации, используя 16ти битные регистры процессора. Что бы перейти в защищенный режим и использовать 32х битные регистры процессора и 4 Гигабайта памяти, есть специальные инструкции процессора. Т.е. логика такая - ваша программа запускается в реальном режиме (например под MS-DOS или после включения компьютера). Затем выполняет специальные инструкции процессора для перехода в защищенный режим. После перехода в защищенный режим, программа может использовать 4 Гигабайта ОЗУ, 32 битные регистры процессора. Но не может вызывать прерывания которые вызываются в режиме реальной адресации.

Расширитель, например dos4gw.exe имеется в среде разработки WATCOM, и когда вы пишите проект для этого расширителя, при компиляции и линковке проекта, небольшой участок кода записывается перед вашей функцией main() на С++. Это небольшой участок кода запускает расширитель, а потом вызывает функцю main() вашей программы на С++. Таким образом запускается расширитель dos4gw.exe, который включает защищенный режим работы процессора, и запускает вашу программу на С++ уже в защищенном режиме.

Почему в защищенном режиме работы процессора доступно 4 Гигабайта памяти? В защищенном режиме работы процессора используются 32х битные регистры. Дальше пойдет немного двоичной арифметики, и вам нужно знать двоичную систему счисления. Одним словом число 2 - основание системы счисления, нужно возвести в степень 32 - это количество бит, или двоичных разрядов. Например что бы получить число 1000 в десятичной системе счисления, нужно 10 - основание системы счисления, возвести в степень 3, и получим число 1000. Т.е. в трех разрядах помещается число 1000. А сколько помещается в 32 битных двоичных разрядах, можно узнать если число 2 (основание системы счисления) возвести в степень 32. Напишем небольшую программку на С++ для вычисления степени:


#include <math.h>

int main (void)
{	

	float ret = powf(2.0, 32.0) - 1;

	return 0;
}

После выполнения это программы результат ret будет хранить число равно 4 Гигабайта. Почему там стоит -1 подумайте сами. Подсказка - 32 бита это 0xFFFFFFFF то есть powf(2.0, 32.0) - 1. А powf(2.0, 32.0) + 1 это 0x100000000. Число 0x100000000 уже хранит не 32 бита а на +1 больше. А нам надо 32 бита. Биты в системе счисления (то есть разряды) нумеруются в нуля.

В режиме реальной адресации процессору доступно всего 1 Мегабайт памяти. Как такое выходит. Надо знать адресацию памяти в реальном режиме адресации. На адресную линию процессора в режиме реальной адресации выдается 20 битовое двоичное число- адрес памяти. 20 разрядов могут хранить число которое равно 1 Мегабайту ОЗУ. (Как аналогично мы считали выше 2 в степени 32, тут надо возвести 2 в степень 20). MS-DOS разрабатывалась во времена когда ширина адресной линии процессора была 20 бит.

Но как же адресовалась память в режиме реальной адресации? Например покажу вам пример, два одинаковых адреса памяти, но по разному записаных:


//первый адрес
0x0070:0x0000

//тот же самый адрес адрес
0x0000:0x0700

В режиме реальной адресации для обозначения адреса используется "сегмент:смещение". Сегмент может быть максимум 16 бит, и смещение может быть максимум 16 бит (потому что используются 16ти битные регистры). Например число 0xFFFF - занимает 16 бит. Это в десятичной системе счисления 65535. То есть 64 Килобайта.

В режиме реальной адресации "сегмент" (16ти разрядное число, например в сегментном регистре SS - сегмент стека) внутри процессора, то есть число 0xFFFF например, умножается (внутри процессора подчеркну) на 16, то есть на 0x10, и таким образом (внутри процессора) мы получаем число 20 бит - 0xFFFFF которое и выдается на адресную линию 20 бит.

Почему внутри процессора значение сегмента адреса памяти умножается на 16 или на 0x10? Потому что каждый новый сегмент начинается с адреса кратным 16ти. То есть например есть 0й адрес, плюс 16 к нему- через 16 ячеек памяти начинается новый сегмент, плюс еще 16 - начинается новый сегмент, плюс еще 16 ячеек памяти - начинается новый сегмент. И каждый новый сегмент может быть размером 64 Килобайта.

Как это выглядит. С нулевого адреса у нас есть 64 Килобайта ОЗУ. Плюс 16 ячеек памяти, с этого адреса у нас еще есть 64 Килобайта ОЗУ (может адресовать процессо), с 32й ячейки памяти у нас начинается новый сегмент размером 64 Килобайта, и так далее. То есть каждый новый сегмент может начинаться с адреса кратного числу 16. Т.е. начиная с 0го адреса памяти каждый новый сегмент начинается ровно через 16ть ячеек.

Таким образом у нас есть сегмент 16ти битное число (значение сегментного регистра кратное 16ти, которое умножается внутри процессора на 0x10). И есть смещение внутри сегмента 16ти битное число. Но за счет того что внутри процессора значение сегментного регистра умножается на 16 что бы подать результат на 20ти разрядную адресную линию, мы получаем 20 разрядов для адреса. Таким образом пример выше - два одинаковых адреса. Сегмент 0x70 умножить на 0x10 (то есть это 16 в десятичной системе счисления) будет 0x700 абсолютный адрес.

Таким образом что бы в режиме реальной адресации получить абсолютный адрес (абсолютное смещение от начала всей памяти) надо значение сегментного регистра умножить на 16 (0x10) и добавить смещение. Мы получим 20ти разрядное число в диапазоне от 0 до 1 Мегабайта. Число 2 в 20й степени равно числу равному 1 Мегабайт. В те далекие времена когда возник режим реальной адресации памяти, адресная линия процессора была 20 бит ширина.

Есть точно - 8086 процессор имел 20ти разрядную адресную линию (не мог работать в защищенном режиме). Потом 80286 процессор имел 24 разярную адресную линию (он уже мог работать в защищенном режиме, в процессор добавили инструкции перехода в защищенный режим). 80386 процессор имел 32х разрядную адреснуюи линию (и мог переходить в защищенный режим). Но 80286 и 80386 работая в режиме реальной адресации по прежнему адресовали до 1 Мегабайта ОЗУ. Например если число 2 возвести в степень 24 - мы получим число 16 Мегабайт, в защищенном режимер процессор 80286 мог адресовать до 16ти Мегабайт.