Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Учебное пособие 3000239.doc
Скачиваний:
23
Добавлен:
30.04.2022
Размер:
1.12 Mб
Скачать

7. Процедуры

7.1. Дальние переходы

До сих пор рассматривались программы с одним сегментом команд, что характерно для небольших программ. Однако в общем случае в программе может быть столь много команд, что они не вместятся в один сегмент памяти (их суммарный размер может превзойти 64Кб). В таком случае (либо по какой-то иной причине) в программе описывается несколько сегментов команд. Например, в программе могут быть описаны такие сегменты команд:

C1 SEGMENT

ASSUME CS:C1, ...

START: MOV AX, 0

...

JMP FAR PTR L ;goto L

...

C1 ENDS

C2 SEGMENT

ASSUME CS:C2

L: INC BX

...

C2 ENDS

Следует отметить, что в начале каждого сегмента команд должна быть указана директива ASSUME, в которой (помимо прочего) сегментному регистру CS ставится в соответствие данный сегмент (именно по этой информации ассемблер узнает, что текущий сегмент является сегментом команд). В противном случае, встретив первую же метку, ассемблер зафиксирует ошибку.

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

Если меняется только IP, то это означает переход внутри сегмента. Такие переходы называются близкими или внутрисегментными; до сих пор только такие переходы мы и рассматривали. Если программа небольшая и все ее команды умещаются в одном сегменте памяти, то другие переходы и не нужны. Но если в программе имеется несколько сегментов команд, тогда возникает потребность в переходах из одного такого сегмента в другой (например, из сегмента C1 на метку L сегмента С2). Такие переходы называются дальними или межсегментными. При этих переходах меняется значение и регистра CS, и регистра IP: CS устанавливается на начало сегмента с меткой (CS:=C2), а в IP записывается смещение метки внутри ее сегмента (IP:=offset L).

В ПК предусмотрены команды, реализующие такие дальние переходы, причем все они являются только безусловными переходами (условные переходы - всегда близкие), прямыми или косвенными. В языке ассемблера в этих командах указывается тот же мнемокод JMP, что и при близких переходах, но используются другие типы операндов. Флаги команды дальнего перехода не меняют.

Дальний прямой переход: JMP FAR PTR <метка>

Здесь слово FAR (дальний) указывает ассемблеру, что метка дальняя, что она находится в другом сегменте команд. По этой команде регистр CS устанавливается на начато того сегмента, в котором эта метка находится, а в регистр IP записывается смещение этой метки внутри данного сегмента:

CS:=seg <метка>; IP:=offset <метка>

Так, в примере выше указан дальний переход на метку L.

Дальний косвенный переход: JMP m32

В этой команде указывается адрес двойного слова, в котором должен находиться абсолютный адрес перехода в виде адресной пары seg:ofs, записанной в «перевернутом» виде: смещение ofs должно быть записано по адресу m32, а номер сегмента seg – по адресу m32+2. Команда делает переход по этому абсолютному адресу, т. е. записывает в регистр CS величину seg, а в регистр IP - величину ofs:

CS:=[m32+2]; IP:=[m32]

Пример:

X DD L ;X:offset L, X+2: seg L

...

JMP X ;goto L(CS:=seg L, IP:=offset L)

...

При записи команды косвенного перехода надо соблюдать осторожность. Если в команде указано имя X, которое описано до этой команды (как в данном примере), тогда, встретив ее, ассемблер уже будет знать, что X обозначает двойное слово, и потому правильно поймет, что в данной команде имеется в виду дальний косвенный переход. Но если имя X будет описано позже, тогда ассемблер, встретив команду перехода, не будет знать, какого вида переход имеется в виду в данном случае – то ли близкий переход по метке, то ли близкий косвенный переход, то ли дальний косвенный переход. В такой ситуации, как уже говорилось ранее, ассемблер предполагает, что указанное имя – это метка из текущего сегмента команд, и формирует машинную команду близкого перехода. Затем же, когда будет обнаружено, что в текущем сегменте такой метки нет, ассемблер зафиксирует ошибку. Чтобы ее избежать, надо в случае ссылки вперед при дальнем косвенном переходе явно указать, что имя обозначает переменную размером в двойное слово. Для этого используется оператор PTR:

JMP DWORD PTR X

Как уже отмечалось, подобного рода уточнения для ссылок вперед приходится делать и при близких переходах. Если собрать вместе все, что уже говорилось про ссылки вперед в командах безусловного перехода, то получится следующее правило: если X – ссылка вперед, тогда команду безусловного перехода следует записывать так:

JMP X – близкий прямой длинный

JMP SHORT X – близкий прямой короткий

JMP FAR PTR X – дальний прямой

JMP WORD PTR X – близкий косвенный

JMP DWORD PTR X– дальний косвенный

Аналогичного рода уточнения надо делать и тогда, когда в команде JMP указана косвенная ссылка, например JMP [BX]; по умолчанию ассемблер рассматривает подобного рода команду как близкий косвенный переход.

Что же касается случая, когда X – ссылка назад, то вид перехода обязательно надо уточнять только в одном случае – при дальнем прямом переходе: ассемблер, даже зная, что X – метка из другого сегмента команд, все равно не будет рассматривать команду JMP X как дальний переход. Дело в том, что, встречая любую метку, ассемблер автоматически приписывает ей тип NEAR (близкий) и далее руководствуется этим типом. Изменить же этот тип можно (в рамках одной команды) только с помощью оператора PTR.

Следует отметить, что NEAR и FAR - это имена стандартных констант со значениями соответственно -1 (0FFFFh) и -2 (0FFFEh). Именно эти значения выдает оператор TYPE, если в качестве его операнда указать не имя переменной, а метку (или имя процедуры); например: TYPE L = NEAR.