драйверов прерывания или рекурсивных программ.
Во время выполнения программы размещение ее в памяти отража-
ется двумя путями. Во первых, для связи счетчика программы (также
называемого как указатель инструкции) или адреса ссылки памяти с
блоком физической памяти используются регистры сегмента. Затем
внутри этого блока формируется действительная ссылка, используя
смещение от начала этого блока. Это смещение появляется в счетчи-
ке программы, в ссылках на память и внутри косвенных ссылок на
память через регистры.
Что это означает для выполнения программ с различными типами
кодирования? Эти типы ссылок и случаи, когда они используются,
определяют как программа загружается в память, какие особенности
она может использовать и как программа может быть структурирова-
на. Рассмотрим как создаются эти ссылки и как их использовать для
создания более совершенных программ.
- 2-21 -
Размещение программного кода в памяти
Понимание альтернатив размещения программного кода в памяти
требует ясного понимания работы как инструкций управления выпол-
нением программы (CALL - вызов процедуры, RET - возврат из проце-
дуры и JMP - безусловный переход), так и доступа к памяти микро-
процессора 8086, так как это прежде всего ограничивает возможнос-
ти программиста при размещении программы в доступном пространстве
памяти.
Инструкции управления выполнением программы часто называют
инструкциями передачи управления, которые включают две основные
инструкции CALL - вызов процедуры и JMP - безусловный переход.
Каждый случай, когда программа начинает выполнение с нового места
в памяти, называется "пунктом назначения". Каждая из этих функций
имеет три опции реализации для указания пункта назначения. Этими
опциями являются: текущее относительное размещение, адресация от-
носительно текущего сегмента и абсолютная адресация.
Относительное размещение
Текущее относительное размещение иногда называют относитель-
ным PC (program counter) счетчиком программы, который вычисляет
адрес пункта назначения от текущего адреса и смещение. Для форми-
рования адреса пункта назначения смещение добавляется к текущему
размещению. В связи с тем, что полная операция в целом не зависит
от абсолютного расположения программного кода в памяти, результи-
рующий адрес имеет независимое размещение. Если в памяти пересы-
лается целый блок программы, то созданный скорректированный адрес
пункта назначения указывает на новое положение инструкции пункта
назначения.
Этот способ вычисления адреса передачи используется во всех
инструкциях условного перехода, во всех внутрисегментных (корот-
ких или близких) инструкциях JMP (безусловный переход) и во всех
внутрисегментных (близких) инструкциях CALL (вызов процедуры).
"Непосредственная" означает, что инструкция (JMP или CALL) содер-
жит смещение как непосредственные данные. Напротив, "косвенная"
(непрямая) инструкция - это инструкция (JMP или CALL) для адреса,
содержащегося в 16-битовом регистре (только смещение), или для
адреса, содержащегося в 16-битовой или 32-битовой ячейке памяти
(смещение или смещение и сегмент). В связи с тем, что прямые пе-
редачи управления не включают действительные адреса, то они могут
быть размещены в памяти где угодно и даже могут быть пересланы
внутри сегмента, пока исходные инструкции (JMP и CALL) и програм-
ма пункта назначения пересылаются совместно.
Адресация относительно текущего сегмента
Адресация относительно текущего сегмента указывает на значе-
ние действительного смещения для загрузки в указатель инструкции
(как при косвенной инструкции CALL - вызвать процедуру) или для
использования в качестве указателя данных. Ссылки, выполняемые
этим способом, всегда указывают на ту же самую ячейку внутри бло-
ка памяти, адресуемого с помощью соответствующего регистра сег-
мента. Как таковые, программные коды или данные не могут быть пе-
ресланы внутри сегмента. Однако, такие программные коды могут
быть пересланы в памяти, если регистр сегмента для этого блока
- 2-22 -
также изменен. Т.к. сегменты должны быть выровнены на границу па-
раграфа (шестнадцатиричный адрес XXXX0), то программный код может
быть переслан только путем прибавления 16 байтов (один параграф).
Этот тип адресации используется внутри сегментными (близкими)
косвенными инструкциями JMP - безусловный переход и CALL - вызов
процедуры, где новое значение указателя инструкции пункта назна-
чения выбирается из регистра или ячейки памяти. Эта адресация
также используется во всех ссылках на данные независимо от ис-
пользуемого сегмента (DS, ES или SS). Код, использующий этот тип
ссылок, рассматривается еще как переместимый, пока обновляются
регистры сегмента для отражения позиции программного кода.
Абсолютная адресация
Абсолютная адресация выполняется в тех случаях, когда явно
указывается адрес физической памяти. Для выполнения абсолютной
адресации в семействе микропроцессоров 8086 необходимо явно ука-
зать адрес сегмента и смещение. Эта ссылка каждый раз указывает
на одну и ту же ячейку памяти. Абсолютная адресация в микропро-
цессоре 8086 используется редко. Только несколько инструкций мик-
ропроцессора 8086 имеют способность генерировать абсолютные адре-
са. Этими инструкциями являются : внутрисегментные (далекие)
инструкции JMP - безусловный переход и CALL - вызов процедуры, а
также инструкции LDS и LES (загрузка указателя, используя DS или
ES). Инструкции JMP и CALL (непосредственные или косвенные) об-
новляют не только смещение (указатель инструкции), но и регистр
сегмента кода (CS). Он указывает физический адрес памяти. В свою
очередь, инструкции LDS и LES не только загружают смещение в 16-
битовый регистр, но и загружают либо регистр сегмента данных
(DS), либо регистр дополнительного сегмента (ES). Опять это физи-
ческий адрес памяти.
Другим способом создания абсолютного адреса является исполь-
зование инструкции MOV - пересылка и POP - извлечь из стека для
непосредственной загрузки константы в один из регистров сегмента.
Заметим однако, что значение, пересылаемое с помощью инструкции
POP в регистр CS, в процессорах iAPX186, iAPX188, или iAPX286 не-
допустимо и не должно выполняться, если только по причине совмес-
тимости.
Типы программного кода
При обсуждении свойств программы рассматривался тип сложности
ее адресации. Если программа содержит лишь одну абсолютную ссыл-
ку, то такая программа называется программой, имеющей абсолютную
адресацию, или неперемещаемая. Ее нельзя перемещать в памяти.
Внимательные читатели могут подумать о том, что допущена
ошибка. В конце концов точка входа в программу макроассемблера
(MASM) указывается как far (далекий) - и все. Выполнимые програм-
мы с расширением .EXE загружают регистры DS и ES по инструкции
MOV - переслать. Оба этих факта, как кажется, подразумевают непе-
реместимую программу, но операционная система MS-DOS выполняет
загрузку программы в память по различным адресам как требуется.
Ключом к этой дилемме является то, что используемые значения не
являются константами в MS-DOS. Макроассемблер MASM и компоновщик
LINK обращаются с именами сегментов и именем процедуры far (дале-
кий) специальным способом обработки, который называется
relocation map (схема настройки). При загрузке программы в память
MS-DOS читает схему настройки и изменяет значения тех ссылок, ко-
торые содержат адреса сегментов. Для программистов важно заме-
тить, что MS-DOS не расширяет это правило для стандартных значе-
ний данных, и загрузка одного из регистров сегмента константой
это не то же самое, что использование имени сегмента или имени
процедуры far (далекий).
Переместимый код
Макроассемблер MASM и компоновщик LINK обычно вырабатывают
перемещаемые программы. Т.е. при нормальном использовании они
создают такие программы, которые могут быть перемещены в памяти с
помощью MS-DOS, и при этом правильно функционировать. Изменится
только содержимое регистров сегментов. Это свойство используется
множеством прикладных программ. Одни программы могут загружать
другие программы, используя функцию 4Вh (полезную для оверлейных
- перекрываемых программ). Несколько программ могут быть загруже-
ны в память одновременно (полезно для мультизадачных систем или
программ резидентной памяти, таких как, например, программы пред-
варительной подкачки данных для печати).
Как уже указывалось, MS-DOS выполняет эту возможность путем
изменения только значений регистров и таких мест в программе, ко-
торые ссылаются на имя сегмента или имя процедуры far (далекий).
Можно также расширить эти концепции гибкости на области данных,
используемые программой. Обычно перемещаемые программы содержат и
перемещаемые области данных. Когда загрузчик MS-DOS помещает
программу в память, он назначает значения для всех ссылок на сег-
менты раньше ссылок на программные сегменты. Листинг 2-5, полу-
ченный из программного файла .EXE стандартного типа, показывает
ссылку на сегмент данных, используемую для загрузки регистра сег-
мента данных. Листинг 2-6 показывает эквивалентную программу, по-
лученную с помощью макроассемблера MASM.
Листинг 2-5. Исходный код для заголовка программы .EXE
-----------------------------------------------------------------
data_seg SEGMENT ; определение сегмента данных
... ; значения и области данных
data_seg ENDS
code_seg SEGMENT ; определение кодового сегмента
ASSUME cs:code_seg
ASSUME ds:data_seg
main PROC FAR ; точка входа в программу
start:
mov ax,data_seg ; передача адреса сегмента данных
mov ds,ax ; ... в AX и оттуда в ...
mov es,ax ; ... регистры сегмента
... ...
-----------------------------------------------------------------
При стандартном использовании переменная data_seg не является
константой.Скорее, эта переменная является перемещаемым значением
сегмента, которое указано в листинге 2-6 макроассемблера MASM че-