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

Учебный курс. Часть 13. Циклы и команда LOOP

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

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

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

Но с ними компилятор дополнительно связывает размер переменной. Метка объявляется очень просто: достаточно в начале строки написать имя и поставить двоеточие. Например:

m1: mov ax,4C00h int 21h

m1: mov ax,4C00h int 21h

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

Assembler #3 / Ассемблер / ЛамПанель / Условные переходы / Циклы / Факториал


exit_app: mov ax,4C00h int 21h

exit_app: mov ax,4C00h int 21h

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

no_error: exit_app: m1: mov ax,4C00h

no_error: exit_app: m1: mov ax,4C00h

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

Команда LOOP

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

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

Пример цикла

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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
use16 ;Генерировать 16-битный код org 100h ;Программа начинается с адреса 100h mov ah,02h ;Для вызова функции DOS 02h — вывод символа mov dl,’A’ ;Первый выводимый символ mov cx,26 ;Счётчик повторений цикла metka: int 21h ;Обращение к функции DOS inc dl ;Следующий символ loop metka ;Команда цикла mov ah,09h ;Функция DOS 09h — вывод строки mov dx,press ;В DX адрес строки int 21h ;Обращение к функции DOS mov ah,08h ;Функция DOS 08h — ввод символа без эха int 21h ;Обращение к функции DOS mov ax,4C00h ; int 21h ;/ Завершение программы ;——————————————————- press: db 13,10,’Press any key. $’

use16 ;Генерировать 16-битный код org 100h ;Программа начинается с адреса 100h mov ah,02h ;Для вызова функции DOS 02h — вывод символа mov dl,’A’ ;Первый выводимый символ mov cx,26 ;Счётчик повторений цикла metka: int 21h ;Обращение к функции DOS inc dl ;Следующий символ loop metka ;Команда цикла mov ah,09h ;Функция DOS 09h — вывод строки mov dx,press ;В DX адрес строки int 21h ;Обращение к функции DOS mov ah,08h ;Функция DOS 08h — ввод символа без эха int 21h ;Обращение к функции DOS mov ax,4C00h ; int 21h ;/ Завершение программы ;——————————————————- press: db 13,10,’Press any key. $’

Assembler — операторы цикла

Команды «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.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
use16 ;Генерировать 16-битный код org 100h ;Программа начинается с адреса 100h mov ah,02h ;Для вызова функции DOS 02h — вывод символа sub dl,dl ;Первый выводимый символ mov cx,16 ;Счётчик внешнего цикла (по строкам) lp1: mov bx,cx ;Сохраняем счётчик в BX mov cx,16 ;Счётчик внутреннего цикла (по столбцам) lp2: int 21h ;Обращение к функции DOS inc dl ;Следующий символ loop lp2 ;Команда внутреннего цикла mov dh,dl ;Сохраняем значение DL в DH mov dl,13 ; int 21h ; mov dl,10 ; / Переход на следующую строку int 21h ;/ mov dl,dh ;Восстанавливаем значение DL mov cx,bx ;Восстанавливаем значение счётчика loop lp1 ;Команда внешнего цикла mov ah,09h ;Функция DOS 09h — вывод строки mov dx,press ;В DX адрес строки int 21h ;Обращение к функции DOS mov ah,08h ;Функция DOS 08h — ввод символа без эха int 21h ;Обращение к функции DOS mov ax,4C00h ; int 21h ;/ Завершение программы ;——————————————————- press db 13,10,’Press any key. $’

use16 ;Генерировать 16-битный код org 100h ;Программа начинается с адреса 100h mov ah,02h ;Для вызова функции DOS 02h — вывод символа sub dl,dl ;Первый выводимый символ mov cx,16 ;Счётчик внешнего цикла (по строкам) lp1: mov bx,cx ;Сохраняем счётчик в BX mov cx,16 ;Счётчик внутреннего цикла (по столбцам) lp2: int 21h ;Обращение к функции DOS inc dl ;Следующий символ loop lp2 ;Команда внутреннего цикла mov dh,dl ;Сохраняем значение DL в DH mov dl,13 ; int 21h ; mov dl,10 ; / Переход на следующую строку int 21h ;/ mov dl,dh ;Восстанавливаем значение DL mov cx,bx ;Восстанавливаем значение счётчика loop lp1 ;Команда внешнего цикла mov ah,09h ;Функция DOS 09h — вывод строки mov dx,press ;В DX адрес строки int 21h ;Обращение к функции DOS mov ah,08h ;Функция DOS 08h — ввод символа без эха int 21h ;Обращение к функции DOS mov ax,4C00h ; int 21h ;/ Завершение программы ;——————————————————- press db 13,10,’Press any key. $’

Читайте также:
Кпк комплимент программа комплимент примеры

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

Упражнение

Источник: fasmworld.ru

Инструкция LOOP

В Контакте Ютуб Почта

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

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

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

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

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

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

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

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

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

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

ПРИМЕЧАНИЕ
В качестве счётчика команда LOOP использует регистр CX в реальном режиме, и регистр ECX в защищённом режиме. Это не всегда удобно, если программу (или её часть) планируется использовать в обоих режимах. Поэтому в системе команд процессоров Интел предусмотрены две специальные команды — LOOPD и LOOPW, которые независимо от режима работы процессора в качестве счётчика используют регистры ECX и CX соответственно.

.model tiny .code ORG 100h start: MOV CX, 26 ; Цикл будет выполнен 26 раз MOV DL, ‘A’ ; CL = 41h (ASCII-код) — первая буква MOV BX, 676h ; Позиция первой буквы на экране MOV AX, 0B800h ; Установить AX = B800h (память VGA) MOV DS, AX ; Копировать значение из AX в DS MOV DH, 01001110b ; CH = атрибуты цвета abcde: MOV [BX], DX ; Записать символ в видеопамять INC DL ; Увеличить ASCII-код (для следующего символа) ADD BX, 2 ; Сместить координаты LOOP abcde ; Повторить цикл END start

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

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

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

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

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

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

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

MOV CX, 2 ; Устанавливаем счётчик top: INC CX LOOP top

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

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

Источник: av-assembler.ru

Циклы и условные переходы

Циклы, позволяющие выполнить некоторый участок программы многократно, в любом языке являются одной из наиболее употребительных конструкций. В системе команд МП 86 циклы реализуются, главным образом, с помощью команды loop (петля), хотя имеются и другие способы организации циклов. Во всех случаях число шагов в цикле определяется содержимым регистра СХ, поэтому максимальное число шагов составляет 64 К.
Рассмотрим простой пример организации цикла. Пусть в программе зарезервировано место для массива размером 10000 слов, и этот массив надо заполнить натуральным рядом чисел от 0 до 9999. Эти числа, заполняющие последовательные элементы массива, иногда называют числами-заполнителями. Соответствующий фрагмент программы будет выглядеть следующим образом:

;В сегменте данных

array dw 10000 dup(0)

;В программном сегменте mov BX,offset array ; Адрес массива mov SI,0 ;Индекс mov AX,0 ; Начальное значение заполнителя mov CX,10000 ; Счетчик цикла fill: mov [BX] [SI],AX ;Заполнитель пошлем в массив inc AX ;Инкремент заполнителя add SI,2 ; модификация индекса loop fill ; Команда цикла

На этапе подготовки мы заносим в регистр ВХ относительный адрес начала массива, отождествляемый с его именем array, устанавливаем начальное значение индекса элемента массива в регистре SI (с таким же успехом можно бьшо взять DI) и начальное значение числа-заполнителя. Сам цикл состоит из трех команд — единственной содержательной команды засылки числа-заполнителя в очередной элемент массива (по адресу, который вычисляется, как сумма содержимого регистров ВХ и SI), а также модификации числа-заполнителя и индекса очередного элемента массива. Завершающей командой loop управление передается на метку fill, и цикл повторяется столько раз, каково содержимое СХ, в данном случае 10000 шагов.
Следует обратить внимание на команду модификации индекса — в каждом шаге к содержимому SI добавляется 2, так как массив состоит из двухбайтовых слов. Если бы нужно было заполнить байтовый массив, то в каждом шаге содержимое регистра цикла SI следовало увеличивать на 1.
Стоит отметить некоторые детали, связанные с механизмом выполнения команды loop. При реализации этой команды процессор сначала уменьшает содержимое регистра СХ на 1, а затем сравнивает полученное число с нулем. Если СХ > 0, переход на указанную метку выполняется. Если СХ = 0, цикл разрывается и процессор переходит на команду, следующую за командой loop. Поэтому после нормального выхода из цикла содержимое СХ всегда равно 0.
Другое обстоятельство связано с кодированием команды loop. В ее коде под смещение к точке перехода отводится всего 1 байт. Поскольку смещение должно являться величиной со знаком, максимальное расстояние, на которое можно передать управление командой loop, составляет от -128 до +127 байт (хотя довольно трудно представить себе цикл, в котором переход осуществляется вперед). Другими словами, тело цикла ограничивается всего 128 байтами. Если циклически повторяемый фрагмент программы имеет большую длину, цикл придется организовать другим, более сложным способом:

;Организация длинного цикла

mov CX,10000 ;Счетчик цикла

fill: ; Метка начала цикла

. ; Тело длинного цикла

dec CX ; Декремент счетчика цикла

cmp CX,0 ; Отработано заданное число шагов?

je finish ; Да, на метку продолжения программы

jmp fill ; Нет, на начало цикла

finish: ; Продолжение программы

В этом, весьма типичном фрагменте мы «вручную» уменьшаем содержимое счетчика цикла и сравниваем полученное значение с 0. Если СХ = О, это значит, что в цикле выполнено заданное число шагов, и командой условного перехода je осуществляется переход на продолжение программы (метка finish). Если СХ еще не равно нулю, командой безусловного перехода jmp осуществляется возврат в начало цикла. Как было показано в гл. 2, команда jmp позволяет перейти в любую точку сегмента, и ограничение на размер тела цикла снимается.
При необходимости организовать вложенные циклы, для сохранения счетчика внешнего цикла на время выполнения внутреннего удобно воспользоваться стеком. В следующем фрагменте организуется временная задержка длительностью несколько секунд (конкретная величина задержки зависит от скорости работы процессора).

mov CX,2000 ;Счетчик внешнего цикла

outer: push CX ; Сохраним его в стеке

mov CX,0 ;Счетчик внутреннего цикла

inner: loop inner ; loop внутреннего цикла

pop CX ;Восстановим внешний счетчик

loop outher ; loop внешнего цикла

Программные задержки удобно использовать при отладке программ, чтобы замедлить их работу и успеть рассмотреть их частичные результаты; иногда программные задержки позволяют синхронизовать работу аппаратуры, подключенной к компьютеру, если скорость отработки аппаратурой посылаемых в нее из компьютера команд меньше скорости процессора.
В приведенном выше фрагменте внешний цикл выполняется 2000 раз; внутренний — 65536 раз. При счете числа шагов внутреннего цикла используется явление оборачивания, которое уже упоминалось ранее. Начальное значение в регистре СХ равно нулю; после выполнения тела цикла 1 раз команда loop уменьшает содержимое СХ на 1, что дает число FFFFh (которое можно рассматривать, как -1). В результате цикл повторяется еще 65535 раз, а в сумме — точно 64 К шагов.
Команда loop внутреннего цикла передает управление на саму себя, т.е. тело внутреннего цикла состоит из единственной команды loop. В этом нет ничего незаконного. Любая команда, в том числе и loop, требует какого-то времени для своего выполнения, и повторение 64 К раз команды loop дает некоторую временную задержку (на современных процессорах порядка тысячной доли секунды).
Перейдем теперь к рассмотрению команд условных переходов.
В приведенном выше фрагменте для реализации длинного цикла использовалась команда условного перехода по равенству je. В системе команд МП 86 имеется свыше трех десятков команд условных переходов, позволяющих осуществлять переходы при наличии разнообразных условий: равенства, неравенства, положительности или отрицательности результата и проч. При выполнении всех этих команд процессор анализирует содержимое регистра флагов и осуществляет (или не осуществляет) переход на указанную метку в зависимости от состояния отдельных флагов или их комбинаций. Поскольку на состояние регистра флагов влияют многие команды процессора, командами условных переходов можно пользоваться не только после команд сравнения или анализа, но и после многих других команд, если внимательно изучить влияние этих команд на флаги процессора. Приведем несколько абстрактных примеров.

cmp AX,BX ;Сравнение двух регистров

je equal ;Переход, если AX=BX

cmp SI,mem ;Сравнение регистра и ячейки памяти

jne notequ ;Переход, если SI<>mem

int 21h ;Вызов DOS

jc syserr ;Переход, если была ошибка

;и флаг CF=1

or BX,BX ;Анализ BX

jz zero ;Переход, если BX=0

inpt: in AL,DX ;Ввод данного из устройства

test AL,80h ;Анализ бита 7 в данном

je inpt ;Ввод до тех пор , пока

;бит 7=0 (ожидание установки бита 7)

test AX,7 ;Анализ битов 0,1,2 в AX

jne found ;Переход, если хотя бы 1 бит

;из них установлен

test DI,OFh ;Анализ битов 0. 3 в DI

jz reset ;Переход, если все они сброшены

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

Читайте также:
Как сделать работу в программе office

Если, однако, то же число FFFFh получилось в арифметической операции вычитания 2 из 1, то его надо рассматривать, как — 1. Точно так же понятие знака бессмысленно по отношению к кодам символов, которые с равным успехом могут принимать любое значение из диапазона 0. 255. С другой стороны, мы можем условно считать, что коды символов первой половины таблицы ASCII положительны, а коды второй половины таблицы (у них установлен старший бит) отрицательны, и использовать для обработки символов команды, чувствительные к знаку.
В составе команд условных переходов имеются две группы команд для сравнения чисел без знака (это команды ja, jae, jb, jbc, jna, jnae, jnb и jnbe) и чисел со знаком (jg, jge, jl, jle, jng, jnge, jnl и jnle). В аббревиатурах этих команд для сравнения чисел без знака используются слова above (выше) и below (ниже), а для чисел со знаком — слова greater (больше) и less (меньше).
Разница между теми и другими командами условных переходов заключается в том, что команды для чисел со знаком рассматривают понятия «больше- меньше» применительно к числовой оси -32К. 0. +32К, а команды для чисел без знака — применительно к числовой оси 0. 64К. Поэтому для первых команд число 7FFFh (+32767) больше числа S000h (-32768), а для вторых число 7FFFh (32767) меньше числа S000h (32768). Аналогично, команды для чисел со знаком считают, что 0 больше, чем FFFFh (-1), а команды для чисел без знака — меньше.
Рассмотрим пример использования команд условных переходов для обработки символов. Пусть мы вводим с клавиатуры некоторую строку символов (например, имя файла), и хотим, чтобы в программе эта строка была записана прописными буквами, независимо от того, какие буквы использовались при ее вводе. Между прочим, при вводе с клавиатуры команд DOS система всегда выполняет эту операцию, поэтому и команды, и ключи, и имена файлов можно вводить как прописными, так и строчными буквами — DOS во всех случаях преобразует все буквы в прописные.

code segment

assume cs:code,ds:data

main proc

mov AX,data ;Инициализация

move DS,AX ;Регистр DS

;Выведем служебное сообщение

mov AH,09h ;Функция вывода

mov DX,offset msg ;Адрес сообщения

int 21h

;Поставим запрос к DOS на ввод строки

mov AH,3Fh ;Функция ввода

mov BX,0 ;Дескриптор клавиатуры

mov CX,80 ;Ввод максимум 80 байт

mov DX, offset buf ;Адрес буфера ввода

int 21h

mov actlen,AX ;Фактически введено

;Превратим строчные русские буквы в прописные

mov CX,actlen ;Длина введенной строки

mov SI,0 ;Указатель в буфере

filter: mov AL,buf[SI] ;Возьмем символ

cmp AL,’a’ ;Меньше ‘a’?

jb noletter ;Да, не преобразовывать

cmp AL,’я’ ;Больше ‘я’?

ja noletter ;Да, не преобразовывать

cmp AL,’п’ ;Больше ‘п’?

ja more ; Да, на дальнейшую проверку

sub AL,20h ;’a’..’п’. Преобразуем в прописную

jmp store ;На сохранение в буфере

more: cmp AL,’p’ ;Меньше ‘p1’ (псевдографика)?

jb noletter ;>’п’,

sub AL,50h ;’p’. ‘я’. Преобразуем в прописную

store: mov buf[SI],AL ;Отправим назад в buf

noletter: inc SI ;Сместим указатель

loop filter ;Цикл по всем символам

; Выведем результат преобразования на экран для контроля

mov AX,40h ;Функция вывода

mov BX,1 ;Дескриптор экрана

mov CX,actlen ;Длина сообщения

mov DX,offset buf ;Адрес сообщения

int 21h

mov AH,01 ;Остановим программу

int 21h ;в ожидании нажатия клавиши

;Завершим программу

mov AX,4C00h

int 21h

main endp

code ends

data segment

msg db «Вводите!$»

buf db 80 dup (‘ ‘) ;Буфер ввода

actlen dw 0

data ends

stk segment stack

dw 128 dup(‘)

stk ends

end main

В начале программы на экран выводится служебное сообщение «Вводите!», которое служит запросом программы, адресованным пользователю. Далее с помощью функции DOS 3Fh выполняется ввод строки текста с клавиатуры. Функция 3Fh может вводить данные из разных устройств — файлов, последовательного порта, клавиатуры. Различные устройства идентифицируются их дескрипторами. При работе с файлами дескриптор каждого файла создается системой в процессе операции открытия или создания этого файла, а для стандартных устройств — клавиатуры, экрана, принтера и последовательного порта действуют дескрипторы, закрепляемые за этими устройствами при загрузке системы. Для ввода с клавиатуры используется дескриптор 0, для вывода на экран дескриптор 1.
При вызове функции 3Fh в регистр ВХ следует занести требуемый дескриптор, в регистр DX — адрес области в программе, выделенной для приема вводимых с клавиатуры символов, а в регистр СХ — максимальное число вводимых символов. Мы считаем, что пользователь не будет вводить более 80 символов. Можно ввести и меньше; в любом случае ввод строки следует завершить нажатием клавиши . Функция 3Fh, отработав, вернет в регистре АХ реальное число введенных символов (включая коды 13 и 10, образуемые при нажатии клавиши ). В примере 3.5 число введенных символов сохраняется в ячейке actlen с целью использования далее по ходу программы.
Далее в цикле из actlen шагов выполняется анализ каждого введенного символа путем сравнения с границами диапазонов строчных русских букв. Русские строчные буквы размещаются в двух диапазонах кодов ASCII (а. п и р. с), причем для преобразования в прописные букв первого диапазона их код следует уменьшать на 20h, а для преобразования букв второго диапазона — на 50h. Поэтому анализ проводится с помощью четырех команд сравнения сmр и соответствующих команд условных переходов. Модифицированный символ записывается на то же место в буфере buf.
После завершения анализа и преобразования введенных символов, выполняется контрольный вывод содержимого buf на экран. Поскольку мы заранее не знаем, сколько символов будет введено, вывод на экран осуществляется функцией 40h, среди параметров которой указывается число выводимых символов. Так же, как и в случае функции ввода 3Fh, для функции вывода 40h в регистре ВХ необходимо указать дескриптор устройства ввода, в данном случае экрана, а в регистре DX — адрес выводимой строки.
Коды символов являются числами без знака, и использование в данном случае команд условных переходов для чисел без знака представляется логичным и даже единственно возможным. Если, однако, внимательно рассмотреть понятия больше- меньше для чисел со знаком и без знака, то легко увидеть, что пока мы сравниваем друг с другом только «положительные» или только «отрицательные» числа, команда ja эквивалентна команде jg, а команда jb эквивалентна команде jl. Однако при сравнении, например, кодов цифр с кодами русских букв, правильный результат можно получить лишь при использовании команд переходов для чисел без знака. Впрочем, всегда нагляднее и надежнее использовать те команды, которые соответствуют существу рассматриваемых данных, даже если такой же правильный результат получится и при использовании «неправильных» команд.
Более отчетливо разница между числами со знаком и без знака проявляется при использовании арифметических операций, например, операций умножения или деления. Здесь для чисел со знаком и чисел без знака предусмотрены отдельные команды:

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

imul — команда умножения чисел со знаком;

div — команда деления чисел без знака;

idiv — команда деления чисел со знаком.

Поясним различия этих команд на формальных примерах.

;Умножение положительных чисел со знаком

mov AL,5 ;Первый сомножитель равен 5

mov BL,7 ;Второй сомножитель равен 7

mul BL ;AX=0023h=35

mov AL,5 ;Первый сомножитель равен 5

mov BL,7 ;Второй сомножитель равен 7

imul BL ;AX=0023h=35

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

;Умножение отрицательных чисел со знаком

mov AL,OFCh ;Первый сомножитель=252

mov BL,4 ; Второй сомножитель =4

mul BL ;AX=03F0h =1008

mov AL,OFCh ;Первый сомножитель=-4

mov BL,4 ; Второй сомножитель =4

imul BL ;AX=FFFO=-16

Здесь действие команд mul и imul над одними и теми же операндами дает разные результаты. В первом примере число без знака FCh, которое интерпретируется, как 252, умножается на 4, давая в результате число без знака 3F0, т.е. 1008. Во втором примере то же число FCh рассматривается, как число со знаком. В этом случае оно составляет -4.

Читайте также:
Как отучить ребенка от сладкого проверенная безопасная и простая программа

Умножение на 4 дает FFF0h, т.е. -16.

Fore kc .ru
Рефераты, дипломы, курсовые, выпускные и квалификационные работы, диссертации, учебники, учебные пособия, лекции, методические пособия и рекомендации, программы и курсы обучения, публикации из профильных изданий

Источник: www.i-assembler.ru

Рейтинг
( Пока оценок нет )
Загрузка ...
EFT-Soft.ru