Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
flor_apparato-orientirovnnoe_prog.doc
Скачиваний:
89
Добавлен:
15.06.2014
Размер:
926.72 Кб
Скачать

3. Архитектурные элементы для построения программ

3.1. Организация условных переходов

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

IF условие THEN оператор

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

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

Специализированной командой для вычисления условия является команда

CMP операнд1, операнд2

название которой восходит к слову compare и которая сравнивает значения операндов операнд1 и операнд2. Результат такого сравнения автоматически фиксируется в специальном регистре, обозначаемом в рассматриваемой архитектуре как EFLAGS (регистр флагов). Это 32-битный регистр, но основное значение имеет его младшая 16-битная половина, обозначаемая просто FLAGS. В этом регистре отдельными битами отображается ситуация равенства нулю результата, его отрицательности, появления переполнения при выполнении некоторых операций (биты ZF, NF, CF) и т.п. Для разветвления действия программы по значениям отдельных установленных бит в этом регистре имеются специальные команды, но они используются достаточно редко, чтобы начинать с их изучения.

Общей мнемоникой команды разветвления является

Jcc метка

где сс обозначает мнемонический код условия (condition code). Для кодов условия имеет множество допустимых вариантов, основные из которых приведены в следующей таблице.

Таблица 3.1.

Команды условных переходов

Мнемокод

Условие перехода

Математическое отношение

Примечание

JE

равно

=

знаковое

JNE

не равно

знаковое

JL

меньше

<

знаковое

JG

больше

>

знаковое

JLE

меньше или равно

<=

знаковое

JGE

больше или равно

>=

знаковое

JA

выше (above)

>

беззнаковое

JB

ниже (below)

<

беззнаковое

JC

по флагу CF переноса

JNC

если CF не установлен

Команды условного перехода для знакового сравнения соответствуют использованию предварительно сформированного условия на основе значений модификатора типа signed, а беззнакового - на основе модификатора unsigned. Сами мнемоники построены, исходя из начальных букв английских слов equal (равно), less (меньше), greater (больше), not (нет). Кроме перечисленных в таблице мнемоник имеются еще JNL, JNG, JNA, JNB, JAE, JBE, отвечающие, соответственно, условиям не меньше, не больше, не выше, не ниже, выше или равно, ниже или равно. Причем мнемоники в парах JGE и JNL, JLE и JNG, JAE и JNB, JBE и JNA обозначают одинаковые по действиям команды.

Действия всех перечисленных команд заключается в том, что в момент их выполнения проверяется содержимое регистра флагов EFLAGS и, если совокупность этих флагов отвечает мнемонике команды, то происходит переход выполнения на указанную в команде метку. В противном случае выполняется следующая по порядку следования в памяти команда.

Для знакомых с программированием на языке Бейсик действия команд условного перехода можно представлять как реализацию оператора Бейсика:

IF условие THEN GOTO метка

где условие предварительно вычисляется предыдущими командами и временно запоминается в служебном регистре EFLAGS.

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

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

Совершенно не обязательно для формирования условия с целью условного перехода использовать команду сравнения. Арифметические и логические команды, в качестве побочного эффекта, переустанавливают флажки в регистре EFLAGS. В частности, это делают команды сложения и вычитания. Поэтому часто достаточно использовать команды условного перехода непосредственно после тех арифметических или логических, которые формируют значение, на основе которого должно приниматься решение о разветвлении.

Например, для вычисления модуля разности двух переменных a и b (описанных в сегменте данных как двойные слова, т.е. переменные типа long или int для 32-битной архитектуры) с результатом в регистре eax можно использовать следующую последовательность команд

MOV eax, [a]

SUB eax, [b]

JGE meta

NEG eax

meta:

Здесь мы встретились с двумя новыми командами: командой вычитания SUB и командой NEG изменения знака числа на противоположный. Первая из них чрезвычайно похожа на команду ADD, но выполняет не сложение, а вычитание. Мнемоника второй восходит к английскому слову negative, а сама команда имеет только один операнд, который перед выполнением содержит исходное значение для выполнения операции, а после выполнения команды этот операнд содержит уже значение с противоположным знаком (реализуется оператор вида N = -N из языка Си). В последней команде в качестве операнда поэтому оказывается возможным использовать только регистровую адресацию или задавать с помощью адресации место в памяти, где собственно и находится преобразуемое значение.

Команды условного перехода в архитектуре Intel имеют одну «хитрую» особенность, унаследованную от первых очень примитивных микропроцессоров. Она состоит в том, что простейшая форма записи команд условного перехода порождает машинные команды, неспособные осуществлять переход далее чем на 127 байтов вперед и далее чем на 128 байтов назад! (Связано это с очень экономным кодированием относительного места перехода, размещаемого всего в одном байте.) Как следствие этого, попытки указать условный переход далее указанного диапазона вызывают ошибку со следующим текстом сообщения о ней: "short jump is out of range". В таких случаях программисту необходимо явно указать использование другого варианта построения команды условного перехода. Это указание заключается в добавлении модификатора NEAR перед меткой, задаваемой в качестве операнда. В общем случае это приводит к записи вида

Jcc NEAR метка

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

Заметим, что с помощью команд условного перехода можно организовывать разветвления не по двум направлениям, как в обычных операторах if языков Паскаль и Си, но и в большем числе направлений. Например, фрагмент

. . . формирование условия

JL metk1

JG metka

JE metk2

обеспечивает переходы на одну из меток mek1, metk2 и metka. Правда, такие ситуации достаточно редки, так как на команду, помещенную непосредственно за приведенными командами, ни при каком из использованных условий не происходит переход, если только за ними не поставить одну из перечисленных меток. Последний вариант избыточен, так как проще в данном случае опустить соответствующую команду условного перехода и команда, следующая за командами фрагмента, будет выполняться автоматически при невыполнении других условных переходов в данном наборе. Иной вариант построения этого фрагмента есть

. . . формирование условия

JL metk1

JG metka

; сюда попадаем при выполнении условия равенства

; и метку, например metk2, ставить совершенно излишне

Практически это тоже разветвление на три направления, но неявное в записи, так как третье направление равносильно продолжению последовательности без явных переходов.

Кроме команд условного перехода в архитектуре имеется (совершенно необходимая) команда безусловного перехода, изображаемая мнемокодом JMP. Она при записи на ассемблере имеет вид

JMP метка

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

IF условие THEN оператор1 ELSE оператор2.

Разворачивание такого условного оператора в последовательность команд имеет общую структуру вида

. . . формирование условия

Jncc метка_оп2

. . . команды оператора1

JMP метка_за

метка_оп2:

. . . команды оператора2

метка_за:

где условное обозначение ncc мнемоники условного перехода обозначает условие, противоположное для исходного условия условие в операторе высокого уровня. Например, если исходное условие есть равно, то используется переход по мнемонике JNE, если исходное условие есть "не больше", то используется мнемоника JG и т.п. Основная цель такого приема в том, чтобы при выполнение исходного условия команда Jncc метка_оп2 не осуществляла переход на метку, с которой начинается выполнение оператора оператор2, а выполнялись следующие по записи команды, которые реализуют оператор оператор1. В конце же последовательности команд, которые реализуют оператор оператор1, должен выполняться "скачок", чтобы не попасть на выполнение команд оператора оператор2, а продолжить выполнение программы далее за пределами рассматриваемой условной конструкции.

Далее в листинге 3.1.1 приведена более содержательная программа для Linux, которая демонстрирует простое использование различных переходов для решение практических задач. Эта программа осуществляет ввод строки текста и сравнение в нем пятого и шестого символа. По результатам сравнения выдается одно из двух сообщений - равны символы или нет. Причем при вводе строки меньшей, чем из шести символов, выдается сообщение о невозможности выполнения задачи.

GLOBAL _start

SEGMENT .text

_start:

;--- read(1, buf, 80) == <3>(ebx, ecx, edx)

mov eax,3 ; N function=read

mov ebx,0 ; 0 handle=0 (stdin)

mov ecx, buf ; address of buf

mov edx,80 ; number of byte

int 80h

cmp eax, 7

jl wrno

mov al, [buf+4]

cmp al, [buf+5]

jne wrne

mov ecx, mese

mov edx, lenmese

;--- write(1, mese, lenmese) == <4>(ebx, ecx, edx)

write: mov eax,4 ; N function=write

mov ebx,1 ; N handle=1 (stdout)

int 80h

mov eax,1

int 80h ; function=exit Linux

wrne: mov ecx, mesne

mov edx, lenmesne

jmp write

wrno: mov ecx, mesno

mov edx, lenmesno

jmp write

SEGMENT .data

buf times 80 db 0 ; или resb 80; но тогда будет предупреждение

mesno db 'Длина текста меньше 6'

lenmesno equ $-mesno

mese db 'Пятый и шестой символы совпадают'

lenmese equ $-mese

mesne db 'Пятый символ отличается от шестого';

lenmesne equ $-mesne

Листинг 3.1.1. Ввод текста и сравнение в нем 5-го и 6-го символов

Напомним, что число символов, введенных системной функцией write, возвращается в регистре eax, (причем выдается число символов, включая завершающий символ перевода строки), поэтому после завершения ввода текста содержимое этого регистра сравнивается с числом 7 посредством команды

cmp eax, 7

за которой следует команда условного перехода JL wrno. В месте программы, отмеченной меткой wrno, выполняется вывод сообщения о недостаточной длине введенного текста. Для чего подготавливается содержимое регистров ecx и edx, в которые помещается адрес текста сообщения и длина этого сообщения, соответственно. После этой подготовки командой JMP write задается принудительный переход на метку write. Начиная со строки, отмеченной меткой write, продолжается подготовка к вызову системной функции write. С этой целью необходимые значения заносятся в регистры eax и ebx (значение 4 - номер системной функции и значение 1 - хэндла стандартного вывода). Далее следует собственно обращение к системной функции (командой INT 80H), за которой записаны команды обращения к системной функции exit завершения выполнения программы.

Если длина введенного текста достаточна для решения поставленной задачи, то рассмотренного выше перехода командой JL wrno не происходит, а выполняются помещенные за ней команды

mov al, [buf+4]

cmp al, [buf+5]

которые переносят в регистр al содержимое пятого символа текста (в математической системе отсчета индексов от единичного), а затем сравнивая содержимое этого регистра с шестым символом введенного текста.

При несовпадении сравниваемых значений следующая команда jne wrne выполняет переход на метку wrne, а при совпадении перехода не происходит и выполняются последующие команды:

mov ecx, mese

mov edx, lenmese

которые заносят в регистры ecx и edx информацию об адресе и длине сообщения, которое должно быть выдано в этой ситуации.

За этими командами непосредственно следует команда с меткой write, которая начинает уже рассмотренную нами последовательность команд, обеспечивающая вывод подготовленного сообщения. Если же выше произошел переход на метку wrne, то, начиная с нее, подготавливается выдача сообщения о неравенстве рассматриваемых символов и опять же осуществляется принудительный переход на метку write для выдачи сообщения.

В этой программе продемонстрирована также замечательная возможность, предоставляемая служебным метасимволом $. Метасимвол $ обозначает относительный адрес начала строки, в которой он встречается. Поэтому в последовательности

mes db 'Текст'

lenmes equ $-mes

директива EQU вычисляет значение выражения, которое задает разность между относительным адресом текущей строки (а это относительный адрес того места, где закончился текст из предыдущей строки) и относительным адресом, который соответствует в памяти имени mes, которое обозначает отметку начала текста. Тем самым результат дает, сколько байтов занимает данный текст.

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

Следует запомнить, что правильный результат для длины текста, формируемой рассмотренным образом (в символической константе lenmes нашего примера), получается только, если указанная директива EQU размещается непосредственно за директивой определения текста и в ее выражении безошибочно используется имя именно этого текста. (Последняя часть замечания тривиальна, так как ошибочная запись и должна приводить к ошибочным результатам, но как раз такая ошибка оказывается очень характерной для практической работы, когда обе директивы определения строятся, исходя из строк, скопированных из другого места программы.)

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

Соседние файлы в предмете Системное программное обеспечение