Команды условного перехода и регистр ecx/cx
Регистр ecx/cx имеет определенное функциональное назначение — он выполняет роль счетчика в командах управления циклами и при работе с цепочками символов.
Синтаксис этой команды условного перехода таков:
jcxz метка_перехода (Jump if cx is Zero) — переход, если cx ноль;
jecxz метка_перехода (Jump Equal ecx Zero) — переход, если ecx ноль.
Эти команды очень удобно использовать при организации цикла и при работе с цепочками символов.
Нужно отметить ограничение, свойственное команде jcxz/jecxz. В отличие от других команд условной передачи управления, команда jcxz/jecxz может адресовать только короткие переходы — на –128 байт или на +127 байт от следующей за ней команды.
Организация циклов
Цикл, как известно, представляет собой важную алгоритмическую структуру, без использования которой не обходится, наверное, ни одна программа.
Организовать циклическое выполнение некоторого участка программы можно, к примеру, используя команды условной передачи управления или команду безусловного перехода jmp. При такой организации цикла все операции по его организации выполняются “вручную”. Но, учитывая важность такого алгоритмического элемента, как цикл, разработчики микропроцессора ввели в систему команд группу из трех команд, облегчающую программирование циклов. Эти команды также используют регистр ecx/cx как счетчик цикла.
Дадим краткую характеристику этим командам:
loop метка_перехода
(Loop) — повторить цикл.
Команда позволяет организовать циклы, подобные циклам for в языках высокого уровня с автоматическим уменьшением счетчика цикла. Работа команды заключается в выполнении следующих действий:
•декремента регистра ecx/cx;
•сравнения регистра ecx/cx с нулем:
•если (ecx/cx) > 0, то управление передается на метку перехода;
•если (ecx/cx) = 0, то управление передается на следующую после loop команду.
loope/loopz метка_перехода
(Loop till cx <> 0 or Zero Flag = 0) — повторить цикл, пока cx <> 0 или zf = 0.
Команды loope и loopz — абсолютные синонимы, поэтому используйте ту команду, которая вам больше нравиться. Работа команд заключается в выполнении следующих действий:
•декремента регистра ecx/cx;
•сравнения регистра ecx/cx с нулем;
•анализа состояния флага нуля zf:
•если (ecx/cx) > 0 и zf = 1, управление передается на метку перехода;
•если (ecx/cx) = 0 или zf = 0, управление передается на следующую после loop команду.
loopne/loopnz метка_перехода
(Loop till cx <> 0 or Not Zero flag=0) — повторить цикл пока cx <> 0 или zf = 1.
Команды loopne и loopnz также абсолютные синонимы. Работа команд заключается в выполнении следующих действий:
•декремента регистра ecx/cx;
•сравнения регистра ecx/cx с нулем;
•анализа состояния флага нуля zf:
•если (ecx/cx) > 0 и zf = 0, управление передается на метку перехода;
•если (ecx/cx)=0 или zf=1, управление передается на следующую после loop команду.
Команды loope/loopz и loopne/loopnz по принципу своей работы являются взаимообратными. Они расширяют действие команды loop тем, что дополнительно анализируют флаг zf, что дает возможность организовать досрочный выход из цикла, используя этот флаг в качестве индикатора.
Недостаток команд организации цикла loop, loope/loopz и loopne/loopnz в том, что они реализуют только короткие переходы (от –128 до +127 байт). Для работы с длинными циклами придется использовать команды условного перехода и команду jmp.
Работа со стеком
Стек — это область оперативной памяти, специально выделяемая для временного хранения данных программы.
Для стека в структуре программы предусмотрен отдельный сегмент (если программист забыл описать сегмент стека, компоновщик tlink выдаст предупреждающее сообщение).
Для стека можно отвести любую область памяти, но ее размер зависит от режима работы микропроцессора и ограничивается 64 Кбайт (или 4 Гбайт в защищенном режиме), а начальный адрес должен быть выровнен на границу параграфа (т.е. д.б. кратным 16).
!!! Даже если сама программа не использует стек, сегмент стека в программе все равно надо описывать. Так как стек программы использует ОС при обработке прерываний, возникающих во время выполнения программы.
Рекомендуемый размер стека (с запасом) - 128 байтов.
В каждый момент времени доступен только один стек, адрес сегмента которого содержится в регистре ss. Этот стек называется текущим.
Особенности организации стека.
1. Запись и чтение данных в стеке осуществляется в соответствии с принципом LIFO (Last In First Out — “последним пришел, первым ушел”).
2. По мере записи данных стек растет в сторону младших адресов. Эта особенность заложена в алгоритм команд работы со стеком; Др. словами, стек заполняется снизу вверх: первый элемент записывается в самый конец стека (в ячейку с наибольшим адресом), а следующий элемент записывается «над» ним.
3. При чтении из стека первым всегда удаляется верхний элемент, поэтому низ стека фиксирован, а вершина стека все время сдвигается. Текущее положение этой вершины наз. вершиной стека. Адрес вершины стека храниться в регистре SP (stack pointer, указатель стека). Т.е. в регистре SP хранится смещение той ячейки, в которой находится элемент, записанный в стек последним. Смещение относительно начала сегмента стека. Итак, абсолютный адрес вершины стека задается парой сегментов SS:SP. Команды работы со стеком неявно изменяют этот регистр так, чтобы он указывал всегда на последний записанный в стек элемент. Если стек пуст, то значение esp равно адресу последнего байта сегмента, выделенного под стек.
Замечание: Регистр ss автоматически используется процессором для выполнения всех команд, работающих со стеком. Это означает, что при использовании регистров esp/sp и ebp/bp для адресации памяти ассемблер автоматически считает, что содержащиеся в нем значения представляют собой смещения относительно сегментного регистра ss и поэтому префикс SS можно не указывать.
4. Элементы стека могут иметь любой размер (байт, слово и т.д.). Но команды записи и чтения, предназначенные для стека, работают только со словами, т.е. информация, помещаемая в стек и извлекаемая из него, имеет длину 2 байта.
Для работы со стеком предназначены три регистра:
•ss — сегментный регистр стека;
•sp/esp — регистр указателя стека;
•bp/ebp — регистр указателя базы кадра стека.
Команды работы со стеком.
Запись слова в стек
push операнд — запись значения операнд в вершину стека.
Операнд м. находиться в регистре общего назначения, в сегментном регистре, в памяти
Нельзя непосредственный операнд !!!
При реализации этой команды выполняются следующие действия
1. (sp) = (sp) – 2; значение sp уменьшается на 2;
2. значение из операнд записывается по адресу ss:sp.
Чтение слова из стека
pop операнд — запись значения из вершины стека по месту, указанному операндом.
Алгоритм работы команды pop обратен алгоритму команды push
1. значение из вершины стека пересылается в операнд
2. (sp) = (sp) + 2; значение sp увеличивается на 2.
!!! Операндом не может быть регистр CS
!!! Команды PUSH и POP не осуществляют проверку на выход за пределы стека.
Например, если стек пуст, а мы считываем слово из стека, будет считано слово за сегментом стека.
Проверку обязан выполнять программист. Как?
n если стек полон, то смещение вершины стека =0 , т.е. проверить SP=0 ?
n если стек пуст, то смещение вершины стека = размеру стека в байтах.
Чтобы очистить стек можно выполнить определенное число раз команду POP, но это неэффективно. Лучше
1) запомнить вначале то значение SP, до которого затем нужно будет очищать стек, а затем его восстановить mov
ax,sp
. . .
mov sp,ax
2) еще лучше add sp,2*n
Для этого надо заметить, что после удаления из стека n слов значение SP должно увеличиться на 2*n. А регистр SP явл. регистром общ. Назначения и его м. исп-ть в любых командах
Запись в стек и чтение регистра флагов
pushf — записывает регистр флагов в стек
Работа этой команды зависит от атрибута размера сегмента:
•use16 — в стек записывается регистр flags размером 2 байта;
•use32 — в стек записывается регистр eflags размером 4 байта.
popf - считывает слово из стека и записывает в регистр флагов
Замечание. Команды PUSHF и POPF - единственные в системе команд микропроцессора, которые позволяют получить доступ ко всему содержимому регистра флагов.
Доступ к элементам стека
Команды PUSH и POP дают доступ только к вершине стека.
Для получения доступа к другим элементам стека необходимо использовать модификацию адреса с помощью регистра BP следующим образом:
1) установить регистр BP на вершину стека
2) для обращения к элементу использовать конструкцию вида [BP+n], где n - число байт, на которое смещен элемент относительно вершины стека.
Пример. Пусть в стек были помещены три слова А, В, С.
Необходимо обратиться к элементу A.
Адрес третьего слова стека равен адресу вершины стека плюс 4. Поэтому
mov bp,sp
mov ax,[bp+4]
Почему необходимо использовать именно BP?
1. Сам SP нельзя так как он не явл. регистром-модификатором
2. При использ. регистра BP сегментным регистром по умолчанию выбирается SS
и тем самым мы сокращаем запись mov ax,ss:[bp+4] и считываем ячейку из стека
Использование стека
· для временного сохранения значений регистров
например, push cx
. . . ; например во вложенных циклах
pop cx
· для сохранения текущих состояний флагов и последующего их восстановления
pushf
popf
· для определения или изменения состояния любого флага
Пример. Записать в регистр AX значение флага трассировки TF (это 8-й бит), не изменяя при этом никакие флаги (!)
pushf ; запомним для последующего восстановления
pushf ; запомним для пересылки в ax
pop ax
mov cl,8 ; счетчик сдвигов
shr,cl ; сдвиг бита TF к правому краю AX
and ax,1b ; теперь AX:=TF
popf ; восстановить исходный Flags
· для пересылки данных
например, X:=Y push Y
pop X
· при работе с процедурами;
· при определении локальных переменных.
Процедуры
Понятие
Процедура, часто называемая также подпрограммой, — это основная функциональная единица декомпозиции (разделения на несколько частей) некоторой задачи.
Процедура представляет собой группу команд для решения конкретной подзадачи
Процедура - именованная, правильным образом оформленная группа команд, которая, будучи однократно описана, при необходимости может быть вызвана по имени любое количество раз из различных мест программы.
Описание процедуры
Для описания последовательности команд в виде процедуры в языке ассемблера используются две директивы: PROC и ENDP.
Синтаксис описания процедуры таков
<имя> proc [тип]
<тело процедуры>
<имя> endp
<Тип> может принимать значения near или far и характеризует возможность обращения к процедуре из другого сегмента кода. Тип near позволяет обращаться к процедуре только из того сегмента кода, в котором она описана. К процедуре типа FAR можно обращаться и из других сегментов тоже. По умолчанию тип принимает значение near.
Замечание. Имя процедуры по сути является меткой, т.е. с помощью простых команд перехода можно осуществить переход на первую команду процедуры.
Размещение в сегменте кода
Процедура может размещаться в любом месте программы, но так, чтобы на нее случайным образом не попало управление.
Если процедуру просто вставить в общий поток команд, то микропроцессор будет воспринимать команды процедуры как часть этого потока и соответственно будет осуществлять выполнение команд процедуры.
Если имеется несколько процедур их обычно размещают рядом.
1. В начале сегмента кода
!!! Процедура должна быть расположена до первой выполняемой команды
Пример. .code
name proc ; ближняя по умолчанию
. . .
name endp
start: . . .
end start
2. В конце сегмента кода
!!! Процедура должна быть размещена после команды, возвращающей управление ОС
Пример .code
start: . . .
mov ax,4C00h
int 21h
name proc near
. .
name endp
end start
3. Можно разместить процедуру внутри сегмента кода
!!! Необходимо предусмотреть обход процедуры
Пример .code
start: . . .
jmp metka
name proc
. . .
name endp
metka: . . .
end start
4. Процедура может быть описана в другом сегменте кода (об этом позже)
Вызов процедур
В принципе можно было бы обратиться к процедуре, просто переходом на метку - имя процедуры. Но!!! Необходимо сохранить где-то адрес возврата, т.е. адрес команды, следующей после процедуры.
В систему команд микропроцессора была введена специальная команда
Вызов процедуры, или переход с возвратом
CALL [модификатор] <имя процедуры>
Команда записывает адрес следующей за ней команды в стек, а затем осуществляет переход по метке <имя процедуры>.
Вспомним, что адрес следующей выполняемой команды задается парой CS:IP.
Если процедура описана как
· дальняя, то в стек по очереди заносятся два значения CS, IP.
· короткая - только значение IP
И при этом соответственно переход считается коротким или дальним.
Поэтому в с-ме команд имеется два варианта команды CALL.
!!! Ассемблер выберет правильный вариант, если процедура была описана до вызова. Если процедура будет описана позже транслятор считает процедуру ближней и формирует команду ближнего вызова. Если процедура окажется дальней, будет зафиксирована ошибка.
Для указания транслятору на то, что процедура, описанная ниже, будет дальней можно использовать <модификатор> со значением FAR PTR
Возврат из процедур
Адрес возврата в вызывающую программу хранится в стеке.
Команда RET [число] возвращает управление вызывающей программе.
Она считывает адрес возврата из вершины стека, загружает его в регистры CS и IP (теперь выполняться будет следующая за CALL команда в программе) адрес возврата при этом удаляется из стека(!), затем стек очищается на указанное число байт и выполняется переход по адресу возврата.
Команда RET соответствует команде RET 0
В зависимости от того, дальняя или ближняя была описана процедура, ассемблер формирует одну из возможных команд RET. В первом случае из стека извлекается два слова, которые загружаются в регистры CS и IP, во втором - одно слово в регистр IP.
Команды CALL и RET действуют согласованно, за соответствием между командами следит транслятор.
Необязательный параметр [число] задает значение, удаляемое из стека при возврате из процедуры - в байтах (use16) или в словах (use32)
Параметры процедур
Передача значений в процедуру называется передачей параметров.
Результатом выполнения процедуры может быть некоторое значение, которое необходимо передать вызывающей программе. Передача результата из процедуры в программу тоже организовывается как передача параметров.
Передавать параметры можно
· через регистры
· через стек
· через общую область памяти
· с помощью директив extern и public
Существует два способа передачи данных:
1. Передача параметров по значению
В этом случае передаются сами данные, т.е. их значения
!!! При передаче параметров по значению в процедуре обрабатываются их копии. Поэтому значения переменных в основной программе не изменяются !!!
2. Передача параметров по ссылке
В этом случае передаются адреса (ссылки) параметров
!!! Здесь обрабатываются не копии, а сами данные, поэтому при изменении данных в процедуре они автоматически изменяются и в вызывающей программе !!!
Т.Е. Передача параметра по ссылке означает передачу адреса ячейки, соответствующей фактическому параметру.
Передача параметров через регистры
Самый простой способ передачи данных. Его суть заключается в следующем:
Основная программа записывает фактические параметры в какие-нибудь регистры, а процедура берет их оттуда и обрабатывает.
Аналогично поступают с результатом. Процедура помещает результат в какой-то регистр, а программа извлекает его оттуда.
!!! Если размер данных превышает 16 или 32 бит (размер регистра), то передавать нужно
Передача параметров через стек
Суть: основная программа записывает параметры в стек, а процедура извлекает их оттуда.
!!! Результат лучше передавать через регистр. Иначе нужно осторожно чистить стек при выходе из процедуры.
Перед обращением к процедуре основная программа должна поместить в стек фактические параметры. Модель передачи (т.е. в каком порядке) определяет программист.
Процедура должна получить доступ к этим элементам. Т.к. они не на вершине стека, будем пользоваться регистром BP.
Таким образом, обратиться к первому (сверху) (а на самом деле это N параметр списку) элементу стека можно [BP+4], к следующему - [BP+6] и т.д.
!!! Если процедура является дальней, то следует еще +2, т.к. для адреса возврата поместят два элемента в стек (CS, IP). Для доступа к параметру N - [BP+6] и т.д.
Перед завершением процедуры необходимо выполнить действия по корректному возврату из процедуры.
Необходимо 1) поместить на вершину стека адрес возврата из процедуры
2) очистить стек от параметров (они уже стали ненужными)
(мы к ним только обращались, но не удаляли из стека)
1) mov SP,BP ; восстановить SP
pop BP ; восстановление старого BP
Теперь на вершине стека адрес возврата и можем выполнять RET
2) Очистить стек от параметров может основная программа ADD SP,2*N
Лучше очищать стек в самой процедуре.
Можно воспользоваться параметром [число] команды RET.
Этот параметр задает число байтов, на которое очищается стек.
Макросредства языка Ассемблер
Макросредства - инструменты (средства) модификации текста программы во время ее трансляции.
Макросредства предназначены для облегчения написания программ на языке Ассемблер и для улучшения понимания исходного текста программы.
Основная идея. Повторяющийся фрагмент программы специальным образом описывается (макрос), именуется, а затем в нужных местах программы указывается ссылка на него. При создании объектного кода вместо ссылки подставляется сам фрагмент, т.е. происходит подстановка фрагмента вместо ссылки.
Транслятор ассемблера состоит из двух частей: макрогенератора и непосредственно транслятора. Транслятор при этом называют макроассеблером. Именно таким макроассемблером мы пользуемся в системе MASM (macroassembler language).
Обработка программы с использованием макросредств осуществляется транслятором в два этапа. На первом этапе работает макрогенератор, который производит замены для всех макросов, а на втором этапе уже преобразованный текст программы транслируется в объектный код.
Макроопределение
Это описание макроса. Синтаксис макроопределения:
<имя макрокоманды> MACRO [формальные параметры]
тело макроопределения
ENDM
Директива MACRO - это заголовок макроопределения. В ней указывается имя и через запятую перечисляются формальные параметры, если необходимо.
Формальные параметры позволяют копировать макрос не в неизменном виде, а с изменениями. Те величины, которые необходимо будет изменить описываются формальными параметрами.
Замечание. Имена формальных параметров локальны по отношению к макросу, т.е. они могут совпадать с именами в основной программе, макрогенератор понимает их как параметры.
Завершает макроопределение директива ENDM. !!! Не надо повторять имя макроса.
Пример 1 ; настройка сегмента данных
initds macro
mov ax, @data
mov ds, ax
endm
Размещаться макроопределения могут :
1. В любом месте программы.
!!! Обязательно до первой ссылки на него.
2. В отдельном файле.
Чтобы сделать доступными макроопределения в программе, необходимо в начале программы использовать директиву INCLUDE <имя файла>. При этом на этапе работы макрогенератора текст указанного файла будет вставлен полностью на место директивы.
Пример. Masm
model small
include Mymacro.inc
. . .
Можно универсальные макрокоманды записать в один файл, в так называемую макробиблиотеку. Подключать ее с помощью директивы include.
Чтобы в текст программы не включать лишние макроопределения, можно воспользоваться директивой
PURGE <список через запятую имен макроопределений>
Директива указывает, какие макроопределения не должны включаться в текст программы.
Пример. . . .
include mymacro.inc
purge outstr, initds
. . .
Макрокоманды
Макрокоманда - обращение к макроопределению. Или указание макрогенератору на то, что на указанном месте необходимо подставить тело макроопределения. Итак, одна макрокоманда заменяется на группу команд, поэтому она и наз. макро (большая).
Синтаксис макрокоманды:
<имя макроса> [<фактические параметры>]
Замечание. Фактические параметры можно разделять запятыми или пробелами.
Формальные параметры макроопределения заменяются соответствующими фактическими параметрами макрокоманды.
!!! i-тый фактич. Параметр соответствует i-тому формальному параметру
!!! Число фактических параметров должно быть равно числу формальных параметров,
если фактических параметров больше, то лишние игнорируются
если формальных больше, считается что в качестве недостающих фактических указаны пустые тексты
Процесс замещения формальных параметров фактическими называется макроподстановкой. Результат макроподстановки (т.е. полученный в результате текст называется макрорасширением.
Действия макрогенератора:
1) макрогенератор находит макроопределение с указанным именем
2) в его теле заменяет все формальные параметры фактическими
3) полученное макрорасширение подставляет в программу вместо макрокоманды
Сравнительный анализ процедур и макросредств
Повторяющиеся действия (фрагменты) в программе можно описать и как процедуру, и как макроопределение. При этом в обоих случаях повторяющийся участок кода описан только один раз, а обращаемся к нему с помощью одной команды (вызов процедуры или макрокоманда). Но...
После трансляции процедура так и останется описанной один раз, а тело макроопределения подставится во все места вызова и тем самым увеличит размер программы.
Вывод 1. Применение процедур делает код более компактным, т.е. экономим память
Но при обращении к процедуре
а) выполняется засылка параметров в регистры или стек,
б) запоминается адрес возврата
в) осуществляется переход,
г) по окончании работы процедуры восстанавливается адрес возврата,
д) очищаются регистры или стек и т.п.
Итак, при работе процедуры тратится время на переходы и передачу параметров во время выполнения программы.
!!! При замене макрокоманд на макрорасширения тоже тратится время, но это происходит на этапе трансляции, а не во время выполнения программы.
Вывод 2. Применение макросредств экономит время выполнения программы.
Поэтому в программах критических по времени следует применять макросредства, а если необходимо экономить память следует применять процедуры.
Если в повторяющемся участке кода много команд (т.е. большой фрагмент) лучше описать его как процедуру. Если же небольшую группу команд описать процедурой, то число вспомогательных команд по ее вызову и передаче параметров станет сравнимым с числом команд самой процедуры, ее время выполнения станет на много больше.
Вывод 3. Большие участки кода рекомендуется описывать как процедуры, а маленькие - как макроопределения.
!!! Еще одно отличие использования макросредств и процедур заключается в том, что параметрами процедур могут быть только операнды команд, а параметрами макрокоманд могут быть любые последовательности символов, в том числе и сами команды.
Дата добавления: 2016-02-20; просмотров: 1867;