Смекни!
smekni.com

ЯЗЫК МАКРОАССЕМБЛЕРА IBM PC (стр. 6 из 9)

1.4.5 Начальная загрузка сегментных регистров

Директива ASSUME сообщает ассмеблеру о том, по каким регистрам он должен сегментировать имена из каких сегментов, и "обещает", что в этих регистрах будут находиться начальные адреса этих сегментов. Одна­ко загрузку этих адресов в регистры сама директива не осуществляет. Сделать такую загрузку - обязанность самой программы, с загрузки сег­ментных регистров и должно начинаться выполнение программы. Делается это так.

Поскольку в ПК нет команды пересылки непосредственного операнда в сегментный регистр (а имя, т.е. начало, сегмента - это непосредствен­ный операнд), то такую загрузку приходится делать через какой-то дру­гой, несегментный, регистр (например, AX):

MOV AX,DT1 ;AX:=начало сегмента DT1

MOV DS,AX ;DS:=AX

Аналогично загружается и регистр ES.

Загружать регистр CS в начале программы не надо: он, как и счетчик команд IP, загружается операционной системой перед тем, как начинается выполнение программы (иначе нельзя было бы начать ее выполнение). Что же касается регистра SS, используемого для работы со стеком, то он мо­жет быть загружен так же, как и регистры DS и ES, однако в MASM преду­смотрена возможность загрузки этого регистра еще до выполнения прог­раммы (см. 1.7).

1.4.6 Ссылки вперед

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

В подобной ситуации ассемблер действует следующим образом: если в команде встретилась ссылка вперед, то он делает некоторое предположе­ние относительно этого имени и уже на основе этого предположения фор­мирует машинную команду. Если затем (когда встретится описание имени) окажется, что данное предположение было неверным, тогда ассемблер пы­тается исправить сформированнную им ранее машинную команду. Однако это не всегда удается: если правильная машинная команда должна занимать больше места, чем машинная команда, построенная на основе предположе­ния (например, перед командой надо на самом деле вставить префикс за­мены сегмента), тогда ассемблер фиксирует ошибку (как правило, это ошибка номер 6: Phase error between passes.)

Какие же предположения делает ассемблер, встречая ссылку вперед? Во всех командах, кроме команд перехода (о них см. 1.5), ассемблер предполагает, что имя будет описано в сегменте данных и потому сегмен­тируется по регистру DS. Это следует учитывать при составлении прог­раммы: если в команде встречается ссылка вперед на имя, которое описа­но в сегменте, на начало которого указывает сегментный регистр, отлич­ный от DS, то перед таким именем автор программы должен написать соот­вествующмй префикс. Пример:

code segment

assume cs:code

x dw ?

beg: mov ax,x ;здесь вместо cs:x можно записать просто x mov cs:y,ax ;здесь обязательно надо записать cs:y


...

y dw ?

code ends

1.5. ПЕРЕХОДЫ

В систему команд ПК входит обычный для ЭВМ набор команд перехода: безусловные и условные переходы, переходы с возвратами и др. Однако в ПК эти команды имеют некоторые особенности, которые здесь и рассматри­ваются.

Абсолютный адрес команды, которая должна быть выполнена следующей, определяется парой CS:IP, поэтому выполнение перехода означает измене­ние этих регистров, обоих или только одного (IP). Если изменяется только счетчик команд IP, то такой переход называется внутрисегментным или близким (управление остается в том же сегменте команд), а если ме­няются оба регистра CS и IP, то это межсегментный или дальний переход (начинают выполняться команды из другого сегмента команд). По способу изменения счетчика команд переходы делятся на абсолютные и относитель­ные. Если в команде перехода указан адрес (смещение) той команды, ко­торой надо передать управление, то это абсолютный переход. Однако в команде может быть указана величина (сдвиг), которую надо добавить к текущему значению регистра IP, чтобы получился адрес перехода, и тогда это будет относительный переход; при этом сдвиг может быть положитель­ным и отрицательным, так что возможен переход вперед и назад. По вели­чине сдвига относительные переходы делятся на короткие (сдвиг задается байтом) и длинные (сдвиг - слово). Абсолютные же переходы делятся на прямые и косвенные: при прямом переходе адрес перехода задается в са­мой команде, а при косвенном - в команде указывается регистр или ячей­ка памяти, в котором (которой) находится адрес перехода.

1.5.1 Безусловные переходы.

В MASM все команды безусловного перехода обозначаются одинаково:

JMP op

но в зависимости от типа операнда, ассемблер формирует разные машинные команды.

1) Внутрисегментный относительный короткий переход.

JMP i8 (IP:=IP+i8)

Здесь i8 обозначает непосредственный операнд размеров в байт, который интерпретируется как знаковое целое от -128 до 127. Команда прибавляет это число к текущему значению регистра IP, получая в нем адрес (смеще­ние) той команды, которая должна быть выполнена следующей. Регистр CS при этом не меняется.

Необходимо учитывать следующую особенность регистра IP. Выполнение любой команды начинается с того, что в IP заносится адрес следующей за ней команды, и только затем выполняется собственно команда. Для коман­ды относительного перехода это означает, что операнд i8 прибавляется не к адресу этой команды, а к адресу команды, следующей за ней, поэто­му, к примеру, команда JMP 0 - это переход на следующую команду про­граммы.

При написании машинной программы сдвиги для относительных перехо­дов приходится вычислять вручную, однако MASM избавляет от этого не­приятного занятия: в MASM в командах относительного перехода всегда указывается метка той команды, на которую надо передать управление, и ассемблер сам вычисляет сдвиг, который он и записывает в машинную ко­манду. Отсюда следует, что в MASM команда перехода по метке восприни­мается не как абсолютный переход, а как относительный.

По короткому переходу можно передать управление только на ближай­шие команды программы - отстоящие от команды, следующей за командой перехода, до 128 байтов назад или до 127 байтов вперед. Для перехода на более дальние команды используется

2) Внутрисегментный относительный длинный переход.

JMP i16 (IP:=IP+i16)

Здесь i16 обозначает непосредственный операнд размером в слово, кото­рый рассматривается как знаковое целое от -32768 до 32767. Этот пере­ход аналогичен короткому переходу.

Отметим, что, встретив команду перехода с меткой, которой была по­мечена одна из предыдущих (по тексту) команд программы, ассемблер вы­числяет разность между адресом этой метки и адресом команды перехода и по этому сдвигу определяет, какую машинную команду относительного пе­рехода - короткую или длинную - надо сформировать. Но если метка еще

не встречалась в тексте программы, т.е. делается переход вперед, тогда ассемблер, не зная еще адреса метки, не может определить, какую именно машинную команду относительного перехода формировать, поэтому он на всякий случай выбирает команду длинного перехода. Однако эта машинная команда занимает 3 байта, тогда как команда короткого перехода - 2 байта, и если автор программы на MASM стремится к экономии памяти и знает заранее, что переход вперед будет на близкую метку, то он должен сообщить об этом ассемблеру, чтобы тот сформировал команду короткого перехода. Такое указание делается с помощью оператора SHORT:

JMP SHORT L

Для переходов назад оператор SHORT не нужен: уже зная адрес метки, ас­семблер сам определит вид команды относительного перехода.

3) Внутрисегментный абсолютный косвенный переход.

JMP r16 (IP:=[r])

или JMP m16 (IP:=[m16])

Здесь r16 обозначает любой 16-битовый регистр общего назначения, а m16 - адрес слова памяти. В этом регистре (слове памяти) должен находиться адрес, по которому и будет произведен переход. Например, по команде JMP BX осушествляется переход по адресу, находящемуся в регистре BX.

4) Межсегментный абсолютный прямой переход.

JMP seg:ofs (CS:=seg, IP:=ofs)

Здесь seg - начало (первые 16 битов начального адреса) некоторого сег­мента памяти, а ofs - смещение в этом сегменте. Пара seg:ofs определя­ет абсолютный адрес, по которому делается переход. В MASM эта пара всегда задается конструкцией FAR PTR <метка>, которая "говорит", что надо сделать переход по указанной метке, причем эта метка - "дальняя", из другого сегмента. Отметим, что ассемблер сам определяет, какой это сегмент, и сам подставляет в машинную команду его начало, т.е. seg.

5) Межсегментный абсолютный косвенный переход.

JMP m32 (CS:=[m32+2], IP:=[m32])

Здесь под m32 понимается адрес двойного слова памяти, в котором нахо­дится пара seg:ofs, задающая абсолютный адрес, по которому данная ко­манда должна выполнить переход. Напомним, что в ПК величины размером в двойное слово хранятся в "перевернутом" виде, поэтому смещение ofs на­ходится в первом слове двойного слова m32, а смещение seg - во втором слове (по адресу m32+2).

Команды межсегментного перехода используются тогда, когда команды программы размещены не в одном сегменте памяти, а в нескольких (напри­мер, команд столь много, что в совокупности они занимают более 64Кб, т.е. более максимального размера сегмента памяти). При переходе из од­ного такого сегмента в другой необходимо менять не только счетчик ко­манд IP, но и содержимое регистра CS, загружая в последний начальный адрес второго сегмента. Такое одновременное изменение обоих этих ре­гистров и делают команды межсегментного перехода.