ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 11.04.2019
Просмотров: 5826
Скачиваний: 8
Слова "выше" и "ниже" в таблице употребляются при сравнении беззнаковых целых чисел, слова "больше" и "меньше" учитывают знак. Например: число -1меньше нуля, но если его рассматривать как беззнаковое, то оно будет выше нуля, потому что примет значение 65535 (0FFFFh). При выборе команды условного перехода в своей программе вы должны учитывать, будет выполняться сравнение знаковых или беззнаковых чисел.
Если команды условного перехода только проверяют флаги, то понятно, что должны присутствовать в ассемблере команды меняющие флаги. Обычно флаги меняют арифметические команды, устанавливая, например, флаг переноса или переполнения в результате своей работы (с арифметическими командами мы познакомимся на пятом дне). Эти флаги могут проверять соответствующие команды условного перехода, выполняя переход на метку или не выполняя. Однако чаще всего команды условного перехода работают совместно с командой сравнения (CMP происходит от англ. compare — сравнение):
CMP op1,op2
Эта команда сравнивает величины op1 и op2 и устанавливает соответствующие флаги в регистре флагов. На самом деле эта команда просто вычисляет разность op1-op2без сохранения результата. Понятно, что возможны следующие варианты: op1<op2, op1<=op2, op1=op2, op1>=op2 и op1>op2. Таблица 3.1 поможет вам определить условия CMP, при которых срабатывают соответствующие переходы. Например, если операнды op1 и op2 не равны между собой, то команда JNE выполнит переход на указанную метку.
В листинге 3.4 показана программа, которая сравнивает два числа, задаваемые в регистрах AX и BX, и выводит результат сравнения на экран. Ассемблирование выполняется как обычно:
ml file.asm
После выполнения программа выдаст на экран результат:
Число в AX больше, чем в BX.
Сравнение осуществляется с учетом знака, поэтому можно в AX и BX задавать как положительные, так и отрицательные числа. Попробуйте подставлять различные значения и после ассемблирования посмотреть результаты.
Листинг 3.4. Сравнение двух чисел (jump2.asm)
.model tiny
.code
org 100h
|
start: |
|
|
mov |
ax,3 |
|
mov |
bx,-15 |
|
cmp |
ax,bx |
|
jle |
Lab |
|
mov |
ah,9 |
|
mov |
dx,offset message1 |
|
int |
21h |
|
ret |
|
|
Lab: |
|
|
mov |
ah,9 |
|
mov |
dx,offset message2 |
|
int |
21h |
|
ret |
|
|
message1 |
db "Число в AX больше, чем в BX.",0Dh,0Ah,'$' |
|
message2 |
db "Число в AX меньше или равно значению в BX.",0Dh,0Ah,'$' |
|
end |
start |
Команды CMP и Jcc вместе аналогичны оператору if... then..., который имеется практически во всех языках высокого уровня. Например, команда на языке BASIC:
if AX=>BX then goto Lab
на языке ассемблера будет выглядеть следующим образом:
cmp ax,bx
jge Lab
-
Оператор цикла. Применение оператора цикла.
На втором дне мы с вами научились создавать программу, которая выводит на экран фразу "Hello, World!".
А теперь представим, что нам требуется создать программу, которая должна вывести на экран пять раз фразу"Hello, World!". Конечно, можно просто пять раз выполнить в программе операцию вывода, как показано в листинге 3.1.
Листинг 3.1. Вывод на экран пять раз "Hello, World!" без использования цикла
(loop1.asm)
.model tiny
.code
org 100h
|
start: mov |
ah,9 |
|
|
mov |
dx,offset message |
|
|
int |
21h |
|
|
mov |
ah,9 |
|
|
mov |
dx,offset message |
|
|
int |
21h |
|
|
mov |
ah,9 |
|
|
mov |
dx,offset message |
|
|
int |
21h |
|
|
mov |
ah,9 |
|
|
mov |
dx,offset message |
|
|
int |
21h |
|
|
mov |
ah,9 |
|
|
mov |
dx,offset message |
|
|
int |
21h |
|
|
ret |
|
|
|
message |
db |
"Hello, World!",0Dh,0Ah,'$' |
end start
Выполните трансляцию и линковку этой программы: ml loop1.asm
Затем запустите полученный файл loop1.com, вы должны увидеть на экране:
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Программа успешно работает, но нерациональность такого решения очевидна. Нам приходится набирать один и тот же участок кода в пяти экземплярах. Возможно, пять раз не столь ощутимо, но представим, что нам бы потребовалось вывести 100 или даже 1000 раз фразу "Hello, World!"?
Чтобы решить эту проблему для выполнения повторяющихся действий в любом языке программирования присутствует оператор цикла. В ассемблере для создания циклов служит команда LOOP, которая имеет следующий синтаксис:
loop метка
Метка служит для передачи управления на ту команду (или последовательность команд), которая должна повторяться. Число повторов для команды LOOP всегда задается в регистре CX (для 16-битныхпрограмм) или в регистре ECX (для 32битных программ) (неслучайно регистр CX называется "счетчик"). Обычно значение
вCX заносится с помощью команды MOV CX,N, где N – целое число большее нуля.
Вобщем виде цикл на ассемблере с использованием LOOP организуется следующим образом:
|
MOV CX, N |
; устанавливаем число повторов (N>0) |
|
Mark: |
; метка |
|
... |
; тело цикла будет выполнять N раз |
|
LOOP Mark |
|
В листинге показана программа с применением цикла для вывода пять раз фразы
"Hello, World!".
Листинг 3.2. Вывод на экран пять раз "Hello, World!" с использованием цикла
(loop2.asm)
.model tiny
.code
org 100h
|
start: |
|
|
|
|
mov |
cx,5 |
; устанавливаем число повторов (5 раз) |
|
|
Mark: |
|
|
|
|
mov |
ah,9 |
|
; тело цикла |
|
mov |
dx,offset message |
; |
|
|
int |
21h |
|
; |
|
loop |
Mark |
|
|
|
ret |
|
|
|
|
message |
db |
"Hello, World!",0Dh,0Ah,'$' |
|
|
end |
start |
|
|
Как видите, программа получилась значительно короче, чем в листинге 3.1.
Файл после трансляции и линковки тоже получается меньше, чем в предыдущем примере, за счет того, что мы использовали меньшее количество инструкций.
Стоит отметить важную деталь, что после каждой итерации цикла значение в CX (или в ECX для 32-битныхпрограмм) уменьшается на 1. То есть, в нашем случае после вывода первой фразы "Hello, World!" в CX останется значение 4, после вывода второй фразы в CX останется 3 и т. д. Цикл завершается, когда в CX остается значение 0.
Существуют разновидности команды LOOP.
Следующие две команды работают одинаково (имеют одинаковые коды): loope метка
loopz метка
Переход на метку осуществляется, если значение регистра CX ≠ 0 и значение флага
ZF = 1.
Следующие две команды также имеют одинаковые коды, а, значит, работают одинаково:
loopne метка loopnz метка
В них переход на метку осуществляется, если CX ≠ 0 и ZF = 0.
Также как для обычной команды LOOP число повторов для перечисленных четырех команд задается в регистре CX (для 16-битныхпрограмм) или в регистре ECX (для32-битныхпрограмм). Содержимое регистра CX (ECX) уменьшается на один после каждой итерации, команды заканчивают выполнение, когда значение в CX (ECX) становится равным нулю.
-
Использование стека для организации вложенного цикла.
Вложенный цикл — это цикл внутри другого цикла. Чтобы сделать вложенный цикл необходимо сохранить значение CX перед началом вложенного цикла и восстановить после его завершения (перед командой LOOP внешнего цикла).
При необходимости организовать вложенные циклы, для сохранения счетчика внешнего цикла на время выполнения внутреннего удобно воспользоваться стеком. В следующем фрагменте организуется временная задержка длительностью несколько секунд (конкретная величина задержки зависит от скорости работы процессора).
Например, программные задержки позволяют синхронизовать работу аппаратуры, подключенной к компьютеру, если скорость отработки аппаратурой посылаемых в нее из компьютера команд меньше скорости процессора.
В приведенном выше фрагменте внешний цикл выполняется 2000 раз; внутренний - 65536 раз. При счете числа шагов внутреннего цикла используется явление оборачивания(Если последовательно увеличивать содержимое какого-либо регистра или ячейки памяти, то, достигнув верхнего возможного предела FFFFh, число "перевалит" через эту границу, станет равным нулю и продолжит нарастать в области малых положительных чисел (1, 2, 3, и т.д.). Точно так же, если последовательно уменьшать некоторое положительное число, то оно, достигнув нуля, перейдет в область отрицательных (или, что то же самое, больших беззнаковых) чисел, проходя значения 2, 1, 0, FFFFh, FFFEh и т.д.).
FFFFh+0001h=0000h
0000h-0001h=FFFFh
Начальное значение в регистре СХ равно нулю; после выполнения тела цикла 1 раз команда loop уменьшает содержимое СХ на 1, что дает число FFFFh (которое можно рассматривать, как -1). В результате цикл повторяется еще 65535 раз, а в сумме - точно 64 К шагов.
Команда loop внутреннего цикла передает управление на саму себя, т.е. тело внутреннего цикла состоит из единственной команды loop. В этом нет ничего незаконного. Любая команда, в том числе и loop, требует какого-то времени для своего выполнения, и повторение 64 К раз команды loop дает некоторую временную задержку (на современных процессорах порядка тысячной доли секунды).
-
Понятие подпрограммы. Понятие подпрограммы. Особенности написания подпрограмм на языке Ассемблер. Понятие ближней, дальней подпрограммы. Особенности написания и использование ближней и дальней подпрограмм.
Подпрограмма (англ. subroutine) — поименованная или иным образом идентифицированная часть компьютерной программы, содержащая описание определённого набора действий. Подпрограмма может быть многократно вызвана из разных частей программы.
В больших программах, чтобы избежать повторного написания группы из одних и тех же команд, их обычно оформляют в виде одной подпрограммы и вызывают по мере необходимости из любых мест программы.
Например, если часто нужно выводить на экран различные строки, то команды mov ah,9
int 21h
можно оформить в отдельную процедуру и вызывать ее тогда, когда требуется вывод на экран.
Подпрограммы оформляются с помощью двух директив: PROC (от англ. "procedure" — процедура) и ENDP (от англ. "end of procedure" — конец процедуры)
следующим образом:
имя_процедуры PROC параметр тело_процедуры
RET
имя_процедуры ENDP
В обеих директивах указывается одно и то же имя процедуры.
Директива PROC может иметь параметр — NEAR (от англ. близкий) или FAR (от англ. дальний). Если параметр отсутствует, то ассемблер будет считать, что он равен NEAR (в связи с этим параметр NEAR обычно не указывается). Соответственно процедура, оформленная с параметром NEAR (или с отсутствующим параметром) называется "близкой", а с параметром FAR — "дальней". К близкой процедуре нельзя обращаться из других сегментов команд, а к дальней — можно.
Вызов процедуры осуществляется с помощью команды CALL, которая имеет следующий синтаксис:
CALL имя_процедуры
А возврат из процедуры осуществляется с помощью команды RET.
В листинге 3.5 показана программа с применением процедуры. В процедуре (я дал ей имя Output) размещаются команды вызова функции DOS 9h. Процедура вызывается три раза для вывода на экран трех разных строк.
Листинг 3.5. Пример работы процедуры (proc.asm)
.model tiny
.code
org 100h
start:
|
mov |
dx,offset message1 |
|
call |
Output |
|
mov |
dx,offset message2 |
|
call |
Output |
|
mov |
dx,offset message3 |
|
call |
Output |
|
ret |
|
|
|
|
||
|
Output PROC |
|
|
|
|
mov |
ah,9 |
|
|
|
int |
21h |
|
|
|
ret |
|
|
|
|
Output ENDP |
|
|
|
|
message1 |
db |
"Hello, Ivan!",0Dh,0Ah,'$' |
|
|
message2 |
db |
"Hello, Petr!",0Dh,0Ah,'$' |
|
|
message3 |
db |
"Hello, Fedor!",0Dh,0Ah,'$' |
|
end start
-
Передача данных в подпрограмму.
Для передачи аргументов в языке ассемблера существуют следующие способы:
-
через регистры;
-
через общую область памяти;
-
через стек;
Существует два способа передачи данных:
1. Передача параметров по значению
В этом случае передаются сами данные, т.е. их значения
!!! При передаче параметров по значению в процедуре обрабатываются их копии. Поэтому значения переменных в основной программе не изменяются !!!
2. Передача параметров по ссылке
В этом случае передаются адреса (ссылки) параметров
!!! Здесь обрабатываются не копии, а сами данные, поэтому при изменении данных в процедуре они автоматически изменяются и в вызывающей программе !!!
Т.Е. Передача параметра по ссылке означает передачу адреса ячейки, соответствующей фактическому параметру.
Передача параметров через регистры
Самый простой способ передачи данных. Его суть заключается в следующем:
Основная программа записывает фактические параметры в какие-нибудь регистры, а процедура берет их оттуда и обрабатывает.
Аналогично поступают с результатом. Процедура помещает результат в какой-то регистр, а программа извлекает его оттуда.
!!! Если размер данных превышает 16 или 32 бит (размер регистра), то передавать нужно не сами данные, а ссылки на них.
Совет 1 Рекомендуется тщательно комментировать процедуру и основную программу.
Необходимо описать в какие регистры какие параметры помещаются, в какие регистры помещается результат.
Совет 2. Рекомендуется использовать при небольшом количестве параметров, т.к. число доступных регистров ограничено.
mov ax,value ; сохраняем значение нашей переменной в регистре ах
call procedure ; вызываем процедуру
Через
общую область
Вызывающая
и вызываемая процедура «договариваются»
о совместном использовании некоторой
области памяти.
Сегменты данных при
этом должны иметь одинаковые имена
и тип
комбинирования common
Передача параметров через стек
Суть: осн. программа записывает параметры в стек, а процедура извлекает их оттуда.
!!! Результат лучше передавать через регистр. Иначе нужно осторожно чистить стек при выходе из процедуры.
Перед обращением к процедуре основная программа должна поместить в стек фактические параметры. Модель передачи (т.е. в каком порядке) определяет программист.
; основная программа
. . .
push par1
push par2
. . .
push parN
call name
Замечание! При вызове процедуры в стек запишется адрес возврата, т.о. параметры будут находиться «под» ним.
Процедура должна получить доступ к этим элементам. Т.к. они не на вершине стека, будем пользоваться регистром BP.
Замечание! Регистр BP может использоваться в основной программе, поэтому предварительно следует запомнить его старое значение. Лучше тоже в стеке, но надо будет это учитывать!
Таким образом, обратиться к первому (сверху) (а на самом деле это N параметр списку) элементу стека можно [BP+4], к следующему - [BP+6] и т.д.
!!! Если процедура является дальней, то следует еще +2, т.к. для адреса возврата поместят два элемента в стек (CS, IP). Для доступа к параметру N - [BP+6] и т.д.
Перед завершением процедуры необходимо выполнить действия по корректному возврату из процедуры.
Необходимо 1) поместить на вершину стека адрес возврата из процедуры
2) очистить стек от параметров (они уже стали ненужными)