END MAIN ;директива ассемблера




Рис. 6.1. Полный текст программы на языке ассемблера, для сложения пяти целых чисел

Три символических имени, объявленных в разделе данных, используются в ко­мандах в разделе кода. В частности, метка MAIN предназначена для определения точки начала выполнения программы, а также задается в ассемблерной директи­ве END, которой заканчивается текстовый файл программы. В ассемблере IA-32 имеются и другие директивы. Некоторые из них будут рассмотрены позже.

6.3. Управление потоком выполнения программы

Существует два способа отклонения программы от «прямолинейного курса». Первый из них (о нем рассказывается в разделе 5.8) — это вызов подпрограммы и возврат из нее. Кроме того, в программе могут выполняться переходы к задан­ным командам — как условные, так и безусловные. О них мы сейчас и поговорим.

6.3.1. Условные переходы и флаги кодов условий

Представленная на рис. 5.8, команда

JG STARTADD

относится к числу команд условного перехода. Условие «больше нуля» задается в ней суффиксом кода операции G. Это условие касается и результата последней выполненной команды обработки данных, которой в нашем примере была команда

DEC ECX

Признаки результатов, которые генерируются командами типа Decrement и Add, выполняющими арифметические операции и операции сравнения, записы­ваются в четыре флага кодов условий в регистре состояния процессора, показан­ном на рис. 5.2. В зависимости от результата операции эти флаги, называемые SF (sign — знак), ZF (zero — нуль), OF (overflow — переполнение) и CF (carry — перенос), устанавливаются в 1 или очищаются нулем. Но существует одно исключение. В операции вычитания бит CF устанавливается в 1, если перенос не выполняется, что соответствует сигналу обратного переноса. Состояние этих флагов можно проверить в последующих командах условного перехода, с тем чтобы решить, следует ли осуществлять переход. В нашем примере при выполнении условия [ECX] > 0 управление передается команде, записанной по целевому адресу STARTADD.

В команде условного перехода задается не абсолютное значение целевого ад­реса перехода, а число со знаком, которое прибавляется к содержимому регистра указателя команды, то есть целевой адрес задается относительно адреса в указа­теле команды. Значение указателя команды увеличивается сразу после выборки очередной команды, поэтому он всегда определяет следующую выполняемую ко­манду программы. Когда к указателю прибавляется относительный адрес перехода, он начинает указывать на команду, следующую за командой перехода. Предполо­жим, что адрес STARTADD в нашем примере равен 1000. Для кодирования ко­манд ADD, INC, DEC и JG из программы на рис. 5.8, а требуется 7 байт. Обнов­ленное содержимое регистра указателя команды EIP будет равно 1007, то есть адресу последней в программе команды MOV. Таким образом, относительное расстояние до целевой команды перехода составляет -7; именно данное значение и содержится в команде условного перехода. Это маленькое отрицательное число можно представить одним байтом. Поэтому, с учетом байта кода операции, для записи команды условного перехода достаточно 2 байт. Такой размер имеют ко­манды перехода, в которых относительный адрес перехода лежит в диапазоне от -128 до +127. Если же расстояние до целевой команды перехода больше, то ис­пользуется 4-байтовое смещение.

В этом примере проверяется значение в регистре ЕСХ — нас интересует, боль­ше ли оно нуля. Другие свойства результата можно проверить при помощи иных команд условного перехода. Например, если результат равен нулю, переход вы­полняется командой JZ (или JE), а если знак результата отрицателен (знаковый бит равен 1) — командой JS.

6.3.2. Команды сравнения

Условный переход в программах часто осуществляется в соответствии с резуль­татом сравнения двух чисел. Команда Compare

CMP dst,src

выполняет операцию

[dst] - [src]

и на основе полученного результата устанавливает флаги кодов условий. При этом ни один из операндов не изменяется и первый операнд всегда сравнивается со вторым. Например, переход по условию «больше» выполняется в том случае, если операнд назначения dst больше исходного операнда src.

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

Команда безусловного перехода JMP всегда вызывает переход к команде по целе­вому адресу. В ней может быть задано короткое (1 байт) или длинное (4 байта) от­носительное смещение со знаком. Кроме того, как и в командах условного перехо­да, могут использоваться другие режимы адресации. Такая гибкость определения целевого адреса перехода может быть очень полезной. В каждой точке программы выполняется только одно из альтернативных вычислений. Предположим, что в спе­циальной таблице в памяти начиная с адреса JUMPTABLE хранятся 4-байтовые адреса первой команды каждой из подпрограмм, представляющих возможные ветви программы. Если последовательно пронумеровать эти ветви как 0, 1, 2... и загрузить индекс выполняемой подпрограммы в регистр ESI, то переход к вы­бранной ветви может быть выполнен посредством такой команды с использова­нием индексной адресации со смещением:

JMP [JUMPTABLE + ESI*4]

6.4. Логические команды, команды сдвига и циклического сдвига

6.4.1. Логические операции

В архитектуре IA-32 имеются команды, выполняющие логические операции И, ИЛИ и Исключающее ИЛИ (END, OR, XOR). Это поразрядные операции с двумя операндами и записью результата по адресу назначения. Предположим, что в регистре ЕАХ со­держится шестнадцатеричное значение 0000FFFF, а в регистре ЕВХ — значение 02FA62CA. Команда

AND EBX,EAX

очистит левую половину регистра ЕВХ, заполнив ее нулями, а правую его часть оставит без изменений. В результате в ЕВХ окажется значение 000062СА.

Кроме того, в архитектуре IA-32 имеется команда NOT, генерирующая логиче­ское дополнение всех битов операнда, то есть заменяющая все единицы нулями, а все нули единицами.

6.4.2. Операции сдвига и циклического сдвига

При помощи операции логического или арифметического сдвига операнд может быть смещен влево или вправо на заданное количество разрядов. Формат коман­ды сдвига таков:

КодОперации dst,count

где сдвигаемый операнд dst задается при помощи одного из стандартных адрес­ных режимов, а количество разрядов сдвига count представляется 8-разрядным значением, либо задаваемым непосредственно в команде, либо содержащимся в 8-разрядном регистре CL. Существует четыре команды сдвига:

- SHL — логический сдвиг влево;

- SHR — логический сдвиг вправо;

- SAL — арифметический сдвиг влево (то же, что SHL);

- SAR — арифметический сдвиг вправо.

При внимательном рассмотрении представления двоичного числа, понятно, что сдвиг числа на один разряд влево эквивалентен его умножению на 2, а сдвиг на один разряд вправо — его делению на 2. Конечно, при сдвиге влево может произойти переполнение, а при сдвиге вправо может по­теряться конец числа. Освобождающиеся в результате сдвига разряды устанавливаются в 0, а сдвигаемые за границу операнда разряды отмечаются с помощью флага перено­са С, а затем удаляются. Устанавливать флаг С особенно удобно при выполнении арифметических операций с большими числами, занимающими больше одного слова. На рис. 6.2, а показан пример сдвига содержимого регистра R0 влево на два разряда. Команда логического сдвига вправо работает точно так же (рис. 6.2, 6).

Еще одно важное наблюдение заключается в том, что при сдвиге вправо в освободившемся разряде должен быть повторен знаковый бит. Этим арифметический сдвиг вправо отличается от логического, в котором осво­бождающиеся разряды всегда заполняются нулями. Пример арифметического сдвига вправо (SAR) приведен на рис. 6.2, в. Арифметический сдвиг влево ничем не отличается от логического.

а

б

в

Рис. 6.2. Команды логического и арифметического сдвига: логический сдвиг влево, SHL R0,2 (а); логический сдвиг вправо, SHR R0,2 (б); арифметический сдвиг вправо, SAR R0,2 (в)

Существует также четыре команды циклического сдвига:

ROL - циклический сдвиг влево;

ROR - циклический сдвиг вправо;

RCL - циклический сдвиг влево с установкой флага CF;

RCR - циклический сдвиг вправо с установкой флага CF.

Компьютер поддерживает две вер­сии циклического сдвига вправо и две версии циклического сдвига влево. В пер­вой версии разряды операнда просто циклически сдвигаются, а во второй в сдви­ге участвует еще и флаг CF. Примеры всех четырех операций циклического сдвига приведены на рис. 6.3.

Рис. 6.3. Команды циклического сдвига: влево без переноса, ROL R0,2 (a); влево с пере-носом, RLC R0,2 (б); вправо без переноса, ROR R0,2 (a); вправо с переносом, RRC R0,2 (г)

6.4.3. Программа упаковки цифр

В качестве простого примера использования указанных команд давайте рассмот­рим программу упаковки цифр. Код этой програм­мы для процессоров IA-32 вы видите на рис. 6.4. Два байта ASCII загружаются в регистры AL и BL Команда SHL сдвигает байт в регистре AL на четыре позиции влево, заполняя четыре освободившихся младших бита нулями. Посредством второго операнда этой команды задается количество разрядов, на которое должен быть сдвинут первый операнд. Команда AND очищает четыре старших бита вто­рого байта, записывая в них нули. После этого 4-разрядные значения, представ­ляющие BCD-коды чисел, объединяются командой OR в регистре AL, а затем со­храняются в памяти по адресу PACKED.

Рис. 6.4. Программа для процессоров IA-32, упаковывающая две цифры BCD в один байт

6.5. Другие команды

Мы рассмотрели лишь малую часть набора команд архитектуры IA-32. Еще не­сколько важных команд будут представлены ниже.

6.5.1. Вычитание

Команда SUB (вычитание), так же как и ADD выполняет вычитание байтов или слов, содержащих двоичные данные. Вычитание осуществляется в компьютере по методу сложения с двоичным дополнением: для второго операнда устанавливаются обратные значения битов и прибавляется 1, а затем происходит сложение с первым операндом. Во всем, кроме первого шага, операции сложения и вычитания идентичны.

Существует пять возможных комбинаций операндов: Примеры:

вычитание регистр – регистр SUB EAX,EBX;

вычитание память – регистр SUB NUMB,EBX;

вычитание регистр – память SUB EAX,NUMB;

вычитание регистр - непосредственное значение SUB EAX,100;

вычитание память - непосредственное значение SUB 100,EBX.

6.5.2. Команды умножения и деления

Кроме команд для сложения и вычитания целых чисел со знаком, описанных в разделе 5.5, в наборе команд IA-32 имеются команды для целочисленного ум­ножения и деления, а также для выполнения арифметических операций над чис­лами с плавающей запятой.

Имеются следующие команды умножения:

MUL – для умножения чисел без знака;

IMUL - для умножения чисел со знаком.

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

IMUL REG,src(исходный)

помещающая 32-разрядное значение результата в регистр общего назначения REG. Исход­ный операнд может находиться либо в регистре, либо в памяти.

В случае результата двойной длины команды

IMUL src и MUL src

используют в качестве второго операнда регистр AL, AX или ЕАХ, в зависимости от размера исходного операнда, располагающегося либо в регистре, либо в памяти. Произведение двойной длины по­мещается в два регистра: старшая половина разрядов результата сохраняется в ре­гистрах DH, DX или EDX, а младшая — в регистрax AL, AX или ЕАХ.

Команда для выполнения целочисленного деления имеет следующий формат:

IDIV src

Выполняет целочисленное деление со знаком AL, АХ или ЕАХ (в зависимости от размера источника) на источник (регистр или переменная) и помещает результат в AL, АХ или ЕАХ, а остаток — в АН, DX или EDX соответственно. Результат всегда округляется в сторону нуля, знак остатка всегда совпадает со знаком делимого, абсолютное значение остатка всегда меньше абсолютного значения делителя.

Числа с плавающей запятой имеют гораздо больший диапазон значений, чем целые числа, и исполь­зуются в первую очередь для научных вычислений. В архитектуре IA-32 приме­няется полный набор арифметических операций с такими числами. Их операнды и результаты располагаются в регистрах с плавающей запятой, показанных на рис. 5.12. Поддерживаются два формата чисел: с одинарной (32 разряда) и двой­ной (64 разряда) точностью.

6.5.3. Команды мультимедийного расширения

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

Ускорить процесс обработки данных в приложениях обоих типов можно за счет разделения последовательности элементов данных (как правило, байтов или 16-разрядных слов) на маленькие группы, которые можно обрабатывать парал­лельно. В наборе команд IA-32 имеется множество таких, которые параллельно обрабатывают данные группами по 64 бита, называемыми четверными словами. (Четверное слово содержит восемь байт или четыре 16-разрядных слова.) Эти ко­манды называются ММХ-командами или командами мультимедийного расшире­ния (multimedia extension). Их операнды могут располагаться в памяти или в ре­гистрах с плавающей запятой. Таким образом, эти регистры служат двойной цели:

в них могут храниться числа с плавающей запятой или операнды ММХ-команд. При использовании в командах ММХ регистры обозначаются как ММ0-ММ7.

Для пересылки 64-разрядных ММХ-операндов между памятью и регистрами ММХ используются специальные команды Move.

Команда

PADDB MMi,src

складывает соответствующие байты 8-байтовых операндов и помещает в ре­гистр назначения восемь сумм. Исходный операнд может располагаться в памяти или в регистре ММХ, но операнд назначения обязательно должен находиться в регистре ММХ. Подобные команды имеются как для операций по вычитанию, так и для логических операций.

Типичной операцией, выполняемой в приложениях обработки сигналов, явля­ется свертка — умножение короткой последовательности отсчетов входных сиг­налов на константы, называемые весовыми коэффициентами, и сложение произ­ведений для получения значения выходного сигнала. Подобные операции осуще­ствляются с помощью специальной команды ММХ, объединяющей умножение и сложение. В ней используются 64-разрядные операнды ММХ, содержащие по четыре 16-разрядных элемента данных, представляющих отсчеты сигнала.

6.5.4. Векторные команды

В архитектуре IA-32 определен набор команд, предназначенных для выполнения арифметических операций над маленькими группами чисел с плавающей запятой. Команды SIMD (Single Instruction, Multiple Data — одиночный поток команд и множественный поток данных) полезны для векторных и матричных вычисле­ний в научных приложениях. В терминологии Intel они называются командами потокового расширения SIMD (Streaming SIMD Extension, SSE). Эти команды об­рабатывают составные операнды длиной 128 бит, состоящие из четырех 32-раз­рядных чисел с плавающей запятой. Для хранения этих операндов имеются 128-разрядные регистры (на рис. 5.1 не показаны). Двумя базовыми командами этой группы являются команды сложения и умножения. Они воздействуют на че­тыре соответствующие пары исходных 32-разрядных значений, которые находят­ся в составных 128-разрядных операндах, и помещают четыре отдельных резуль­тата в 128-разрядный операнд назначения.

6.6. Подпрограммы

Очень часто программа должна по многу раз выполнять определенную подзада­чу, но с разными значениями данных. Такая подзадача обычно называется под­программой. Подпрограмма может, скажем, вычислять функцию sin или сортиро­вать список в порядке возрастания или убывания значений.

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

После реализации подпрограммы работа вызывающей ее программы должна быть продолжена. В таком случае говорят, что выполняется возврат из подпро­граммы в вызывающую программу. Делается это с помощью команды Return. По­скольку подпрограмма может вызываться из нескольких разных мест основной программы, при ее вызове где-то должен сохраняться адрес возврата. Иными сло­вами, для обеспечения правильного возврата из подпрограммы команда Call должна сохранить содержимое регистра PC.

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

Особой разновидностью команды перехода является команда Call, выполняю­щая такие операции, как сохранение содержимого регистра PC в регистре связи и переход по указанному в команде целевому адресу. Команда Return также явля­ется разновидностью команды перехода, но она выполняет переход по адресу, за­данному в регистре связи. Этот процесс проиллюстрирован на рис. 6.5.

Рис. 6.5. Связывание подпрограммы через регистр связи

6.6.1. Вложенность подпрограмм и стек процессора

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

Теоретически подпрограммы могут вкладываться на любую глубину. Рано или поздно последняя вызванная подпрограмма завершит свою работу и вернет управление вызвавшей ее подпрограмме. Необходимый для этого адрес возвра­та — это последний адрес, сохраненный в данной цепочке вызовов подпрограмм. Иными словами, адреса возврата сохраняются и используются в порядке LIFO (Last In First Out, что в переводе с английского значит «последним вошел — пер­вым вышел»). Очевидно, что адреса возврата, связанные с вызовами подпрограмм, должны помещаться в стек. Многие процессоры делают это автоматически, как часть работы команды Call. Для использования в качестве указателя стека (SP) вы­зовов подпрограмм выделяется отдельный регистр, именуемый стеком процессора. Команда Call помещает в стек процессора содержимое регистра PC, а команда Return выталкивает из этого стека адрес возврата и помещает его в PC.

6.6.2. Организация стека

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

Стек — это список элементов данных, обычно слов или байтов, доступ к которым ограничен следующим правилом: элементы этого списка могут добавляться толь­ко в его конец и удаляться только из конца. Конец списка называется вершиной стека, а его начало — дном. Такую структуру иногда называют магазином. Этот механизм хранения и обработки данных хорошо описывается емкой фразой «последним вошел — пер­вым вышел» (Last In First Out, LIFO), означающей, что элемент данных, поме­щенный в стек последним, удаляется из него первым. Операцию помещения но­вого элемента в стек часто называют его проталкиванием (push), а операцию извлечения последнего элемента из стека называют его выталкиванием (pop).

Данные, организованные в виде сте­ка хранятся, так чтобы последовательные элементы располагались друг за другом. Первый элемент хранится по адресу BOTTOM, а когда в стек помеща­ются новые элементы, они располагаются в порядке уменьшения последователь­ных адресов. Таким образом, стек растет в направлении уменьшения адресов. На рис. 6.6 показано, как располагается в памяти компьютера стек, элементы которого занимают по одному 32-разрядному слову. На дне он содержит числовое значение 43, а на вершине -28. Для отслеживания адреса вершины стека используется регистр процессора, называемый указателем стека (Stack Pointer, SP (ESP)).

Так как память адресуется побайтово и слово имеет длину 32 разряда (4 байта), операцию проталкивания в стек можно реализовать так:

SUB ESP,4

MOV NEWITEM,(ESP)

где команда SUB вычитает операнд 4 из результирующего операн­да, содержащегося в регистре ESP, и помещает результат в регистр ESP. Эти две ко­манды помещают слово, хранящееся по адресу NEWITEM, на вершину стека, предварительно уменьшая указатель стека (адрес вершины) на одно слово равное 4 байтам. Операция выталкивания из стека может быть реализована так:

MOV ITEM,(ESP)

ADD ESP,4

Эти две команды перемещают значение, хранившееся на вершине стека, в дру­гое место памяти, по адресу ITEM, а затем уменьшают указатель стека на 4, чтобы он указывал на тот элемент, который теперь располагается на вершине стека.

Рис. 6.6. Стек слов в памяти

Как было уже отмечено, стек процессора удобно применять для выпол­нения операций, связанных с входом в подпрограммы и возвратом из таковых. В архитектуре IA-32 в качестве указателя стека используется регистр ESP, указы­вающий на текущую вершину стека процессора (то есть на его верхний элемент). Ширина стека составляет 32 разряда, а это означает, что его эле­менты являются двойными словами.

Существует четыре команды для проталкивания элементов в стек и выталки­вания их из стека.

Команда

PUSH Source

уменьшает значение ESP на 4, а затем сохраняет двойное слово, находящееся по адресу Source, в па­мяти, который указан в ESP.

Команда POP

POP Destination

выполняет обратную операцию: считывает из памяти двойное слово, на которое указывает ESP, то есть считывает из стека верхний элемент, а затем сохраняет его по адресу Destination и увеличивает значение ESP на 4, удаляя тем самым верхний эле­мент из стека. Регистр ESP используется в этой команде неявно. Исходный и ре­зультирующий операнды задаются в одном из режимов адресации IA-32. Еще две команды предназначены для выталкивания из стека и проталкивания в него сра­зу нескольких элементов.

Команда

PUSHAD

проталкивает в стек содержимое восьми регистров общего назначения, от ЕАХ до EDI, а команда

POPAD

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

6.6.3. Передача параметров

Вызывая подпрограмму, программа должна передать ей параметры (операнды), которые будут использоваться в вычислениях, или же их адреса. Закончив свою работу, подпрограмма вернет другие параметры — результаты вычислений. Такой обмен информацией между вызывающей программой и подпрограммой называ­ется передачей параметров. Передача параметров может выполняться нескольки­ми способами. Например, параметры можно помещать в регистры или в память, от­куда подпрограмма сможет их считать. В качестве альтернативы параметры можно поместить в стек процессора, используемый для хранения адресов возврата.

Использование регистров процессора — способ простой и эффективный. На рис. 6.7 показано, как реализовать программу, вы­полняющую сложение последовательности чисел, в виде подпрограммы с передачей параметров через регистры. Длина последовательности n, информация о которой хранится в памяти по адресу N, и адрес первого числа, NUM1, передаются под­программе через регистры ECX и EBX. Вычисленная подпрограммой сумма возвра­щается вызывающей программе через регистр EAX. Соответствующую часть вызы­вающей программы составляют первые четыре команды из числа представлен­ных на рис. 5.19. Первые две команды загружают в регистры ECX и EBX значения N и NUM1. Команда Call выполняет переход к подпрограмме, начинающейся по ад­ресу LISTADD. Кроме того, эта команда помещает в стек процессора адрес воз­врата из подпрограммы. Подпрограмма вычисляет сумму и помещает ее в регистр EAX. После возврата из подпрограммы вызывающая программа сохраняет эту сум­му в памяти по адресу SUM.

Таким образом, регистры ЕВХ, ЕСХ и ЕАХ используются для передачи параметров. Регистр EDI подпрограмма при выполнении сложения за­действует в качестве индексного регистра, поэтому его содержимое должно со­храняться и восстанавливаться при помощи команд PUSH и POP. Подпрограмма вызывается командой

CALL LISTADD

Первым делом эта команда проталкивает в стек адрес возврата, а затем выпол­няет переход по адресу LISTADD. Содержимое стека после сохранения в нем со­держимого регистра EDI показано на рис. 6.7, б. Адрес возврата в нашем приме­ре — это адрес команды MOV, непосредственно следующей в вызывающей про­грамме за командой CALL. Команда RET возвращает управление вызывающей программе, выталкивая из стека содержимое указателя команды EIP.

а

б

Рис. 6.7. Программа с рис. 5.8, переписанная в виде подпрограммы для процессоров IA-32; параметры передаются через регистры: вызывающая программа и подпрограмма (а);

содержимое стека после сохранения значения EDI в подпрограмме (б)

На рис. 6.8 показан еще один вариант этой же программы, в котором парамет­ры передаются подпрограмме через стек. Параметры NUM1 и N проталкиваются в стек двумя командами PUSH в вы­зывающей программе. После выполнения команды CALL вершина стека располагается на уровне 2. Регистры EDI, ЕАХ, ЕВХ и ЕСХ используются так же, как в подпрограмме на рис. 5.7. Их значения сохраняются в стеке, затем в них загру­жаются начальные значения и параметры. Эту работу выполняют первые 8 ко­манд подпрограммы. В результате вершина стека оказывается на уровне 3. После сложения чисел при помощи цикла из четырех команд сумма помещается в стек на место параметра NUM1. Выполнив команду RET, команды вызывающей про­граммы ADD и POP удаляют из стека параметр N и помещают результирующую сумму в память по адресу SUM, возвращая вершину стека на уровень 1.

а

б

Рис. 6.8. Программа с рис. 6.7, а, переписанная в виде подпрограммы для процессоров IA-32 (параметры передаются через стек): вызывающая программа и подпрограмма (а);

содержимое стека после сохранения значения EDI в подпрограмме (б)

В завершение темы вызова подпрограмм мы рассмотрим пример обработки вложенных вызовов. На рис. 5.9 приведен код программы на языке ассемблера IA-32, иллюстрирующей пример обработки вложенных вызовов. Стековые фреймы обоих подпрограммы вы видите на рис. 5.10. Указателем на фрейм служит ре­гистр ЕВР. В наборе команд IA-32 имеются команды PUSHAD и POPAD, с помощью которых можно сохранить в стеке и восстановить из него все восемь регистров общего назначе­ния, но в программе на рис. 5.9 мы предпочли воспользоваться отдельными ко­мандами PUSH и POP, поскольку в подпрограммах задействована только поло­вина всех регистров.


Рис. 6.9. Вложенные подпрограммы на языке ассемблера IA-32

 

Рис. 6.10. Стековые фреймы для программы, представленной на рис 6.9. (Вершина стека ESP)

6.7. Примеры программ

6.7.1. Программа для вычисления скалярного произведения двух векторов

На рис. 6.11 приведена программа вычисления скалярного произведения двух векторов для процессоров архитектуры IA-32. Начальные адреса этих векторов равны AVEC и BVEC. В программе для доступа к последовательным элементам векторов используется базовая индексная адресация. В качестве индексного ре­гистра применяется регистр EDI. Коэффициент масштабирования равен 4, по­скольку элементы векторов являются двойными словами (4 байта). В качестве счетчика цикла используется регистр ЕСХ, инициализированный значением n. Это позволяет задействовать команду LOOP (см. раздел 5.2), кото­рая сначала уменьшает значение регистра ЕСХ, а затем выполняет условный пе­реход по адресу LOOPSTART, если содержимое регистра ЕСХ не равно нулю. Предполагается, что произведение двух элементов векторов поместится в двой­ное слово, поэтому в команде умножения IMUL явно задан регистр назначения EDX (см. раздел 5.14).

Рис. 6.11. Программа для процессоров IA-32, вычисляющая скалярное произведение двух векторов

6.7.2. Программа сортировки байтов

 

int main (int argc, char*, argv[])

{

for (j=n-1; j>0; j=j-1)

{for(k=j-1; k >= 0; k = k - 1)

{if (LIST[k] > LIST[j])

{TEMP = LIST[k];

LIST[k] = LIST[j];

LIST[j] = TEMP;

}

}

}

}

 

Рис 6.12. Программа сортировки байтов для процессоров IA-32 на языке С

На рис. 6.13, вы видите программу для процессора IA-32, выполняющую сорти­ровку байтов. Регистр ЕАХ инициализируется значением LIST и используется в качестве базо­вого регистра для доступа к элементам списка при базовой индексной адресации. Регистр EDI служит индексным регистром для внешнего цикла (индекс j), а ре­гистр ЕСХ — индексным регистром для внутреннего цикла (индекс k). В регистре DL содержится текущий наибольший байт сортируемого подсписка. Для замены элементов LIST(k) и LIST(j) программе требуются три команды, а также регистр для временного хранения данных.

В программе введена еще одна неизвестная команда

XCNG operand1, operand 2,

которая меняет значения операндов.

Рис. 6.13. Программа сортировки байтов для процессоров IA-32 на языке ассемблера

 

6.7.3. Подпрограммы для вставки и удаления элементов связного списка

Программы на рис. 6.14 и 6.15, выполняют вставку и удаление элементов связ­ного списка. Па­раметры им передаются через регистры. Причем регистры с именами RHEAD, RNEWREC, RIDNUM, RCURRENT и RNEXT используются так же, как в уни­версальных подпрограммах. Указанные имена применяются вместо имен регист­ров IA-32 ЕАХ, ЕВХ и т. д. Для хранения кода новой вставляемой записи задейст­вован шестой регистр, RNEWID, в который первая команда подпрограммы с рис. 6.14 загружает код новой записи.

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

Рис. 6.14. Подпрограмма для процессоров IA-32, вставляющая в связный список новый элемент

 

Рис. 6.15. Подпрограмма для процессоров ia-32 удаляющая элемент из связного списка

6.8. Различия между программами в ЕХЕ - и СОМ - файлах

Несмотря на то, что существуют служебные программы, например EXE2BIN, которые преобразует ЕХЕ -файл в СОМ -файл, однако нужно знать определенные различия между программой, выполняемой как ЕХЕ -файл и программой, выполняемой как СОМ-файл.

1. Размер программы.

Программа в формате ЕХЕ может иметь любой размер, в то время как СОМ-файл ограничен размером одного сегмента и не превышает 64 К. Размер СОМ-файла всегда меньше, чем размер соответствующего ЕХЕ-файла; одна из причин этого - отсутствие в СОМ-файле 512-байтового заголовка ЕХЕ-файла (префикса программного сегмента, PSP).

2. Сегмент стека.

В ЕХЕ-программе определяется сегмент стека, в то время как СОМ-программа генерирует стек автоматически. Таким образом, при создании ассемблерной программы, которая будет преобразована в СОМ-файл, стек должен быть опущен.

3. Сегмент данных.

В ЕХЕ- программе обычно определяется сегмент данных, а регистр DS инициализируется адресом этого сегмента. В СОM-программе все данные должны быть определены в сегменте кода.

4. Инициализация.

В ЕХЕ-программе выполняются запись нулевого слова в стек и инициализация регистра DS. Так как в СОМ-программе стек и сегмент данных не определены, то эти шаги отсутствуют. Когда СОМ-программа начинает работать, все сегментные регистры содержат адрес префикса программного сегмента (PSP) - 256-байтовый (100Н) блок, который резервируется операционной системой DOS непосредственно перед СОМ- или ЕХЕ-программой в памяти. Так как адресация начинается со смещения 100Н от начала PSP, то в программе после оператора SEGMENT кодируется директива ORG 100Н.

5. Обработка.

Для программ в ЕХЕ- и СОМ-форматах выполняются:

a) ассемблирование для получения OBJ-файла:

Для TASM:

tasm /z /zi /l prog.asm

кроме файла: prog.obj дополнительно создается:

файл.lst (листинг программы (опция /l);

выводится исходный текст вместе с сообщением об ошибках (опция /z);

дополнительная информация для дебагера(опция /zi).

b) компоновка для получения ЕХЕ-файла:

Для TLINK:

tlink /v prog.obj

Создает:

файлы: prog.exe и prog.map

дополнительную информацию для дебагера (опция /v).

Если программа создается для выполнения как ЕХЕ-файл, то ее уже можно выполнить.

Если же программа создается для выполнения как СОМ-файл, то компоновщиком будет выдано сообщение:

Warning: No STACK Segment*

Это сообщение можно игнорировать, так как определение стека в программе не предполагалось.

6.8.1. Пример программы типа СОМ

Программа PROG, приведенная на рис.6.16, написана согласно требованиям СОМ-формата. Обратите внимание на следующие особенности в этой СОМ-программе:

• Сегмент стека и сегмент данных отсутствуют.

• Директива ORG служит для резервирования 100H байт от начального адреса под PSP.

• Директива ORG 100H устанавливает относительный адрес для начала выполнения программы. Программный загрузчик устанавливает этот адрес в командном указателе.

• Команда JMP служит для обхода данных, определенных в программе.

Размеры ЕХЕ- и СОМ-программ - 788 и 20 байт соответственно. Учитывая такую эффективность СОМ-файлов, рекомендуется все небольшие программы создавать в СОМ-формате.

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

;Программа Prog

;Программа складывает два числа и завершается

.MODEL TINY; модель памяти, используемая для СОМ.CODE [SEGMENT]; начало сегмента кода ORG 100H; начальное значение счетчика - 100h, конец PSP

BEGIN: JMP START;обход через данные

FLDA DW 0F77Fh;определение данных

FLDB DW 03219h

FLDC DW?



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2016-02-16 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: