Материал: Системное программирование

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

if AX = BX then goto M

следует реализовывать так:

if AX<>BX then goto L; { короткий переход }

goto M; { длинный переход }

L:...

Ассемблер транслирует ее в следующий код:

CMP АХ, ВХ

JNE L

JMP М

L:...

M:...

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

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

Рассмотрим следующие два сегмента:

C1 SEGMENT

ASSUME CS: C1

START: MOV AX, 0

...

JMP FAR PTR L

C1 ENDS

C2 SEGMENT

ASSUME CS: C2

L: INC BX

...

C2 ENDS

Переход из сегмента C1 на метку L сегмента C2 будет дальним. При таких переходах меняется значение регистра CS и регистра IP: регистр CS настраивается на начало сегмента с меткой CS:=C2, а в регистр IP записывается смещение метки внутри сегмента IP:=OFFSETL.

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

JMP FAR PTR <метка>

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

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

При дальнем косвенном переходе в команде указывается адрес двойного слова, в котором находится адрес перехода в виде адресной пары seg:disp, записанной в обратном порядке: смещение disp должно быть записано по адресу m32, а номер сегмента seg - по адресу m32+2:

JMP m32

Следует заметить, что если 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 (близкий). Операторы NEAR и FAR являются именами стандартных констант со значениями соответственно -1 (0FFFFh) и -2 (0FFFEh).

5.6. Команды организации циклов

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

MOV CX, N ; CX - счетчик цикла (число повторений)

L: ...

... ; тело цикла

DEC CX ; СХ=СХ-1

CMP CX, 0 ; СХ=0?

JNEL; СХ<>0 >gotoL

В конце цикла всегда применяется одна и та же тройка команд. Команда LOOP объединяет действия трех команд и позволяет организовать цикл по счётчику (табл. 52). Флаги команда не изменяет. В качестве счётчика всегда используется регистр CX.

Табл. 52. Команда LOOP.

Код

Инструкция

Описание

E2 cb

LOOP rel8

Цикл по счётчику.

E1 cb

LOOPE rel8

Цикл по счётчику и равенству.

E1 cb

LOOPZ rel8

Цикл по счётчику и нулевому результату.

E0 cb

LOOPNE rel8

Цикл по счётчику и неравенству.

E0 cb

LOOPNZ rel8

Цикл по счётчику и ненулевому результату.

Действие команды LOOP можно представить следующим образом:

CX=CX-1;

if СХ<>0 then goto <метка>

С помощью команды LOOP цикл запишется так:

MOV CX, N ; N > 0

L: ...

... ; тело цикла

LOOP L

Команда LOOP работает быстрее трех предыдущих команд, однако существует ряд особенностей:

  • В качестве счетчика цикла можно использовать только регистр СХ.

  • Начальное значение для СХ должно быть присвоено до цикла, причем этому регистру надо присваивать ровно столько, сколько должен повторяться цикл. Команда LOOP контролирует только нулевое значение, при отрицательном значении цикл не завершится.

  • Минимальное число итераций равно одному. Для случая СХ=0 команда не подходит.

Если число итераций может быть нулевым, то при СХ=0 надо выполнить обход цикла:

MOV CX, N

JCXZ L1

L: ...

... ; тело цикла

LOOP L

L1:...

Для выполнения таких обходов существует команда условного перехода JCXZ.

Дополнительным условием продолжения выполнения цикла может быть состояние флага ZF в 0 (LOOPNE, LOOPNZ) или 1 (LOOPE, LOOPZ). Названия LOOPE и LOOPZ являются синонимами, также как LOOPNE и LOOPNZ.

Действие этих команд следующее:

СХ:=СХ-1;

If (CX <> 0) and (ZF = 1) then goto <метка>

Команда LOOPE используется для организации цикла с известным числом итераций, из которого возможен досрочный выход. До начала цикла в регистр СХ записывается число итераций. Команда помещается в конец цикла (операнд команды – метка первой команды цикла), а перед ней помещается команда, меняющая флаг ZF (обычно команда сравнения СМР). Команда LOOPE заставляет цикл повторяться СХ раз, но только если предыдущая команда фиксирует равенство сравниваемых величин (вырабатывает нулевой результат).

Причину выхода (ZF=1 или СХ=0) следует проверять после цикла. Причем проверяется флаг ZF (по команде JE/JZ или JNE/JNZ), а не регистр СХ, т.к. условие ZF=0 может появиться на последнем шаге цикла, когда регистр СХ стал нулевым.

Команда LOOPNE/LOOPNZ аналогична команде LOOPE/LOOPZ, но выход из цикла осуществляется при СХ=0 или ZF=0. Действие команды:

СХ:=СХ-1;

if (CX <> 0) and (ZF = 0) then goto <метка>

Приведем пример использования команды цикла. Предположим, требуется найти в некоторой области памяти первый ненулевой байт. Смещение начального адреса находится в регистре SI, конечного – в регистре DI. Смещение найденного байта возвращается в регистр SI. Если ненулевой байт не найден, SI, будет указывать на конец области памяти, т.е. содержать тоже значение, что и DI.

SUB DI, SI ; размер области памяти

INC DI ; DI=DI-SI+1

MOV CX, DI ; счетчик байтов

DEC SI

L:INCSI; переместить указатель к следующей ячейке

CMPBYTEPTR[SI], 0 ; сравнить содержимое ячейки с нулем

LOOPZ L ; переход к проверке следующего байта

JNZ L1 ; переход по метке L1, если байт ненулевой

... ; ненулевой байт не найден

L1:... ; смещение ненулевого байта находится в SI

Также как и команды условного перехода, все инструкции циклов выполняют только короткие переходы, поскольку в них используется относительная адресация с 8-разрядным отклонением. Если цикл содержит больше команд, команду цикла использовать не получится и цикл следует реализовать по-иному.

5.7. Команды обработки строк

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

Во всех строковых командах под строкой понимается последовательность байтов (символов) или последовательность слов. Поэтому каждая команда представлена двумя вариантами – для обработки строк из байтов и из слов. Мнемокоды команд различаются только последней буквой – B (Byte) или W (Word) соответственно.

Команда CMPS (сравнение строк) сравнивает между собой байты или слова источника и приёмника (табл. 53). Флаги устанавливаются в соответствии с результатами выполнения операции сравнения. Оба операнда должны быть расположены в памяти. Адрес источника задаётся регистровой парой DS:SI, адрес приёмника – ES:DI. Сегментный регистр DS может быть заменён другим сегментным регистром с помощью префикса замены сегмента. Сегментный регистр ES не может быть заменён другим.

Табл. 53. Команда CMPS.

Код

Инструкция

Описание

A6

CMPSB

Сравнение байтов [DS:SI] и [ES:DI].

A7

CMPSW

Сравнение слов [DS:SI] и [ES:DI].

После выполнения операции содержимое регистров SI и DI автоматически увеличивается или уменьшается на размер операнда, т.е. на 1 или на 2 байта. Увеличение (инкремент) имеет место, если флаг направления DF в регистре флагов сброшен, а уменьшение (декремент) – если флаг DF установлен. Изменить флаг направления можно с помощью команд CLD и STD (см. раздел 5.11).

Сравнение, как и в команде CMP, выполняется вычитанием значения приёмника из значения источника. Эти значения рассматриваются как двоичные числа со знаком. Результат вычитания не сохраняется, и исходные операнды не изменяются. По результатам вычитания устанавливаются соответствующие признаки в регистре флагов. Инструкция CMPS выполняет сравнение только одной пары байтов или слов.

Рассмотрим пример сравнения двух байтовых строк A и B. Предположим, строка A расположена в сегменте данных DS, а строка B – в дополнительном сегменте ES. Длина обеих строк одинакова и равна N. Для организации побайтного сравнения от начала к концу строк необходимо сбросить флаг DF и организовать цикл. В регистр счетчика следует поместить число N, а выход из цикла выполнять по условию JNE.

Источник: https://files.student-it.ru/previewfile/284711