ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 11.04.2019

Просмотров: 5830

Скачиваний: 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

  1. Оператор цикла. Применение оператора цикла.

На втором дне мы с вами научились создавать программу, которая выводит на экран фразу "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) становится равным нулю.

  1. Использование стека для организации вложенного цикла.

Вложенный цикл — это цикл внутри другого цикла. Чтобы сделать вложенный цикл необходимо сохранить значение 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 дает некоторую временную задержку (на современных процессорах порядка тысячной доли секунды).

  1. Понятие подпрограммы. Понятие подпрограммы. Особенности написания подпрограмм на языке Ассемблер. Понятие ближней, дальней подпрограммы. Особенности написания и использование ближней и дальней подпрограмм.


Подпрограмма (англ. 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. Передача данных в подпрограмму.

Для передачи аргументов в языке ассемблера существуют следующие способы:

  • через регистры;

  • через общую область памяти;

  • через стек;

Существует два способа передачи данных:

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, т.к. для адреса возврата поместят два элемента в стек (CSIP). Для доступа к параметру N - [BP+6] и т.д.

Перед завершением процедуры необходимо выполнить действия по корректному возврату из процедуры.

Необходимо 1) поместить на вершину стека адрес возврата из процедуры

2)  очистить стек от параметров (они уже стали ненужными)