ПОИСК
Категории книг
ОПРОС
Вопрос: Какой язык программирования вы предпочитаете
С/C++
Delphi
Visual Basic
Perl
Java
PHP
ASP
Другой
ЭТО ПОЛЕЗНО!
ОБРАТНАЯ СВЯЗЬ

/ Главная / Программирование / Иллюстрированный самоучитель по SVGA
Иллюстрированный самоучитель по SVGA

  

Часто используемые в примерах имена

В примерах, приводимых в данной и последующих главах книги, многократно повторяются имена переменных, подпрограмм для работы с окнами видеопамяти и макроопределений, предназначенных для записи в стек или выталкивания из него содержимого регистров. Для того чтобы каждый раз не объяснять назначение и способы описания и определения этих имен, мы сделаем это в данном разделе.

Подпрограммы для работы с окнами. Текст подпрограмм, выполняющих установку текущего (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
; Сохранение в стеке регистров, перечисленных в списке гeg
PushReg macro reg ; заголовок макроопределения
  irp r,<reg> ; начало оператора повторения
push r
заготовка повторяемой команды
endm
конец оператора повторения
endm
конец макроопределения
BO(
:становление из
стека регистров, перечисленных в списке reg.
PopReg
macro reg
заголовок макроопределения
irp r,<reg>
оператор повторения
pop r
заготовка повторяемой команды
endm
конец оператора повторения
endni
конец макроопределения

Макросы примера 2.12 различаются только заготовкой повторяемой команды. В одном случае это запись в стек, а в другом — выталкивание из него.
При оформлении макросов используются специальные директивы. Текст любого макроопределения начинается директивой macro, перед ней указывается имя макроса, а после нее, в той же строке, список аргументов, если таковые имеются, в данном примере это reg.

Другая, часто используемая директива — endm. В зависимости от контекста она указывает конец макроопределения или оператора, что и показано в примере 2.12.
Тела макроопределений примера 2.12 состоят из директивы повторения irp. После нее, в той же строке, указываются параметр г и имя списка аргументов, заключенное в угловые скобки. Оно должно совпадать с именем, указанным в директиве macro. В следующей строке записывается повторяемая команда, один из операндов которой г соответствует параметру директивы irp.

В общем случае тело директивы irp может состоять из нескольких команд или содержать другие директивы, поэтому нужен признак конца директивы endm.
Обнаружив в тексте программы макроопределение, Макроассемблер проверяет его синтаксис и запоминает имя и текст, не включая его в тело задачи. Исполнение макроопределения (вставка команд в тело задачи) будет производиться при каждом макровызове.

Параметры макровызова

Макровызовы или макрокоманды — это вставка текста макроопределения в нужных местах программы с подстановкой конкретных параметров, если они имеются. Для макроопределений примера 2.12 макровызов состоит из имени PushReg или PopReg и списка регистров, заключенного в угловые скобки. Угловые скобки позволяют использовать запятые между именами регистров, входящих в список.

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

В частности, при вызове макроопределений примера 2.12 директива irp повторяется столько раз, сколько имен содержит список reg. Название каждого имени выбирается из списка и подставляется в команду push или pop вместо параметра r.

В макроопределениях примера 2.12 разнообразие имен не ограничено явно, допустимо указание любых величин, которые могут быть операндами команд push и pop. Ими могут быть не только имена 16- и 32-разрядных регистров, но и имена переменных, кроме того, команда push может сохранять в стеке значения констант (непосредственное указание сохраняемой величины). Все эти величины можно использовать в макровызове. Например, во многих подпрограммах будет использоваться такой вариант макровызовов:

PushReg <fs, gs, Cur_win> И PopReg <Cur_win, gs, fs>

Обратите внимание, имена в списках PushReg и PopReg расположены в обратном порядке, поскольку работа со стеком организована по принципу "последнее записанное — первое считанное".

Альтернативным способом сохранения группы регистров в стеке и восстановления их оттуда одной командой являются операции pusha и рора, не имеющие параметров. Их исполняют все модели микропроцессоров, кроме Intel 8086. Эти операции сохраняют в стеке или восстанавливают из него регистры в таком порядке:

PUSHA -> ах, ex, dx, bx, sp, bp, si, di.
РОРА -> di, si, bp, sp, bx, dx, ex, ax.

Начиная с модели Intel 80386, микропроцессоры поддерживают операции pushad и popad, также не имеющие параметров. Они сохраняют в стеке или восстанавливают из него 32-разрядные регистры в таком порядке:

PUSHAD-> eax, ecx, edx, ebx, esp, ebp, esi, edi.
POPAD -> edi, esi, ebp, esp, ebx, edx, ecx, eax.

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

  


Компьютерные книги © 2006-2013
computers.plib.ru