ASSUME CS:CSEG,DS:CSEG
ASSUME SS:CSEG,ES:CSEG
0100 ORG 100H
0100 EB 00 BEGIN: JMP SHORT MAIN
; ...
0102 MAIN PROC NEAR
.LALL
MOVIF B
+ IFIDN ,
0102 F3/A4 + REP MOVSB
+ EXITM
MOVIF W
+ ENDIF
+ IFIDN ,
0104 F3/A5 + REP MOVSW
+ ENDIF
MOVIF
+ ENDIF
+ ELSE
+ ;Не указан парам. B или W, по умолч.принято B
+ ;--------------------------------------------
0106 F3/A4 + REP MOVSB
+ ENDIF
0108 C3 RET
0109 MAIN ENDP
0109 CSEG ENDS
END BEGIN
__________________________________________________________________________
Рис.20.7. Использование директивы IFIDN
ОСНОВНЫЕ ПОЛОЖЕНИЯ НА ПАМЯТЬ
________________________________________________________________
- Макросредства возможны только для полной версии ассемблера (MASM).
- Использование макрокоманд в программах на ассемблере дает в
результате более удобочитаемые программы и более производительный код.
- Макроопределение состоит из директивы MACRO, блока из одного или
нескольких операторов, которые генерируются при макрорасширениях и
директивы ENDM для завершения определения.
- Код, который генерируется в программе по макрокоманде, представляет
собой макрорасширение.
- Директивы .SALL, .LALL и .XALL позволяют управлять распечаткой
комментариев и генерируемого объектного кода в макрорасширении.
- Директива LOCAL позволяет использовать имена внутри
макроопределений. Директива LOCAL кодируется непосредственно после
директивы MACRO.
- Использование формальных параметров в макроопределении позволяет
кодировать параметры, обеспечивающие большую гибкость макросредств.
- Библиотека макроопределений дает возможность использовать
макрокоманды для различных ассемблерных программ.
- Условные директивы позволяют контролировать параметры макрокоманд.
ВОПРОСЫ ДЛЯ САМОПРОВЕРКИ
________________________________________________________________
20.1. Напишите необходимые директивы: а) для подавления всех команд,
которые генерирует макрокоманда и б) для распечатки только команд,
генерирующих объектный код.
20.2. Закодируйте два макроопределения для умножения: а) MULTBY
должна генерировать код для умножения байта на байт; б) MULTWD должна
генерировать код для умножения слова на слово. Для множителя и множимого
используйте в макроопределении формальные параметры. Проверьте выполнение
макрокоманд на небольшой программе, в которой также определены необходимые
области данных.
20.3. Запишите макроопределения из вопроса 20.2 в "макробиблиотеку".
Исправьте программу для включения элементов библиотеки по директиве
INCLUDE в первом проходе ассемблирования.
20.4. Напишите макроопределение BIPRINT, использующей BIOS INT 17H
для печати. Макроопределение должно включать проверку состояния принтера и
обеспечивать печать любых строк любой длины.
20.5. Измените макроопределение на рис.20.6 для проверки делителя на
ноль (для обхода деления).
ГЛАВА 21 Компоновка программ
__________________________________________________________________________
Ц е л ь: Раскрыть технологию программирования, включающую компоновку
и выполнение ассемблерных программ.
ВВЕДЕНИЕ
________________________________________________________________
Примеры программ в предыдущих главах состояли из одного шага
ассемблирования. Возможно, однако, выполнение программного модуля,
состоящего из нескольких ассемблированных программ. В этом случае
программу можно рассматривать, как состоящую из основной программы и одной
или более подпрограмм. Причины такой организации программ состоят в
следующем:
- бывает необходимо скомпоновать программы, написанные на разных
языках, например, для объединения мощности языка высокого уровня и
эффективности ассемблера;
- программа, написанная в виде одного модуля, может оказаться
слишком большой для ассемблирования;
- отдельные части программы могут быть написаны разными группами
программистов, ассемблирующих свои модули раздельно;
- ввиду возможно большого размера выполняемого модуля, может
появиться необходимость перекрытия частей программы в процессе
выполнения.
Каждая программа ассемблируется отдельно и генерирует собственный
уникальный объектный (OBJ) модуль. Программа компоновщик (LINK) затем
компонует объектные модули в один объединенный выполняемый (EXE) модуль.
Обычно выполнение начинается с основной программы, которая вызывает одну
или более подпрограмм. Подпрограммы, в свою очередь, могут вызывать другие
подпрограммы.
На рис.21.1 показаны два примера иерархической структуры основной
подпрограммы и трех подпрограмм. На рис.21.1.(а) основная программы
вызывает подпрограммы 1, 2 и 3. На рис.21.1.(б) основная программа
вызывает подпрограммы 1 и 2, а подпрограмма 1 вызывает подпрограмму 3.
Существует много разновидностей организации подпрограмм, но любая
организация должна быть "понятна" и ассемблеру, и компоновщику, и этапу
выполнения. Следует быть внимательным к ситуациям, когда, например, под
программа 1 вызывает подпрограмму 2, которая вызывает подпрограмму 3 и,
которая в свою очередь вызывает подпрограмму 1. Такой процесс, известный
как рекурсия, может использоваться на практике, но при неаккуратном
обращении может вызвать любопытные ошибки при выполнении.
__________________________________________________________________________
a) ЪДДДДДДДДДДД¬ б) ЪДДДДДДДДДДД¬
¦ Основная ¦ ¦ Основная ¦
¦ программа ¦ ¦ программа ¦
АДДДДДВДДДДДЩ АДДДДДВДДДДДЩ
ЪДДДДДДДДД†ДДДДДДДДД¬ ЪДДДДБДДДД¬
ЪДДДДДД¬ ЪДДДДДД¬ ЪДДДДДД¬ ЪДДДДДД¬ ЪДДДДДД¬
¦ П/П 1 ¦ ¦ П/П 2 ¦ ¦ П/П 3 ¦ ¦ П/П 1 ¦ ¦ П/П 2 ¦
АДДДДДДДЩ АДДДДДДДЩ АДДДДДДДЩ АДДДВДДДЩ АДДДДДДДЩ
ЪДДДДДД¬
¦ П/П 3 ¦
АДДДДДДДЩ
__________________________________________________________________________
Рис.21.1. Иерархия программ.
МЕЖСЕГМЕНТНЫЕ ВЫЗОВЫ
________________________________________________________________
Команды CALL в предыдущих главах использовались для внутрисегментных
вызовов, т.е. для вызовов внутри одного сегмента. Внутрисегментный CALL
может быть короткий (в пределах от +127 до -128 байт) или длинный (
превышающий указанные границы). В результате такой операции "старое"
значение в регистре IP запоминается в стеке, а "новый" адрес перехода
загружается в этот регистр.
Например, внутрисегментный CALL может иметь следующий объектный код:
E82000. Шест.E8 представляет собой код операции, которая заносит 2000 в
виде относительного адреса 0020 в регистр IP. Затем процессор объединяет
текущий адрес в регистре CS и относительный адрес в регистре IP для
получения адреса следующей выполняемой команды. При возврате из процедуры
команда RET восстанавливает из стека старое значение в регистре IP и
передает управление таким образом на следующую после CALL команду.
Вызов в другой кодовый сегмент представляет собой межсегментный
(длинный) вызов. Данная операция сначала записывает в стек содержимое
регистра CS и заносит в этот регистр адрес другого сегмента, затем
записывает в стек значение регистра IP и заносит новый относительный адрес
в этот регистр.
Таким образом в стеке запоминаются и адрес кодового сегмента и
смещение для последующего возврата из подпрограммы.
Например, межсегментный CALL может состоять из следующего объектного
кода:
9A 0002 AF04
Шест.9A представляет собой код команды межсегментного вызова которая
записывает значение 0002 в виде 0200 в регистр IP, а значение AF04 в виде
04AF в регистр CS. Комбинация этих адресов указывает на первую выполняемую
команду в вызываемой подпрограмме:
Кодовый сегмент 04AF0
Смещение в IP 0200
Действительный адрес 04CF0
При выходе из вызванной процедуры межсегментная команда возврата REP
восстанавливает оба адреса в регистрах CS и IP и таким образом передает
управление на следующую после CALL команду.
АТРИБУТЫ EXTRN и PUBLIC
________________________________________________________________
Рассмотрим основную программу (MAINPROG), которая вызывает
подпрограмму (SUBPROG) с помощью межсегментного CALL, как показано на
рис.21.2.
Команда CALL в MAINPROG должна "знать", что SUBPROG существует вне
данного сегмента (иначе ассемблер выдаст сообщение о том, что
идентификатор SUBPROG не определен). С помощью директивы EXTRN можно
указать ассемблеру, что ссылка на SUBPROG имеет атрибут FAR,
т.е.определена в другом ассемблерном модуле. Так как сам ассемблер не
имеет возможности точно определить такие ссылки, он генерирует "пустой"
объектный код для последующего заполнения его при компановке:
9A 0000 ---- E
Подпрограмма SUBPROG содержит директиву PUBLIC, которая указывает
ассемблеру и компоновщику, что другой модуль должен "знать" адрес SUBPROG.
В последнем шаге, когда оба модуля MAINPROG и SUBPROG будут успешно
ассемблированы в объектные модули, они могут быть скомпонованы следующим
образом:
Запрос компоновщика LINK: Ответ:
Object Modules [.OBJ]: B:MAINPROG+B:SUBPROG
Run File [filespec.EXE]: B:COMBPROG (или другое имя)
List File [NUL.MAP]: CON
Libraries [.LIB]: [return]
Компоновщик устанавливает соответствия между адресами EXTRN в одном
объектном модуле с адресами PUBLIC в другом и заносит необходимые
относительные адреса. Затем он объединяет два объектных модуля в один
выполняемый. При невозможности разрешить ссылки компоновщик выдает
сообщения об ошибках. Следите за этими сообщениями прежде чем пытаться
выполнить программу.
__________________________________________________________________________
ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД¬
¦ EXTRN SUBPROG:FAR ¦
¦ MAINPROG: . ¦
¦ . ¦
¦ CALL SUBPROG ¦
¦ . ¦
¦ . ¦
ГДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ
¦ PUBLIC SUBPROG ¦