Стандарт VESA создавался для того, чтобы графические задачи могли самостоятельно, или при минимальном вмешательстве оператора, настроиться на работу с установленной на ПК видеокартой. В этой главе описано, как производится такая настройка.
Любой стандарт оставляет некоторую свободу действий производителям оборудования, поэтому существуют модели видеокарт, которые формально соответствуют требованиям VESA, а фактически их программирование все же имеет специфические особенности. Тем не менее, возможна единая схема, в которую укладывается работа с большинством наиболее распространенных видеокарт. Мы рассмотрим элементы этой схемы работы с видеокартами, а обнаруженные автором отклонения от нее будут специально оговариваться.
Независимо от видеорежима VESA, который используется в задаче, перед началом работы с графикой должны быть выполнены определенные действия, обеспечивающие в дальнейшем се корректную работу, универсальность и независимость от модели видеокарты. Вот перечень этих действий:
проверить, поддерживает BIOS требуемый видеорежим или нет; проверить, достаточно видеопамяти для выбранного режима или нет; получить и сохранить в области данных характеристики режима; прочитать и сохранить исходный видеорежим (не обязательно); установить требуемый видеорежим VESA; вычислить константу для коррекции номеров окон видеопамяти; настроить подпрограммы для работы с видеоокнами; определить размер и расположение полей базовых цветов.При выполнения перечисленных действий используются функции VBE, описанные в предыдущей главе.
Для нормального выполнения любой прикладной задачи должны быть созданы соответствующие условия. Поэтому при разработке задач, как правило, предусматриваются вспомогательные действия, направленные на проверку и создание таких условий. В этом отношении графические задачи не являются исключением.
Предварительные действия, выполняемые в графических задачах, можно разделить на две категории по признаку их зависимости от видеорежима. В первую очередь обычно выполняются те из них, которые не зависят от видеорежима, используемого в задаче. К ним относятся проверки операционной среды (версии DOS), наличие необходимого пространства оперативной памяти, изменение значений векторов прерываний, формирование многократно используемых величин и т. п.
От номера версии DOS зависит набор функций, выполняемых по запросам прикладных задач. Базовый набор функций, предназначенных для работы с файловой системой, был сформирован в версии 3.0 и с тех пор существенно не изменялся. На большинстве современных компьютеров используются версии не ниже 6.0, поэтому вопрос о необходимости проверки версии DOS решает программист с учетом особенностей создаваемой задачи.
Графические задачи обычно нуждаются в большом пространстве оперативной памяти, соизмеримым с объемом видеопамяти, необходимым для поддержания используемого видеорежима. Поэтому в процессе подготовительных действий обязательно производится определение доступного для задачи пространства оперативной памяти, его резервирование и распределение для внутреннего использования. Как это делается, описано в приложении Б данной книги.
В процессе выполнения задачи могут использоваться специальные таблицы. Если это общесистемные таблицы, то надо определить их расположение в оперативной памяти или в области BIOS. Если же таблицы являются собственностью задачи, то их надо разместить в доступном пространстве памяти. Вспомогательные действия, выполняемые при работе с таблицами шрифтов, содержащими изображения букв, цифр и других символов, описаны в главе 5.
В тех случаях, когда задача должна реагировать на прерывания от внешних устройств, при выполнении подготовительных действий надо создать условия для вызова прерывающих подпрограмм. В главах 5 и 6 описана настройка подпрограмм, реагирующих на прерывания, поступающие от системного таймера и манипулятора "мышь".
В процессе настройки могут быть выявлены условия, препятствующие выполнению задачи или требующие вмешательства оператора, например не соответствующая версия DOS, недостаточный объем оперативной памяти и пр. В таком случае на экран выводятся аварийные сообщения или поддерживается диалог с оператором, если он может что-то изменить. Вывод сообщений проще программировать в текстовом режиме, который установлен DOS перед вызовом задачи. Поэтому переход в графический видеорежим целесообразно производить после всех описанных проверок.
Когда невозможна установка видеорежима?
Установке требуемого видеоре-жнма могут препятствовать три причины:
режимы VESA вообще не поддерживаются; не поддерживается конкретный видеорежим; недостаточно видеопамяти для работы в этом режиме.Возникновение первой причины может означать, что на компьютере установлена очень старая видеокарта. Кроме того, если задача была вызвана не из DOS, а из Windows З.Х, 9Х, ME или 2000, то при определенных условиях она может не получить доступ к функциям BIOS. Если же задача была вызвана из DOS и качество видеокарты не вызывает сомнений, то имеет смысл проверить состояние системного программного обеспечения, установленного на компьютере.
Возникновение второй причины означает, что предельно допустимый объем видеопамяти, поддерживаемый видеокартой, не позволяет работать в выбранном видеорежиме.
Возникновение третьей причины означает, что установленный на видеокарте объем памяти меньше предельно допустимого и того, который нужен для работы в запрашиваемом видеорежиме.
Объем видеопамяти, необходимый для работы в конкретном режиме, можно подсчитать на основании данных, приведенных в табл. 1.1. Если окажется, что видеокарта имеет достаточный объем памяти, то имеет смысл проверить состояние системного программного обеспечения, установленного на компьютере. Разумеется, вы должны быть уверены в корректности самой задачи.
Рассмотрим, как производится проверка возможности работы в выбранном видеорежиме. Для проверки соответствия видеокарты стандарту VESA и получения общей информации о ней предназначена функция 4FOOh прерывания int l0h (см. главу 1). Перед ее вызовом в паре регистров es:di надо указать адрес буфера размером в 256 байтов для размещения полученных данных. Указание буфера обязательно, независимо от того, будет задача использовать эти данные или нет.
Буфер такого же размера потребуется и при следующей проверке для размещения основных данных, используемых в процессе настройки задачи. Выделять специально для него постоянное место в памяти, например, в разделе данных задачи, не целесообразно, поскольку полученная информация нужна только при выполнении подготовительных действий. В задаче наверняка должен быть предусмотрен буфер большего размера для чтения файлов, содержащих рисунки. В приложении Б данной книги описано, как создается такой буфер. При выполнении подготовительных действий этот буфер свободен и в его начало можно поместить служебную информацию. Предположим, что буфер существует и сегмент, в котором он расположен, хранится в переменной info, описанной в разделе данных задачи.
Проверка существования VBE
В примере 2.1 приведен текст фрагмента программы, проверяющего соответствие видеокарты стандарту VESA.
Пример 2.1. Проверка поддержки стандарта VESA
test_1:push es | ; сохранение содержимого es |
mov es, Info | ; значение сегмента буфера Info |
xor di, di | ; адрес начала буфера |
mov ax, 4F00h | ; код запрашиваемой функции |
int 10h | ; обращение к BIOS |
cmp ax, 4Fh | ; стандарт VESA поддерживается ? |
jz test 2 | ; -> да, продолжение проверок ; нет, выполнение задачи не возможно |
pop es | ; вывод аварийного сообщения |
Выполнение примера 2.1 начинается с сохранения в стеке содержимого регистра es (если он еще не использовался и его содержимое не имеет значения, то команды push es и pop es можно исключить из текста примера). Далее в регистр es записывается значение сегмента, содержащего буфер info, a di очищается для размещения данных с начала буфера. После этого в регистр ах записывается код запрашиваемой функции и происходит обращение к прерыванию BIOS int 10h. Если видеокарта соответствует стандарту VESA, то при возврате из BIOS в ах находится код 4Fh, это и проверяет команда cmp ax, 4Fh. Если результат проверки положительный, то следующая команда jz выполнит переход на метку test_2, которая обозначает начало примера 2.2.
Если результат проверки отрицательный (код отличается от 4Fh), то дальнейшее выполнение задачи не возможно. На экран надо вывести аварийное сообщение типа "Видеокарта не поддерживает режимы VESA" И Прекратить выполнение задачи. Как можно подготовить текст сообщения и вывести его на экран, описано в главе 5, посвященной работе с текстом.
В случае успешного выполнения запроса, в буфере info находится общая информация о видеокарте (см. главу 1). Как правило, ее использует только Windows при выборе драйверов для работы с конкретной картой. Нам драйверы подбирать не надо, поэтому интерес может представлять только объем видеопамяти, указанный в слове массива info со смещением 12h. Эта величина выражена в блоках размером по 64 Кбайт, поэтому 1 Мбайт соответствует код 10h. Объем видеопамяти в случае его использования при выполнении задачи надо сохранить в области данных, т. к. уже на следующем шаге содержимое массива info изменится.
Проверка поддержки видеорежима. Если видеокарта соответствует стандарту VESA, то надо проверить, поддерживается выбранный вами видеорежим или нет, и одновременно прочитать в массив info информацию о нем. В примере 2.2 показано, как можно выполнить эти действия.
Пример 2.2. Чтение информации о режиме и проверка его поддержки
test 2:mov ax, 4F01h | ; код запрашиваемой функции |
mov ex, NewMode ; | ; код нужного видеорежима |
int 10h | ; обращение к BIOS |
pop es | ; восстановление содержимого es |
cmp ax, 4Fh | ; нужный режим поддерживается? |
jz test_3 |
; -> да, продолжение проверок |
Предполагается, что пример 2.2 выполняется после примера 2.1, поэтому пара регистров es:di содержит адрес буфера info для записи информации о режиме и в стеке сохранено содержимое регистра es. Выполнение примера начинается с указания кодов запрашиваемой функции и нужного видеорежима, после чего происходит обращение к BIOS для выполнения запроса. После возвращения в задачу восстанавливается сохраненное в стеке исходное содержимое регистра es. Если видеокарта рассчитана на поддержку выбранного видеорежима, то в ах будет находиться код 4Fh, это и проверяет предпоследняя команда примера. Если условие выполнено, то произойдет переход на метку test_3 (начало примера 2.3) для продолжения проверок.
Если код в регистре ах отличается от 4Fh, то видеокарта не поддерживает требуемый видеорежим. В зависимости от конкретных особенностей задачи, ее выполнение может быть либо прервано, либо предпринята попытка перейти на работу в другом режиме, требующем меньший объем видеопамяти. Если вы уверены в корректности задачи и в том, что видеокарта поддерживает нужный режим, то проверьте системное программное обеспечение, установленное на компьютере. В практике автора был случай, когда драйвер манипулятора "мышь" реагировал на выполнение функции 4FOih, к которой °н не имел никакого отношения. В остальном работа этого драйвера не вызывала никаких нареканий. Ошибка была выявлена при работе задачи, в которой проверялось исполнение запросов функций VESA, и устранена путем замены драйвера.
Заключительная проверка
Если в регистре ах находится код 4Fh, то остается последний штрих — проверить достаточность реально существующего (а не предельно допустимого) объема видеопамяти для поддержки вндеоре-жима. При сборе информации о запрошенном режиме BIOS выполняет нужные для проверки вычисления и сравнения, результат которых помещается в нулевой разряд нулевого байта массива info. Задаче остается только проверить состояние этого разряда.
После выполнения примера 2.2 было восстановлено исходное содержимое регистра es и доступ к массиву info с использованием этого регистра уже невозможен. Для дальнейшей работы с данными о режиме надо выделить другой сегментный регистр, например, fs или gs, который не используется функциями BIOS.
Фрагмент программы, выполняющий заключительную проверку, приведен в примере 2.3. Предполагается, что он выполняется после примеров 2.1 и 2.2, поэтому регистр di очищен и указывает начало буфера, содержащего данные о режиме. Для доступа к этим данным используется сегментный регистр fs, поэтому в него записывается значение переменной info.
Пример 2.3. Заключительная проверка поддержки видеорежима
test_3: mov fs, Info | ; сегмент с данными о режиме |
test byte ptr fs:[di], 1 | ; объем видеопамяти достаточен ? |
jne stmd | ; -> да, конец проверок |
; Недостаточно видеопамяти для поддержки нужного режима |
В команде test запись byte ptr явно указывает, что операндом является байт. Тип операнда указывается в тех случаях, когда Макроассемблер не может его определить по записи команды. Если нулевой разряд байта установлен, то объем памяти достаточен и команда jne передаст управление на метку stmd, указанную перед первой командой примера 2.4 .
Если нулевой разряд очищен, то объем видеопамяти не достаточен для поддержки выбранного режима. Что делать в подобных случаях, говорилось при описании примера 2.2.
Установка видеорежима. После успешного выполнения трех описанных проверок можно либо сразу установить рабочий видеорежим, а затем продолжить подготовительные действия, либо, наоборот, сначала завершить всю подготовку и лишь после этого устанавливать рабочий видеорежим. Только из соображений удобства описания, мы сначала рассмотрим установку видеорежима, а затем вернемся к подготовительным действиям.
Установку режимов VESA осуществляет функция 4F02h. Если при завершении задачи должен быть восстановлен исходный видеорежим, то его значение сохраняется в разделе данных. Следующий фрагмент программы иллюстрирует способ сохранения исходного и установку нового видеорежима.
Пример 2.4. Сохранение исходного видеорежима и установка нового
stmd: mov ax,OFOOh | код функции "Чтение видеорежима" |
int lOh BIOS | читает текущий видеорежим |
mov OldMode, al | сохранение кода видеорежима |
mov bx, NewMode | ; код одного из режимов VESA |
mov ax, 4F02h | ; код запрашиваемой функции BIOS |
int lOh BIOS | ; исполняет запрос |
cmp ax, 4Fh | ; режим установлен ? |
jz succ -> | ; да, на продолжение программы |
; Ошибка при установке видеорежима |
В тексте примера 2.4 использованы имена OldMode и NewMode. Первое из них может быть только именем байта, расположенного в области данных программы. Как создаются такие имена, описано в последнем разделе данной главы. NewMode может быть именем расположенного в области данных слова, или константы, которой заранее присвоено конкретное значение, скажем, NewMode = noh. Кроме того, код режима может быть указан в команде явно, например, mov bx, noh. Если при выполнении задачи видеорежим устанавливается только один раз, то выбор способа указания NewMode произволен. Тем не менее использование переменных является более универсальным и предпочтительным приемом.
Первые три команды примера 2.4 считывают текущий видеорежим и сохраняют
его в байте OldMode. Следующие три команды устанавливают новый режим,
в котором будет работать задача. После второго возврата из BIOS анализируется
содержимое регистра ах. Если в нем записан код 4Fh, то нужный режим установлен
и происходит переход на начало примера 2.5 (метка succ). Отличие кода
от 4Fh означает чрезвычайную ситуацию, поскольку предварительно были выполнены
проверки, показавшие, что видеокарта поддерживает нужный режим. Если ваша
задача заведомо корректна, то остается только проверять общее состояние
компьютера и программного обеспечения.
В примере 2.4 для определения значения исходного видеорежима издается
запрос OFh прерывания int 10h. При его исполнении BIOS просто считывает
в регистр al содержимое байта 0000:0449, относящегося к области данных
BIOS. Выполнить эти действия можно непосредственно в задаче без обращения
к BIOS. В таком случае исключаются примерно 30 команд, которые BIOS выполняет
при расшифровке запроса, сохранении и восстановлении содержимого регистров.
Коды VESA и OEM
Размер кода режимов VESA превышает размер байта, поэтому при установке этих режимов в байт 0000:0449 записывается так называемый код OEM, т. е. код, выбираемый по усмотрению разработчиков видеокарты. В ROM BIOS имеются две таблицы соответствия для преобразования кодов видеорежимов из VESA в OEM и обратно. Никаких соглашений относительно значений кодов OEM не существует, кроме того, что его размер не превышает семи разрядов, а значения отличаются от кодов видеорежимов IBM. Функция VBE 4F03h читает код текущего видеорежима из байта 0000:0449 и преобразует его в код режима VESA по таблице соответствия, хранящейся в ROM BIOS. Если в указанном байте находился код одного из видеорежимов IBM, то его значение не изменяется. В этом отношении функция 4F03h более универсальна, чем функция OFh.
Стандартная функция установки видеорежимов IBM (функция оо прерывания int 10h) установит режим VESA, если при обращении к ней в регистре al указать соответствующий код OEM. Так что коды OEM не совсем бесполезны, они, например, используются в драйверах для Windows.
Если вы владеете техникой дисассемблирования, то можно сравнительно
просто найти в ROM BIOS таблицы соответствия кодов режимов VESA и OEM.
Эти таблицы используются функции VBE 4F02h И 4F03h.
При программировании графики надо знать количество-точек на экране по горизонтали и вертикали, способ кодирования цвета, расположение базовых цветов, способ переключения окон видеопамяти, значение сегмента для доступа к видеопамяти и некоторые другие данные. Одни из этих величин зависят от видеорежима, другие — от особенностей видеокарты, третьи — от установленного на компьютере оборудования и программного обеспечения. Поэтому есть только один способ сделать задачу переносимой и заключается он в максимальном использовании данных, приведенных в табл. 1.2. После выполнения примеров 2.1—2.3 эти данные расположены в массиве info. Значение сегмента массива находится в регистре fs, а адрес начала массива (смещение от начала сегмента) равен нулю и расположен в регистре di.
Для сохранения и последующего использования служебной информации в разделе данных задачи надо зарезервировать переменные требуемого размера и присвоить им соответствующие имена. Например, размер рабочего поля экрана по вертикали мы будем хранить в переменной versize, а по горизонтали — в переменной Horsize. Подробнее о резервировании и назначении переменных сказано в
На видеокарте обязательно расположена оперативная память, которую принято называть видеопамятью (video memory). Видеоконтроллер непрерывно выводит содержимое части видеопамяти на экран монитора, причем размер этой части зависит от установленного видеорежима. На современных видеокартах базовый объем памяти составляет не менее 1 Мбайт (1 Мбайт равен 1 048 576 байтам) и может быть расширен, по крайней мере, до 4 Мбайт при установке на видеокарту дополнительных микросхем. Напомним, что для работы в режиме VESA ивь четырех мегабайтов недостаточно. У акселераторов объем видеопамяти существенно больше (до 64 Мбайт).
Специфической особенностью семейства IBM PC является ограничение пространства доступных адресов размером 1 Мбайт.
Оно делится на сегменты, предельный размер которых составляет 64 Кбайт (1 Кбайт равен 1024 байтам). Всего в пространстве адресов помещается 16 сегментов предельного размера, 10 из них занимает оперативная память ПК.
Для доступа к видеопамяти выделяется один сегмент размером в 64 Кбайт, который чаще всего имеет адрес доооь для графических и взооь для текстовых режимов. Только при указании этих сегментов графическая или текстовая информация будет направлена в видеопамять или считана из нее, поэтому их значения ни при каких условиях не должны изменяться задачей. Размер видеосегмента значительно меньше реального объема видеопамяти. Для того чтобы задачи могли работать со всей памятью, в современных видеоконтроллерах реализован следующий механизм.
Все пространство видеопамяти делится на сегменты размером по 64 Кбайт,
которые пронумерованы начиная с нуля. По принятой терминологии такие сегменты
называют окнами или видеоокнами. В специальном регистре видеоконтроллера
хранится номер текущего окна. При обращениях к видеопамяти контроллер
добавляет его к 16-разрядному адресу, указанному задачей, и получает абсолютный
адрес. Количество разрядов в абсолютном адресе зависит от предельного
объема памяти, которая может быть установлена на видеокарте. Если на видеокарте
может быть установлено 2 Мбайт памяти, то адрес занимает 21 разряд, если
установлено 4 Мбайт — то 22 разряда и т. д.
Таким образом, полный адрес видеопамяти складывается из двух частей. Младшая
часть является относительным адресом (смещением в сегменте), а старшая
часть — номером текущего окна, хранящимся в одном из регистров видеоконтроллера.
Задача может изменять текущее окно с помощью функции 4F05h.
Прямое обращение к BIOS. Видеокарты различаются не только адресами регистров, в которых хранится номер текущего окна, но и тем, в каких разрядах этих регистров он располагается. Поэтому при описании функции 4F05H в документации специально оговорено, что номер окна выражается в единицах приращения его значений, дословно in granularity unit. В примере 2.6 показан способ вычисления единицы приращения, она хранится в переменной GrUnit. Обычно она равна 1, только для одной видеокарты получилось другое значение, но если есть исключения, то надо следовать рекомендациям VESA и использовать переменную GrUnit.
Для установки окна с помощью функции 4F05h его номер помещается в регистр dx, а регистр bx очищается. После этого в регистр ах записывается код функции 4F05h и выполняется команда int lOh. В примере 2.7 показан способ прямого обращения к BIOS для установки окна. В нем и во всех последующих примерах текущий номер окна, выраженный в единицах GrUnit, выбирается из переменной cur_win. Она имеет размер слова и располагается в разделе данных задачи (см.
Большинство исследованных автором видеокарт поддерживало работу только с одним окном А. Исключением явился акселератор тасЬб4 фирмы ATI Technologies Inc., у которого для доступа к видеопамяти используется два окна. Запись данных в видеопамять осуществляется через окно А, а чтение — через окно в. Оба окна отображены на один видеосегмент АОООЬ. При обращениях к видеопамяти видеоконтроллер самостоятельно выбирает нужное окно, в зависимости от того записываются данные или считываются. Программисту остается "только" следить за тем, какое из двух окон надо переключать при достижении границы сегмента. Но это "только" может стать серьезным камнем преткновения для неискушенного программиста.
Стандартом VESA предусмотрена возможность работы с двумя окнами А и В, чем говорилось при описании функции BIOS 4F05h. Для работы с окном А (его установки или чтения) регистры (младший байт регистра bx) должен быть очищен, а для работы с окном в в него записывается единица. Если существует только одно окно, то оно имеет имя А и доступно для записи и чтения. Для проверки количества окон достаточно проанализировать состояние окна в, т. к. А заведомо существует. Если окно в доступно только для чтения, то А будет доступно лишь для записи или наоборот. Оба окна не могут быть доступны для выполнения одной и той же операции с видеопамятью, поскольку в таком случае придется вводить специальный механизм выбора нужного окна, а он стандартом VESA не предусмотрен.
При поддержке видеокартой двух окон возможны два способа работы с ними. Первый способ (в описании стандарта VESA он называется overlapping windows) основан на одновременном (синхронном) изменении номеров обоих окон. Второй (в описании стандарта VESA он называется distinguished windows) — на независимом изменении номеров окон (различение окон).
При программировании большинства графических алгоритмов удобнее иметь дело с одним окном видеопамяти, независимо от того, сколько их есть на самом деле. Для этого надо изменить описанное в
Одним из традиционных приемов при работе с видеопамятью является ее деление на страницы. Из нескольких страниц только одна отображается на экране монитора, а остальные не видны. Видимую (отображаемую на экране) страницу называют активной, а невидимые — пассивными. Изменение содержимого невидимых страниц никак не отражается на экране монитора. Поэтому можно заранее подготовить и расположить на пассивной странице нужное изображение, а затем "мгновенно" изменить картинку на экране, сделав эту страницу активной.
Возможность деления видеопамяти на страницы основана на том, что при взаимодействии с монитором видеоконтроллер отображает только ту ее часть, которая нужна для заполнения рабочей области экрана. Остальное пространство видеопамяти просто не используется. Размер рабочего пространства видеопамяти зависит от установленного видеорежима и изменяется в достаточно больших пределах (см. табл. 1.1). Соответственно изменяется и размер свободного пространства. В одних случаях оно может быть намного больше рабочей части видеопамяти, а в других его может просто не быть. Поэтому возможность и целесообразность деления видеопамяти на страницы решается с учетом ее реального объема и используемого в задаче видеорежима.
Стандарт VESA не распространяется на страничную организацию видеопамяти и, вообще, его авторы предпочитают говорить не о страницах, а о разных изображениях, расположенных в видеопамяти. Поэтому все практические вопросы решаются по усмотрению программиста. Именно он выбирает способ переключения и расположение страниц в видеопамяти.
Как уже говорилось, активной является та страница, содержимое которой в данный момент отображается на экране монитора. Специальный механизм переключения страниц отсутствует, но существует функция VBE с кодом 4F07h (см. раздел), которая позволяет переместить начало рабочей области (Display start) в любую точку видеопамяти. Есть только одно ограничение на ее применение — от выбранной точки до конца видеопамяти должно оставаться пространство, достаточное для размещения рабочей области. Координаты выбранной точки указываются в виде строки и столбца.
Перед вызовом этой функции в регистр dx помещается порядковый номер первой отображаемой строки, а в регистр сх — номер ее первой точки. Напомним, что все номера начинаются с нуля. Кроме этого регистр bx очищается, что является признаком изменения начала отображаемой области, а в регистр ах помещается код функции 4F07h. После чего производится обращение к BIOS. Описанные действия выполняет группа команд примера 2.10.
Пример 2.10. Установка нового начала отображаемой области памяти
хог bx, bx ; признак смены страницы
mov ex, BaseCol ; номер точки в исходной строке
mov dx, BaseRow ; номер исходной строки
mov ax, 4F07h ; код запрашиваемой функции
int 10h ; обращение к BIOS
В примере 2.10 значения координат начала рабочей области выбираются из переменных BaseCol и BaseRow, которые должны быть описаны в сегменте данных программы. Для превращения этого примера в подпрограмму переключения активных страниц к нему надо добавить вычисление значений указанных переменных по номеру страницы. Способ вычисления выбирается по усмотрению программиста, а зависит он от выбранного расположения страниц в видеопамяти.
При работе в режимах packed pixel graphics, использующих регистры палитры, перед переключением страниц может понадобиться сохранение в оперативной памяти текущей палитры и установка новой. Способы работы г палитрой цветов описаны в главе 4. При работе в режимах Hi-color и Truecoior палитра не используется, т. к. цвет каждой точки указан в ее коде.
Изменения в вычислениях адресов
Страничная организация памяти влияет олько на способы определения адресов точек графических объектов. Поэтому желательно выбрать такое расположение страниц, при котором изменение работы с адресами точек будет минимально возможным. Иначе говоря, нас интересует такое расположение страниц в видеопамяти, при котором основные процедуры рисования, построения или преобразования графических объектов почти не изменяются.
Перед построением или изменением графического объекта обычно выбирается некая опорная точка, относительно которой вычисляются адреса всех остатьных точек объекта. Чаще всего это левый верхний угол прямоугольной области экрана, в которой располагается или будет расположен объект. Координаты опорной точки обычно задают в виде номера строки и столбца, на пересечении которых она находится. Но для дальнейшей работы их надо преобразовать в адрес видеопамяти.
Если видеопамять не разделена на страницы, то начало ее рабочего пространства находится на пересечении нулевой строки и нулевого столбца. В таком случае существует простая связь между координатами произвольной точки и ее адресом в видеопамяти. Адрес вычисляется как сумма двух произведений: номера строки на размер строки в байтах и номера столбца на размер кода точки в байтах. Здесь имеется в виду размер строки, отображаемой на экране (scan Line). Способы вычисления адресов описаны в разделе (режимы packed pixel graphics) разделе и (режимы direct color), a соответствующие подпрограммы приведены в примерах 3.4 и 7.3.
При делении видеопамяти на страницы вычисленное адреса точек становятся относительными. Для получения абсолютных значений адресов в этом случае при вычислениях надо учитывать адрес начала страницы или ее координаты (номера нулевой строки и нулевого столбца). Следовательно, при введении страничной организации памяти придется изменить подпрограммы, выполняющие вычисление адресов точек по их координатам. Но это не все.
В процессе работы с графическим объектом приходится вычислять адреса начала его строк. В примерах, приведенных в данной книге, для этой цели использовалась "константа переадресации", значением которой является разность между размером отображаемой на экране строки (scan Line) и шириной графического объекта, выраженной в байтах. Обе величины зависят от установленного задачей видеорежима, способ учета этой зависимости описан в разделе , а варианты соответствующих подпрограмм приведены в примере 7.3. При их составлении предполагалось, что отображаемые на экране строки начинаются с нулевого столбца.
Следовательно, если страницы расположить так, чтобы каждая из них начиналась с начала одной из строк видеопамяти, то значение константы переадресации строк не будет зависеть от номера страницы. При таком расположении страниц изменяются только подпрограммы, вычисляющие адреса точек по их координатам.
Расположение и размеры страниц
Если размер страниц равен размеру отображаемой на экране (рабочей) части видеопамяти, то каждая из них будет начинаться с новой строки. При таких размерах страницы располагаются в видеопамяти подряд друг за другом.
Если предполагается использовать N страниц, то в оперативной памяти надо выделить массив размером 2N слов. При выполнении подготовительных действий и эти слова задача должна поместить номера окон, в которых начинаются страницы и адреса нулевых строк страниц в этих окнах. Эти величины нужны для подпрограммы, выполняющей преобразование значений координат в адреса точек. Учитывая, что реальное количество страниц невелико, такой массив можно хранить в разделе данных задачи.
Оиеним возможное количество страниц. Предположим, что установленный
на видеокарте объем памяти составляет 4 Мбайт и используется видеорежим
с разрешением 640x480 точек. Если это режим packed pixel graphics, то
в видеопамяти помещается 13 страниц, в режиме Hi-color их количество сократится
в 2 раза, а в режиме True Color — в 4 раза. При более высоком геометрическом
разрешении количество страниц сокращается. Поэтому можно считать, что
оно не больше десяти.
Для определения допустимого количества страниц при выполнении задачи надо
прочитать байт массива info со смещением 10h. В нем хранится номер последней
страницы, которую можно установить в конкретном видеорежиме при имеющемся
объеме видеопамяти. Страницы нумеруют начиная с нуля, поэтому их количество
на 1 больше числа, хранящегося в байте 10b.
Кроме указанного массива, в разделе данных задачи надо выделить две переменные, например, Base_win и Base_addr для хранения исходного окна и адреса текущей страницы, с которой работает задача. В исходном состоянии эти переменные очищены, поскольку обычно работа начинается с нулевой страницы. В дальнейшем задача изменяет их значения в зависимости от номера используемой страницы.
Страница в начале окна
Особый интерес представляет случай, когда каждая страница начинается с нового окна видеопамяти. При этом базовые адреса равны нулю, и в оперативной памяти надо хранить только номера окон, с которых начинаются страницы. Кроме того, в этом случае значения абсолютных и относительных адресов отличаются только на номер окна. Поэтому после вычисления относительного адреса его надо просто увеличить на номер окна. Как это делается, показано в примерах 3.4 и 7.3.
Рекомендованные стандартом VESA видеорежимы перечислены в табл. 1.1.
Если исключить из рассмотрения текстовые и 16-цветные, то остается О режимов, которые по геометрической разрешающей способности делятся на 5 групп. В каждую группу входит 4 режима, различающихся по количеству цветов.
При работе с тремя группами видеорежимов начала страниц можно совместить с началом окон видеопамяти. В табл. 2.1 приведены размеры страниц этих групп режимов.
Таблица 2.1. Размеры страниц для трех групп видеорежимов
Разрешающая способность в точках | Количество окон (к = 1, 2, 4) |
Количество строк |
640x480 | 5*k |
512 |
1024x768 | 12*k |
768 |
1280x1024 | 20 *k |
1024 |
Во втором столбце табл. 2.1 буква k указывает количество байтов, которое занимает код точки. Это косвенная характеристика цветовой .палитры. 1 байт — 256 цветов (packed pixel graphics), 2 байта — 32К или 64К цветов (Hi-Color), 4 байта — 16М цветов (True Color).
При разрешении 1024x768 и 1280x1024 точки размер страницы совпадает с размером рабочей области видеопамяти. При разрешении 640x480 точек размер страницы на 32 строки больше размера рабочей области, т. е. по сравнению с последовательным расположением страниц теряется некоторое пространство видеопамяти, но это не имеет принципиального значения.
Из двух групп, не попавших в табл. 2.1, практический интерес представляют
видеорежимы с разрешением 800x600 точек. Целое количество строк, содержащих
800 точек, укладывается в 25 окнах, что почти в три раза превышает размер
рабочей области видеопамяти. В этом случае при совмещении начала страниц
с началом окон будет потеряно большое пространство видеопамяти. Поэтому
при разрешении 800x600 точек страницы лучше располагать в видеопамяти
последовательно друг за другом.
Заключение. Использование страниц видеопамяти расширяет возможности работы
с графикой только при решении определенного класса задач. Например, страницы
видеопамяти применяются всеми текстовыми и графическими редакторами. В
некоторых источниках встречаются указания об использовании переключения
страниц для получения спецэффектов, основанных на быстром изменении картинки
на экране. Однако не надо забывать, что требуется определенное время на
создание нужного изображения на пассивных страницах. Поэтому вопрос о
целесообразности введения страниц видеопамяти и способах работы с ними
надо решать, учитывая особенности конкретной задачи.
В примерах, приводимых в данной и последующих главах книги, многократно повторяются имена переменных, подпрограмм для работы с окнами видеопамяти и макроопределений, предназначенных для записи в стек или выталкивания из него содержимого регистров. Для того чтобы каждый раз не объяснять назначение и способы описания и определения этих имен, мы сделаем это в данном разделе.
Подпрограммы для работы с окнами. Текст подпрограмм, выполняющих установку текущего (setwin), следующего (Nxtwin) и предыдущего (prevwin) окна, приведен в примере 2.8. Здесь мы обратим ваше внимание на следующие особенности, связанные с их использованием в примерах.
Прежде всего, обычно Макроассемблер не различает заглавные и строчные буквы в именах, независимо от их назначения. Поэтому, например, имена setwin, setwin и setwin для него тождественны. Буквы разного размера используются для того, чтобы человек мог визуально различить отдельные части составных имен. Если же вы принудительно заставите Макроассемблер различать строчные и заглавные буквы, что вполне возможно, то придется использовать только одну форму записи имен.
Подпрограммы должны быть включены в текст основной программы, для того чтобы Макроассемблер мог опознать их имена и сформировать команды вызова. Возможны разные способы оформления текстов подпрограмм и их размещения в теле задачи, подробно эти вопросы рассмотрены в приложении В. В примерах основной части книги описаны подпрограммы, расположенные в кодовом сегменте, т. е. там, где находятся команды, образующие тело задачи. Обычно в исходном тексте он имеет имя Code. Место расположения подпрограмм в пределах кодового сегмента выбирается по усмотрению разработчика.
Переменная — это последовательность байтов оперативной памяти, которым присвоено уникальное имя и в которых хранятся величины, применяемые при вычислениях. В командах обычно используются переменные, содержащие 1, 2 или 4 байта. Переменные, содержащие большее количество байтов, принято называть строками, массивами или таблицами.
Переменные обычно хранятся в отдельном сегменте оперативной памяти, который принято называть сегментом данных. В исходных текстах программ его наиболее распространенным именем является Data. Для большей наглядности рекомендуется располагать сегмент данных перед кодовым сегментом. В примере 2.11 показан вариант оформления сегмента данных и описания в нем основных переменных, используемых в последующих примерах. Способ определения (формирования значений) большинства из них был описан в данной главе (см. примеры 2.1—2.6).
Пример 2.11. Описания основных переменных в сегменте данных
Data | SEGMENT | ; директива указывает начало сегмента |
OldMode | db 0 | ; исходный видеорежим |
NevMode | dw 101h | ; видеорежим VESA 101h |
Vbuff | dw 0А000h | ; адрес видеобуфера |
Horsize | dw 640 | ; количество точек в строке |
Bperline | dw 640 | ; количество байтов в строке |
Versize | dw 480 | ; количество строк на экране |
Cur win | dw 0 | ; номер текущего окна |
Cur pos | dw 0 | ; адрес (смещение) в текущем окне |
GrUnit | dw 0 | ; единица приращения номера окна |
VMC | dd 0 | ; адрес процедуры BIOS |
winB | db 0 | ; параметры окна В |
; Далее до конца сегмента располагаются другие описания | ||
Data | ENDS | ; директива указывает конец сегмента |
Описание сегмента открывает директива SEGMENT, а закрывает директива ENDS. Перед обеими директивами указывается одно и то же имя, в данном случае Data. Назначение и способы оформления сегментов описаны в приложении Б данной книги.
В примере 2.11 описаны 11 основных переменных. Каждая из них имеет уникальное имя. После имени расположены директивы db, dw или dd, указывающие тип переменной, т. е. ее размер в байтах: db — байт (8 разрядов), dw — слово (16 разрядов), dd — двойное слово (32 разряда).
После директивы указывается значение, которое Макроассемблер присваивает переменной. В примере 2.11 переменным сразу присвоены конкретные значения, но на практике они формируются в процессе выполнения задачи. Например, код сегмента видеобуфера (значение переменной vbuff) обычно АОООЬ, но возможны исключения, поэтому его значение надо выбирать из массива info.
В конце каждой строки примера 2.11 расположен комментарий, поясняющий назначение переменных и директив. Признаком начала комментария является символ "точка с запятой". Обнаружив его, Макроассемблер просто пропускает весь текст до конца строки.
Обратите внимание, в тексте примера 2.11 после всех имен отсутствует символ "двоеточие". Существует простое правило: двоеточие должно указываться только после имен меток, расположенных перед командами.
Следует отметить, что после директив описания типа может указываться не одно, а несколько значений, при этом Макроассемблер размещает эти значения в последовательно расположенных байтах, словах или двойных словах. Имя переменной в таком случае относится только к первому байту, слову или двойному слову. В отдельных случаях оно может вообще отсутствовать (см. пример 6.3). Существует специальный оператор повторения, который позволяет связать с именем переменной требуемое количество байтов, например:
Buffpal dd 256 DUP (0) ; резервирование и очистка памяти
В этом примере имя buff pal соответствует буферу размером в 256 двойных слов, содержимое которых принудительно очищается.
И последнее замечание, директива db может использоваться для описания текстовых строк, предназначенных для вывода на экран подсказок, предупреждений, аварийных и других сообщений. В этом случае расположенный после директивы текст заключается в одиночные или двойные кавычки. Вот пример оформления спецификации файла:
filspc db 'c:\tmp\current.pal', 00; описание спецификации файла
В этом примере описана спецификация файла current.pal, который находится
на диске с в каталоге ТМР. Спецификация предназначена для процедуры BIOS,
выполняющей открытие файла для чтения или записи, поэтому ее текст заканчивается
пустым байтом.
Макросы PushReg и PopReg. Подпрограммы во время выполнения не должны изменять
чужие или исходные данные. Для этого перед началом основных действий в
стеке сохраняется содержимое используемых регистров или переменных, а
перед выходом из подпрограммы восстанавливаются исходные значения сохраненных
величин. Сохранение в стеке одной величины выполняет одна команда, это
же относится и к восстановлению. Существует специальное средство, позволяющее
сократить запись повторяющихся однотипных действий и сделать ее более
наглядной. Таким средством являются макросы. Подчеркнем, что сокращается
только исходный текст, а не количество команд в теле задачи.
Понятие макрос (macro) распространяется на макроопределение (macro definition) и макроподстановку или макровызов (macro substitution). Макроопределение описывает некую заготовку текста программы, а макровызов — способ ее использования. Макроопределение существует только в исходном тексте, оно модифицируется в зависимости от указанных при вызове параметров и в измененном виде включается в тело задачи на месте каждого макровызова.
Ниже приводится пример двух простых макроопределений (пример 2.12). Их вызовы уже использовались в примерах 2.8 и 2.9, и будут неоднократно встречаться во многих примерах. Первое из них PushReg предназначено для сохранения в стеке содержимого регистров, a PopReg — для восстановления из стека ранее сохраненных регистров.
Пример 2.12. Описание макроопределений
PushReg и PopReg
В данной главе приведены первые примеры программ, поэтому имеет смысл поговорить о принятом в книге оформлении текстов примеров, об описании используемых в них переменных и о некоторых общих вопросах, связанных с оформлением программ на языке ассемблера. Если вы владеете ассемблером и имеете опыт программирования на этом языке, то данный раздел можно пропустить без ущерба для понимания излагаемого в последующих главах материала. Начинающим рекомендуем обратиться к специальной литературе, а при наличии доступа к Internet посетите сайт www.assembier.ru. Данная книга не является руководством по программированию на языке ассемблера, она содержит лишь минимум сведений, необходимый для понимания действий, выполняемых в примерах.
Ассемблер — это язык команд семейства компьютеров, в котором коды инструкций и операндов заменены мнемоническими обозначениями, т. е. именами. Используемые в ассемблерных программах имена делятся на две категории — зарезервированные и выбираемые по усмотрению разработчика.
К зарезервированным относятся имена регистров, операций, директив, операторов
и некоторые другие. Их нельзя изменять или использовать не по назначению,
в противном случае при компиляции программы будет выдано сообщение об
ошибке.
По усмотрению программиста выбираются имена меток, констант, переменных,
структур данных, макросов, сегментов программ и некоторые другие. Желательно,
чтобы имена несли смысловую нагрузку, т. е. указывали назначение величин,
к которым они относятся, и легко читались.
В именах можно использовать заглавные и строчные буквы латинского алфавита, цифры и те символы, которые не имеют специального назначения. К последним относятся: пробел, запятая, двоеточие, точка с запятой, все виды скобок и знаки арифметических операций. По умолчанию, т. е. если явно не указано обратное, ассемблер не различает в именах заглавные и строчные буквы. Поэтому не имеет значения заглавными или строчными буквами набрано имя, или в нем чередуются те и другие.
Для большей наглядности текста отдельные части составных имен могут выделяться заглавными буквами, например oidMode, или отделяться друг от друга нижней чертой, например cur_win. He следует увлекаться слишком длинными именами, поскольку теряется наглядность и повышается вероятность ошибки при их наборе.
Команда — это элементарная единица любой ассемблерной программы, исполняемая процессором ПК. Нас интересует язык ассемблера для микропроцессоров Intel, поскольку на их основе собираются компьютеры семейства IBM PC. В записи на этом языке команда состоит из условного обозначения операции и операндов, количество которых изменяется от 0 до 3 (чаще всего 1 или 2). Операнды отделяются от операции пробелами, а друг от друга запятой и пробелами. Наличие запятой обязательно, а количество пробелов не ограничивается, поэтому вы можете оформлять текст своей программы так, как сочтете нужным.
Перед командой может находиться метка, имя которой заканчивается символом "двоеточие". Метки являются операндами команд передачи управления — условных и безусловных переходов и обращений к подпрограммам.
После команды может записываться комментарий, который отделяется от нее символом "точка с запятой". Обнаружив этот символ в текущей строке. Макроассемблер просто пропускает весь текст до конца строки. Поэтому его можно использовать только как признак последующего комментария, а в комментарии допустимы любые символы.
Вернемся к командам. Операции имеют зарезервированные имена и их изменения недопустимы. Ассемблер формирует код машинной инструкции исходя из имени операции и результатов анализа операндов. Поэтому одной операции может соответствовать несколько разных кодов машинных инструкций. Например, имя mov обозначает операцию пересылки, которую выполняют 8 разных инструкций микропроцессора Intel 80386.
В качестве операндов могут использоваться имена регистров, констант, переменных, меток и выражения, составленные из перечисленных величин.
Регистры являются внутренними устройствами микропроцессора, т. е. они входят в его состав, поэтому обращение к ним происходит быстрее, чем к оперативной памяти. Начиная с Intel 8086, все модели микропроцессоров содержат 16-разрядные регистры, имеющие следующие имена:
/1/АХ, ВХ, СХ, DX, DI, SI, ВР, SP, CS, DS, ES, SS.
Первые четыре имени списка /1/ относятся к регистрам общего назначения. При выполнении задач в них обычно находятся сами операнды, а не их адреса. Исключением является регистр вх, в котором может храниться адрес операнда. Каждый из регистров общего назначения делится на два независимых байта. Старшим байтам соответствуют имена АН, вн, сн, он, а младшим байтам — AL, BL, CL, DL.
Имена DI, SI, BP, SP относятся к регистрам-указателям.
Обычно они содержат адреса оперативной памяти, в которых хранятся операнды.
В таких случаях при записи команды имя регистра заключается в квадратные
скобки. Регистры-указатели не делятся на байты, поскольку адрес не может
быть 8-разрядным. DI и SI
обычно используются при работе с данными, поэтому по умолчанию в качестве
сегментного регистра процессор выбирает DS. Специально для работы со стеком
предназначены ВР и SP, поэтому по умолчанию в качестве сегментного регистра
используется ss.
Существует специальный регистр, содержащий адрес очередной выполняемой
команды. В русскоязычной литературе его называют счетчиком команд, а в
англоязычной — указателем инструкций (IP). В явном виде он не указывается
ни в одной команде, поэтому его имя отсутствует в списке
/1/. Тем Не менее, все команды передачи управления изменяют содержимое
IP.
Последняя четверка имен списка /1/ соответствует сегментным регистрам. Они предназначены для хранения старшей части адресов операндов или команд. Содержимое сз (сегмент кодов) процессор использует при выборке очередной команды. Содержимое DS (сегмент данных) — при чтении и записи операндов. В ss хранится сегмент оперативной памяти, отведенный для стека. Регистр ES используется строковыми командами при записи результата, в остальных случаях программист может распоряжаться им по своему усмотрению. Во всех примерах, приводимых в данной книге, регистр ES используется при обращениях к видеопамяти, поэтому в нем должно находиться значение сегмента видеопамяти (видеосегмента).
В записях операндов имя сегментного регистра предшествует адресу и обязательно заканчивается символом "двоеточие". Адресами, чаще всего, являются имена меток или переменных и индексные выражения, но допустимо и явное указание адреса в виде конкретного числа.
Начиная с модели Intel 386, регистры общего назначения и указатели расширены до 32-х разрядов. Размеры сегментных регистров не изменились, но добавились два новых. Новые имена перечислены в списке /2/.
/2/
ЕАХ, ЕВХ, ЕСХ, EDX, EDI, ESI, EBP, ESP, FS, GS.
Расширение регистров и введение новых имен никак рне отразилось на назначении и возможности использования старых. Просто 16-разрядные регистры стали младшими словами 32-разрядных регистров. Однако старшие слова 32-разрядных регистров самостоятельно не существуют и поэтому не имеют собственных имен.
Одновременно с введением новых регистров был расширен набор способов адресации операндов. Адрес может находиться в любом из 32-разрядных регистров, а ЕАХ, ЕВХ, ЕСХ и EDX могут использоваться в индексных выражениях.
Поскольку полный адрес помещается в 32-разрядных регистрах, то сегментные регистры просто не нужны, именно поэтому их разрядность не была увеличена. Таким образом, сегментные регистры являются атрибутом вычислений с использованием 16-разрядных адресов операндов.
Кроме регистров, перечисленных в списках /1/ и /2/, существуют еще специальные регистры, используемые в системных задачах, программирование которых не рассматривается в данной книге.
Константы — это постоянные величины, которым присвоены определенные имена. Они описываются (или определяются) с помощью операторов присваивания, имеющих следующую структуру:
<имя константы> = арифметическое или логическое выражение>
Простейший пример такого оператора CR = 0Dh. Если его включить в текст программы, то при компиляции все имена CR будут заменены на код ooh (аббревиатура CR расшифровывается как "возврат каретки" и ей соответствует код 0Dh). В более сложных случаях в выражениях кроме чисел могут использоваться имена констант и переменных, символы, обозначающие арифметические или логические операции, и круглые скобки для указания порядка выполнения вычислений.
При программировании на ассемблере константы могут быть только целыми числами, поэтому операторы типа PI = 3.14 вызовут сообщение об ошибке. Не все операции допускают использование констант, например, константу нельзя записать в сегментный регистр с помощью операции пересылки (mov).
Более общей формой описания констант является использование директивы EQU вместо знака равенства. Если справа (после директивы) указано арифметическое или логическое выражение, то обе директивы (EQU и =) равноценны. Однако после EQU можно записать символьное выражение, которое Макроассемблер будет подставлять вместо имени константы при каждом ее использовании в программе. Кроме того, после EQU можно указать произвольный текст, заключенный в угловые скобки. В таком случае имя константы будет соответствовать указанному тексту (за исключением угловых скобок).
Ассемблер не выделяет специального места в теле задачи для хранения констант. При каждом обнаружении имени константы он просто подставляет в формируемую команду соответствующее значение, символьное выражение или строку текста. Этим константы отличаются от переменных.
При компиляции программы (при ее преобразовании в объектный код) Макроассемблер преобразует имена переменных в относительные адреса. Следовательно, должна существовать некая точка отсчета, адрес которой можно принять за нуль. Такой точкой является начало сегмента. В памяти ПК сегмент — это произвольно выбранный участок адресов, размер которого не меньше чем 16 и не больше чем 65 536 байтов. Наименьшее значение объясняется принятым на IBM PC способом вычисления полного адреса, а наибольшее соответствует предельному значению числа, которое может быть записано в 16-разрядный регистр.
Формально в тексте программы должен быть описан хотя бы один сегмент, в противном случае вычисление адресов будет невозможно и Макроассемблер выдаст сообщение об ошибке. Существует специальный класс задач, при программировании которых можно ограничиться одним сегментом. Это так называемые резидентные задачи, которые постоянно находятся в памяти К. Типичным примером являются загружаемые драйверы. Обычно при работе в ПК загружено несколько резидентных задач. Поэтому, чем меньше места в памяти занимает такая задача, тем лучше.
В задачах среднего размера, обычно используется три сегмента для размещения данных, команд и стека. Они ассоциируются с тремя сегментными регистрами DS, CS и SS, используемыми процессором при выполнении команд. Выделение нескольких сегментов в структуре программы преследует две основные цели. Во-первых, ее текст становится более удобочитаемым. Во-вторых — это один из способов увеличения пространства памяти, отведенного для задачи. Вопросы сегментирования программ и распределения памяти обсуждаются в приложении Б данной книги. Здесь мы попробуем разобраться с тем, как устанавливается связь между сегментами программы и соответствующими регистрами процессора.
Любая задача должна иметь явно описанную точку входа, в противном случае DOS не сможет начать ее выполнение. Точкой входа является метка первой выполняемой команды задачи, кроме того, ее имя указывается после директивы END, которой заканчивается текст программы. По этому имени Макроассемблер находит сегмент, в котором описана метка, и значение этого сегмента будет записано в регистр cs перед пуском задачи. Отметим, что определить точные значения сегментов может только DOS при загрузке задачи для выполнения, поскольку именно в этот момент известно распределение памяти ПК и ее доступное пространство. Таким образом, сегмент, содержащий команды, опознается Макроассемблером независимо от присвоенного ему в программе имени.
Для ассоциирования других сегментов с соответствующими регистрами процессора надо либо использовать специальные директивы при их описании, либо загружать значения сегментов в регистры в процессе выполнения задачи. Покажем, как записать в регистр данных значение сегмента, описанного в примере 2.11.
; точка входа в задачу, имя start надо указать после последнего
end.
start: mov ax, data ; запись значения сегмента в ах
mov ds, ax ; копирование ах в ds
; продолжение программы
Первая команда этого фрагмента записывает значение указанного сегмента в регистр ах, а вторая переписывает содержимое ах в ds. Промежуточный регистр ах нужен потому, что имя сегмента (data) является константой и его нельзя записать в сегментный регистр с помощью операции пересылки. После выполнения этих двух команд процессор "знает", где находятся операнды команд. Таким способом можно записать в любой сегментный регистр значение нужного сегмента.
Вне сегментов, т. е. в начале текста программы располагаются директивы, которые используются только при компиляции. К ним относятся описание констант, о котором говорилось выше, и макроопределения.
Макроопределения и макровызовы объединяет одно общее понятие "Макросы".
Это средство для сокращения исходных текстов программ (но не задач), придания
ему большей наглядности и упрощения процесса программирования. В комплект
поставки Макроассемблера входит несколько специальных файлов, содержащих
различные полезные макроопределения. Эти файлы обычно имеют тип (расширение)
inc. Если при установке ком-, пилятора создается каталог include, то они
располагаются в нем.
В данном разделе автор попытался ответить на часть вопросов, которые могут
возникнуть у читателя при изучении приведенных в книге примеров. По мере
изложения материала будут описаны особенности выполнения некоторых команд,
использованных в примерах. Многие из приведенных в книге примеров оформлены
в виде подпрограмм. Поэтому приложение В специально посвящено вопросам,
связанным с разработкой и использованием подпрограмм.