Buderus-trade.ru

Теплотехника Будерус
0 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

11. Циклы и команда LOOP; i8086

11. Циклы и команда LOOP — i8086

Статья основана на материале xrnd с сайта asmworld (из учебного курса по программированию на ассемблер 16-битного процессора 8086 под DOS).

До этой части все наши программы выполнялись последовательно — в них не было ветвлений и переходов. Сегодня мы научимся делать простейшие циклы. Циклом называется повторяющееся выполнение последовательности команд. Но для начала нужно научиться объявлять метки.

Синтаксис объявления меток

Метка представляет собой символическое имя, вместо которого компилятор подставляет адрес. В программе на ассемблере можно присвоить имя любому адресу в коде или данных. Обычно метки используются для организации переходов, циклов или каких-то манипуляций с данными. По сути имена переменных, объявленных с помощью директив объявления данных, тоже являются метками. Но с ними компилятор дополнительно связывает размер переменной. Метка объявляется очень просто: достаточно в начале строки написать имя и поставить двоеточие. Например:

Теперь вместо имени m1 компилятор везде будет подставлять адрес комады mov ax,4C00h. Можно объявлять метку на пустой строке перед командой:

Имя метки может состоять из латинских букв, цифр и символов подчёркивания, но должно начинаться с буквы. Имя метки должно быть уникальным. В качестве имени метки нельзя использовать директивы и ключевые слова компилятора, названия команд и регистров (в этом случае FASM покажет сообщение об ошибке). FASM различает регистр символов в именах меток. Можно также объявлять несколько меток на один адрес. Например:

Подробнее о синтаксисе объявления меток рассказывается в части 25.

Команда LOOP

Для организации цикла предназначена команда LOOP. У этой команды один операнд — имя метки, на которую осуществляется переход. В качестве счётчика цикла используется регистр CX. Команда LOOP выполняет декремент CX, а затем проверяет его значение. Если содержимое CX не равно нулю, то осуществляется переход на метку, иначе управление переходит к следующей после LOOP команде.

Содержимое CX интерпретируется командой как число без знака. В CX нужно помещать число, равное требуемому количеству повторений цикла. Понятно, что максимально может быть 65535 повторений. Ещё одно ограничение связано с дальность перехода. Метка должна находиться в диапазоне -127…+128 байт от команды LOOP (если это не так, FASM сообщит об ошибке).

Пример цикла

В качестве примера я приведу простую программу, которая будет печатать все буквы английского алфавита. ASCII-коды этих символов расположены последовательно, поэтому можно выводить их в цикле. Для вывода символа на экран используется функция DOS 02h (выводимый байт должен находиться в регистре DL).

Команды «int 21h» и «inc dl» (строки 8 и 9) будут выполняться в цикле 26 раз. Для того, чтобы программа не закрылась сразу, используется функция DOS 08h — ввод символа с клавиатуры без эха, то есть вводимый символ не отображается. Перед этим выводится предложение нажать любую кнопку (но Reset лучше не нажимать). Для примера адрес строки объявлен с помощью метки. Символы с кодами 13 и 10 обозначают переход на следующую строку (символ 13(0Dh) называется CR — Carriage Return — возврат каретки, а символ 10(0Ah) LF — Line Feed — перевод строки . Эти символы унаследованы со времён древних телетайпов, когда текст печатался, как на печатной машинке). Так выглядит результат работы программы:

Вложенные циклы

Иногда требуется организовать вложенный цикл, то есть цикл внутри другого цикла. В этом случае необходимо сохранить значение CX перед началом вложенного цикла и восстановить после его завершения (перед командой LOOP внешнего цикла). Сохранить значение можно в другой регистр, во временную переменную или в стек. Следующая программа выводит все доступные ASCII-символы в виде таблицы 16×16. Значение счётчика внешнего цикла сохраняется в регистре BX.

Как видите, всё довольно просто. Результат работы программы выглядит вот так:

Упражнение

Напишите программу для вычисления степени числа 3 по формуле

Число a — 16-битное целое без знака, число n — 8-битное целое без знака (используйте n<11, чтобы избежать переполнения). Проверьте работу программы в отладчике (нажимайте F7 на команде LOOP, чтобы осуществить переход). Результаты можете выкладывать в комментариях.

7.6.3. Команды цикла

Для организации циклов на Ассемблере вполне можно использовать команды условного перехода. Например, цикл языка Паскаль с предусловием while X<0 do S; можно реализовать в виде следующего фрагмента на Ассемблере

L: cmp X,0; Сравнить X с нулём

; Здесь будет оператор S

Оператор цикла с постусловием repeat S1; S2;. . .Sk until X<0; можно реализовать в виде фрагмента на Ассемблере

cmp X,0; Сравнить X с нулём

В этих примерах мы считаем, что тело цикла по длине не превышает примерно 120 байт (это 30-40 машинных команд). Как видим, цикл с постусловием требует для своей реализации на одну команду меньше, чем цикл с предусловием.

Читайте так же:
Сколько цифр записывать с счетчика

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

loop L; Метка L заменится на операнд i8

использует неявный операнд – регистр CX и её выполнение может быть так описано с использованием Паскаля:

if CX<>0 then goto L;

Как видим, регистр CX (который так и называется регистром счётчиком цикла – loop counter), используется этой командой именно как параметр цикла. Лучше всего эта команда цикла подходит для реализации цикла с параметром языка Паскаль вида

for CX:=N downto 1 do S;

Этот оператор можно эффективно реализовать таким фрагментом на Ассемблере:

L: . . .; Тело цикла –

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

Описанная выше команда цикла выполняет тело цикла ровно N раз, где N – беззнаковое число, занесённое в регистр-счётчик цикла CX перед началом цикла. К сожалению, никакой другой регистр нельзя использовать для этой цели (т.к. это неявный параметр команды цикла). Кроме того, в приведённом выше примере реализации цикла тело этого не может быть слишком большим, иначе команда loop L не сможет передать управление на метку L.

В качестве примера использования команды цикла решим следующую задачу. Требуется ввести беззнаковое число N<=500, затем ввести N знаковых целых чисел и вывести сумму тех из них, которые принадлежат диапазону –2000..5000. Можно предложить следующее решение этой задачи.

include io.asm

; файл с макроопределениями для макрокоманд ввода-вывода

data segment

S dw 0; Начальное значение суммы = 0

T1 db ′Введите N<=500 $′

T2 db ′Ошибка – большое N!$′

T3 db ′Вводите целые числа′,10,13,′$′

T4 db ′Ошибка – большая сумма!$′

stack segment stack

dw 64 dup (?)

stack ends

code segment

assume cs:code,ds:data,ss:stack

start:mov ax,data

mov dx, offset T1; Приглашение к вводу

mov dx, offset T2; Диагностика от ошибке

Err: outstr

L1: mov cx,N; Счётчик цикла

jcxz Pech; На печать результата

mov dx,offset T3; Приглашение к вводу

L2: inint ax; Ввод очередного числа

cmp ax,-2000

cmp ax,5000

jg L3; Проверка диапазона

add S,ax; Суммирование

jno L3; Проверка на переполнение S

mov dx,offset T4

L3: loop L2

Pech: outch ′S′

В качестве ещё одного примера рассмотрим использование циклов при обработке массивов. Пусть необходимо составить программу для решения следующей задачи. Задана константа N=20000, надо ввести массивы X и Y по N беззнаковых чисел в каждом массиве и вычислить выражение

Для простоты будем предполагать, что каждое из произведений и вся сумма имеют формат dw (помещаются в слово). Ниже приведена программа, решающая эту задачу.

include io.asm

N equ 20000; Аналог Const N=20000; Паскаля

data1 segment

T1 db ′Вводите числа массива $′

T2 db ′Сумма = $′

T3 db ′Ошибка – большое значение!′,10,13,′$′

S dw 0; искомая сумма

X dw N dup (?); 2*N байт

data1 ends

data2 segment

Y dw N dup (?); 2*N байт

data2 ends

st segment stack

dw 64 dup(?)

code segment

assume cs:code,ds:data1,es:date2,ss:st

mov ax,data1

mov ds,ax; ds – на начало data1

mov ax,data2

mov es,ax; es – на начало data2

mov dx, offset T1; Приглашение к вводу

mov cx,N; счётчик цикла

mov bx,0; индекс массива

L1: inint X[bx];ввод очередного элемента X[i]

add bx,2; увеличение индекса, это i:=i+1

outstr; Приглашение к вводу

mov cx,N; счётчик цикла

mov bx,0; индекс массива

L2: inint ax

mov Y[bx],ax; ввод очередного элемента es:Y[bx]

add bx,2; увеличение индекса

mov bx,offset X; указатель на X[1]

mov si,offset Y+2*N-2; указатель на Y[N]

L3: mov ax,[bx]; первый сомножитель

mul word ptr es:[si]; умножение на Y[N-i+1]

jc Err; большое произведение

jc Err; большая сумма

add bx,type X; это bx:=bx+2

sub si,2; это i:=i-1

loop L3; цикл суммирования

mov dx, offset T2

Err: mov dx,T3

end begin_of_program

Подробно прокомментируем эту программа. Количество элементов массивов мы задали, используя директиву эквивалентности N equ 20000 , это есть указание программе Ассемблера о том, что всюду в программе, где встретится имя N, надо подставить вместо него операнд этой директивы – число 20000. Таким образом, это почти полный аналог описания константы в языке Паскаль. 1 Под каждый из массивов директива dw зарезервирует 2*N байт памяти.

Читайте так же:
Методика поверки квартирных счетчиков

Заметим теперь, что оба массива не поместятся в один сегмент данных (в сегменте не более примерно 32000 слов, а у нас в сумме 40000 слов), поэтому массив X мы размещаем в сегменте data1, а массив Y – в сегменте data2. Директива assume говорит, что на начала этих сегментов будут соответственно указывать регистры ds и es, что мы и обеспечили в самом начале программы. При вводе массивов мы использовали индексный регистр bx, в котором находится смещение текущего элемента массива от начала этого массива.

При вводе массива Y мы для учебных целей вместо предложения

L2: inint Y[bx];ввод очередного элемента

записали два предложения

L2: inint ax

mov Y[bx],ax;ввод очередного элемента

Это мы сделали, чтобы подчеркнуть: при доступе к элементам массива Y Ассемблер учитывает то, что имя Y описано в сегменте data2 и автоматически (используя информацию из директивы assume) поставит перед командой mov Y[bx],ax специальную однобайтную команду es: . Эту команду называют префиксом программного сегмента, так что на языке машины у нас будут две последовательные, тесно связанные команды:

es: mov Y[bx],ax

В цикле суммирования произведений для доступа к элементам массивов мы использовали другой приём, чем при вводе – регистры-указатели bx и si, в этих регистрах находятся адреса очередных элементов массивов. Напомним, что адрес – это смещение элемента относительно начала сегмента (в отличие от индекса элемента – это смещение от начала массива).

При записи команды умножение

mul word ptres:[si];умножениенаY[N-i+1]

мы вынуждены явно задать размер второго сомножителя и записать префикс программного сегмента es:, так как по виду операнда [si] Ассемблер не может сам «догадаться», что это элемент массива Y размером в слово и из сегмента data2.

add bx,type X; это bx:=bx+2

для задания размера элемента массива мы использовали оператор type. Параметром этого оператора является имя из нашей программы, значением оператора type <имя> является целое число – тип данного имени. Для имён областей памяти это длина этой области в байтах (для массива это почти всегда длина одного элемента), для меток команд это отрицательное число –1, если метка расположена в том же сегменте, что и оператор type, или отрицательное число –2 для меток из других сегментов. Все остальные имена имеют тип ноль.

Вы, наверное, уже почувствовали, что программирование на Ассемблере сильно отличается от программирования на языке высокого уровня (например, на Паскале). Чтобы подчеркнуть это различие, рассмотрим пример задачи, связанной с обработкой матрицы, и решим её на Паскале и на Ассемблере.

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

Const N=20; M=30;

Var X: array[1..N,1..M] of integer;

for i:=1 to N do

if X[i,1]<0 then

for j:=1 to M do Sum:=Sum+X[i,j];

Сначала обратим внимание на то, что переменные i и j несут в программе на Паскале двойную нагрузку: это одновременно и счётчики циклов, и индексы элементов массива. Такое совмещение функций упрощает понимание программы и делает её очень компактной по внешнему виду, но не проходит даром: чтобы по индексам элемента массива вычислить его адрес в сегменте, приходится выполнить достаточно сложные действия. Например, адрес элемента X[i,j] приходится вычислять так:

Эту формулу легко понять, учитывая, что матрица хранится в памяти по строкам (сначала первая строка, затем вторая и т.д.), и каждая строка имеет длину 2*M байт. Буквальное вычисление адресом элементов по приведённой выше формуле (а именно так чаще всего и делает Паскаль-машина) приводит к весьма неэффективной программе. При программировании на Ассемблере лучше всего разделить функции счётчика цикла и индекса элементов. В качестве счётчика лучше всего использовать регистр cx (он и специализирован для этой цели), а адреса лучше хранить в индексных регистрах (bx, si и di). Исходя из этих соображений, можно так переписать программу на Паскале, предвидя её будущий перенос на Ассемблер.

Const N=20; M=30;

Var X: array[1..N,1..M] of integer;

Sum,cx,oldcx: integer; bx: ↑integer;

for cx:=N downto 1 do

if bx↑<0 then begin oldcx:=cx;

for cx:=M downto 1 do begin

else bx:=bx+2*M

Теперь осталось переписать этот фрагмент программы на Ассемблере:

oldcx equ di

Data segment

X dw N*M dup (?)

mov bx,offset X; Адрес X[1,1]

L1: cmp word ptr [bx],0

mov oldcx,cx

L2: mov ax,[bx]

mov cx,oldcx

L3: add bx,2*M

L4: loop L1

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

Читайте так же:
Счетчик моточасов для экскаватора

Вернёмся к описанию команд цикла. В языке Ассемблера есть и другие команды цикла, которые могут производить досрочный (до исчерпания счётчика цикла) выход из цикла. Как и для команд условного перехода, для мнемонических имен некоторых из них существуют синонимы, которые мы будем разделять в описании этих команд символом /.

loopz/loope L

выполняется по схеме

Dec(CX); if (CX<>0) and (ZF=1) then goto L;

loopnz/loopne L

выполняется по схеме

Dec(CX); if (CX<>0) and (ZF=0) then goto L;

В этих командах необходимо учитывать, что операция Dec(CX) является частью команды цикла и не меняет флага ZF.

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

Тут вы можете оставить комментарий к выбранному абзацу или сообщить об ошибке.

Две петли, вложенный в другой цикл, в x86 assembly

У меня есть проблема с циклом на языке assembly. Когда мы хотим использовать регистр счетчика для циклирования во вложенном цикле, то сначала мы перемещаем значение регистра счетчика в стек для внешнего цикла, а затем возвращаем его обратно, когда мы закончим с внутренним циклом, таким образом, мы можем использовать один регистр счетчика для циклирования во вложенном цикле с разным количеством итераций в каждом цикле.

Но как насчет вложенного цикла внутри вложенного цикла?

Я хочу напечатать пирамиду, сделанную из символа S . То, что я получаю, это,

Чего я на самом деле хочу, так это,

Вот мой код для этой программы

Чтобы достичь того, чего я хочу, мне нужно добавить еще один цикл внутри вложенного цикла, который печатает символы пробела (например, 020H). Но для этого мне нужен еще один счетчик регистров, и я не в состоянии это сделать. Как я могу решить эту проблему?

2 ответа

  • Как увеличить массив в x86 assembly?

Как бы вы увеличили массив, используя x86 assembly внутри for loop. Если цикл (сделанный с использованием c++) выглядел так: for (int i = 0; i < limit; i++) Значение из массива помещается в регистр, а затем измененное значение помещается в отдельный массив. Как бы я увеличил каждый массив в x86.

Я в некотором роде новичок в программировании в Assembly, и мне нужно некоторое разъяснение относительно следующего вида циклов (@@, @B, @F). Когда у тебя есть такая рутина: Routine: PROC Value: Byte MOV ECX, 3 MOVZX EDX, Value MOV EAX, EDX @@: SHL EAX, 8 OR EAX, EDX LOOP @B RET Routine: ENDP .

Вы уже делаете то, что должно быть сделано в данном ASM. Вы можете поместить текущее значение CX в стек (сохранить его) и вставить его позже, чтобы восстановить его. Вам нужно будет сделать это, когда вам потребуется дополнительная вложенность.

В коде JohnB он просто добавил в цикл, чтобы распечатать пробелы перед печатью звездочек. Никаких дополнительных вложений не требуется, что означает, что это довольно прямолинейно.

Это немного похоже на это:

Именно это вам и показал JohnB.

Кстати, с какой целью вы инициализируете и увеличиваете AX? Вы все равно перезаписываете его при перемещении данных в AH/AL.

Похожие вопросы:

Можно ли вызывать объекты COM через язык x86 assembly? Если да, то как? Зачем мне это делать? Допустим, у меня есть две программы, для которых у меня нет исходного кода — все, что у меня есть, это.

Короче говоря, я изучаю assembly и пытаюсь сделать цикл распечатки ascii символов 0-9. Итак, я сделал все основы, которые видел в примерах, например, сохранил состояния регистров с помощью pushad и.

В настоящее время я изучаю Intel x86 Assembly, и я столкнулся с проблемой, пытаясь построить простой цикл, который повторяется 10 раз. Он должен остановиться после 10 петель, но он продолжает идти и.

Как бы вы увеличили массив, используя x86 assembly внутри for loop. Если цикл (сделанный с использованием c++) выглядел так: for (int i = 0; i < limit; i++) Значение из массива помещается в.

Я в некотором роде новичок в программировании в Assembly, и мне нужно некоторое разъяснение относительно следующего вида циклов (@@, @B, @F). Когда у тебя есть такая рутина: Routine: PROC Value.

Я создаю языковую программу x86 assembly, и я сохранил свое имя в разделе данных кода, и я хотел бы создать цикл для вывода каждого символа по одному за раз. Я теряюсь в догадках, что для этого.

Читайте так же:
Счетчик установлен у двери

Я хочу сравнить две строки в x86 assembly. Один из них считывается из файла, а другой-с клавиатуры, и оба они сохраняются в переменной. Я понятия не имею, как сравнить эти две переменные. Я буду так.

Я должен написать следующий цикл C в mips assembly: for (i=0;i<5;++i) < for (k=0;k<4;++k) < printf (*); >printf (n); > Вот мой код: .data i: .word 0 k: .word 0 limit: .word 4 line: .word 5.

function[output]=tridiag(d,l,r) A=zeros(3); for i=1:5 A(i,i)=d(i); for j=2:5 A(j,(i-1))=l(j); for k=1:4 A(k,(i+5))=r(k); end end end A end в этой части где говорится, for j=2:5 A(j,(i-1))=l(j); Я.

Loop ассемблер что это

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

Инструкция LOOP в Ассемблере уменьшает значение в регистре СХ в реальном режиме или ECX в защищённом. Если после этого значение в СХ не равно нулю, то команда LOOP выполняет переход на МЕТКУ. Синтаксис:

Состояние флагов не изменяется.

МЕТКА – это допустимый в Ассемблере идентификатор. О метках в Ассемблере я рассказывал здесь.

Алгоритм работы команды LOOP:

  • CX = CX – 1
  • Если CX не равен 0, то выполнить переход
  • Иначе не выполнять переход, продолжить цикл

То есть команда LOOP выполняется в два этапа. Сначала из регистра СХ вычитается единица и его значение сравнивается с нулём. Если регистр не равен нулю, то выполняется переход к указанной МЕТКЕ. Иначе переход не выполняется и управление передаётся команде, которая следует сразу после команды LOOP.

Как выполнить цикл в Ассемблере

Выполнение цикла в Ассемблере можно организовать с помощью нескольких команд. Одна из таких команд – это команда LOOP. Команда цикла в Ассемблере всегда уменьшает значение счётчика на единицу. Это значение находится в регистре СХ (или ECX). Отличия между командами цикла заключаются только в условиях, при которых выполняется переход к метке или цикл завершается.

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

Более понятно это будет в примере программы (см. ниже).

Возможные ошибки

Начинающие довольно часто совершают одни и те же ошибки при организации циклов в Ассемблере. А именно – неправильно задают или обнуляют значение счётчика перед выполнение цикла.

При обнулении счётчика перед циклом при первой итерации цикла значение в регистре CX будет равно FFFFh (потому что команда LOOP отнимет от СХ единицу, а в СХ у нас был 0), и цикл в программе будет выполняться, соответственно 65536 раз.

Ещё один момент: диапазон адресов для передачи управления в команде LOOP ограничен в пределах -128…+127 байтов относительно адреса следующей команды. Если учесть, что в реальном режиме процессора средняя длина машинной команды равна 3 байта, то получается, что блок команд, выполняющихся в цикле, может состоять примерно из 42 команд. Если же в вашем цикле будет больше команд, то, например, MASM, выдаст сообщение об ошибке, которое будет выглядеть примерно так:

error A2075: jump destination too far : by 10 byte(s)

Здесь говорится, что местоположение перехода слишком далеко (примерно на 10 байт больше допустимого).

Ещё одна ошибка – это изменение значения регистра CX в теле цикла. В итоге команда LOOP будет работать неправильно. К тому же при этом можно попасть в бесконечный цикл. Пример:

Здесь в теле цикла увеличивается значение регистра СХ, поэтому он никогда не будет равен нулю, и, следовательно, цикл никогда не завершится.

А теперь о происхождении мнемоники LOOP. В общем то это не мнемоника, а слово. В переводе с английского оно означает “петля”, “виток”, “цикл”.

К изучению языка Ассемблер учащиеся подходят, как правило, имея начальные знания в области программирования. Поэтому им проще будет понять, как реализуются основные алгоритмические структуры в Ассемблере, если при изложении нового материала преподаватель будет проводить аналогию с изученным ими ранее языком программирования (например, Turbo Pascal).

Алгоритмическая структура “цикл”, как известно, обеспечивает выполнение некоторой последовательности действий, которая называется телом цикла.

Выделяется три типа циклов: цикл “ДЛЯ”, цикл “ПОКА”, цикл “ДО”. Друг от друга различные типы циклов отличаются в основном лишь способом проверки окончания цикла.

Читайте так же:
Счетчик нева 303 1 sо 220v

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

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

Как и в языке Паскаль, в Ассемблере существует специальная команда, которая позволяет сокращать листинг циклической программы.

Это команда LOOP .

Данная команда выполняет следующие функции:

  1. Автоматически уменьшает значение счетчика.
  2. Выполняет проверку на выход из цикла.
  3. Выполняет переход на начало тела цикла.

Команда LOOP может быть использована лишь в случае цикла с известным числом повторений, т.е. цикла “ДЛЯ”. Количество повторений цикла должно быть присвоено регистру СХ до начала цикла.

Таким образом, команда LOOP заменила тройку команд:

Рассмотрим использование этой команды на практике.

Пример: Составим программу, которая выводит на экран 1000 нулей.

(1) prg segment para public ‘code’
(2) assume cs:prg,ss:prg,es:prg,ds:prg
(3) org 100h
(4) start: jmp go
(5) go:
(6) mov ax, 0600h
(7) mov bh,07
(8) mov cx, 0000
(9) mov dx,184fh
(10) mov cx,1000
(11) Zero:
(12) mov ah,02
(13) mov dl,30h
(14) int 21h
(15) loop Zero
(16) ret
(17) prg ends
(18) end start

Строки с (1) по (10) и с (16) по (18) вы уже знаете.

Строка (11) – это метка (начало цикла). Строка (15) – конец цикла. Все, что находится в пределах строк (11) – (15), является циклом. Сам цикл будет повторяться 1000 раз, для чего мы и заносим в СХ число 1000 (строка (10)).

В строке (12) заносим в регистр ah число 02 (запрос функции вывода одного символа).

В строке (13) в регистр dl заносим код выводимого символа (код символа “0” – 30h).

В строке (14) вызываем прерывание int 21h.

Теперь на экране появится первый ноль. Остается уменьшить счетчик (СХ) на 1 и повторить. Что мы и делаем в строке (15).

Задача 1 для практики: Составить фрагмент программы на языке Ассемблер, подсчитывающий сумму первых 10 натуральных чисел (результат записать в АХ).

Задача 2 для практики: Составить фрагмент программы на языке Ассемблер, вычисляющий значение выражения: (результат записать в АХ).

Задача 3 для практики: Составить фрагмент программы на языке Ассемблер, вычисляющий факториал заданного числа К (К – от 0 до 8).

Команда JMP в примере 7.1 реализует бесконечный цикл. Но более вероятно, что подпрограмма должна выполнять конечное число циклов. Команда LOOP, которая служит для этой цели, использует начальное значение в регистре CX. В каждом цикле команда LOOP автоматически уменьшает содержимое регистра CX на 1. Пока значение в CX не равно нулю, управление передается по адресу, указанному в операнде, и если в CX будет 0, управление переходит на следующую после LOOP команду.

Пример 7.2. Использование команды LOOP.

mov ax,01 ; инициализация ax,

mov cx,10 ; число циклов

add bx,ax ; bx = bx + ax

shl dx,1 ; удвоить dx

loop a20 ; уменьшить cx и повторить

; цикл, если не нуль

mov ax,4c00h ; завершить программу

Программа в примере 7.2, иллюстрирующая использование команды LOOP, выполняет действия, похожие на пример 7.1 за исключением того, что после десяти циклов программа завершается. Команда MOV инициализирует регистр CX значением 10. Так как команда LOOP использует регистр CX, то в программе для удвоения начального значения 1 вместо регистра CX используется DX. Команда JMP A20 заменена командой LOOP и для эффективности команда ADD AX,01 заменена командой INC AX (увеличение AX на 1).

Аналогично команде JMP, операнд команды LOOP определяет расстояние от конца команды LOOP до адреса метки A20. Для команды LOOP это расстояние должно быть в пределах от -128 до +127 байт. Если операнд превышает эти границы, то ассемблер выдаст сообщение "Relative jump out of range" (превышены границы перехода).

Для проверки можно сгенерировать исполняемые модули для примеров 7.1 и 7.2 и запустить их в отладочном режиме в программе Turbo Debugger.

Дополнительно существует две разновидности команды LOOP – это LOOPE (или LOOPZ) и LOOPNE (или LOOPNZ). Обе команды также уменьшают значение регистра CX на 1. Команда LOOPE передает управление по адресу операнда, если регистр CX имеет ненулевое значение и флаг нуля установлен (ZF=1). Команда LOOPNE передает управление по адресу операнда, если регистр CX имеет ненулевое значение и флаг нуля сброшен (ZF=0).

Не нашли то, что искали? Воспользуйтесь поиском:

Лучшие изречения: Учись учиться, не учась! 10432 – | 7912 – или читать все.

голоса
Рейтинг статьи
Ссылка на основную публикацию
Adblock
detector