В.Г. Баула - Введение в архитектуру ЭВМ и системы программирования (1110549), страница 21
Текст из файла (страница 21)
Упражнение. Определите, какие значения должен иметь флаг направления DF при операции встаки и при операции удаления участка редактируемого текста.
Продолжим изучение строковых команд. Команды сравнения двух операндов cmpsb и cmpsw имеют тот же формат память-память, что и команды movsb и movsw. Команды cmpsb и cmpsw выполняются по схеме:
cmp <ds,si>,<es,di>; φ(di); φ(si)
т.е. производится сравнение между собой двух коротких или длинных целых чисел и соответствующим образом устанавливаются флаги (так же, как при выполнении команды сравнения).
Как мы знаем, команды сравнения необходимы для работы команд условного перехода. С командами сравнения cmpsb и cmpsw удобно использовать команды-префиксы повторения repe/repz и repne/repnz. Эти команды похожи на команду rep, но обеспечивают возможность досрочного выхода из цикла по значению флага ZF=0 (для команд repe/repz) и ZF=1 (для команд repne/repnz).
В качестве примера рассмотрим следующую задачу. Как мы знаем, строки текста можно сравнивать между собой не только на равенство и неравенство, но и с помощью других операций отношения (больше, меньше и т.д.). При этом строки считаются упорядоченными в так называемом лексикографическом порядке (а по-простому – как в словаре). С помощью строковых команд и префиксов повторения операцию сравнения двух строк можно так реализовать на Ассемблере (правда, здесь строки одинаковой длины, сравнение строк разной длины реализуется более сложно):
N equ 20000
Data segment
X db N dup (?)
Y db N dup (?)
. . .
Data ends
Code segment
assume cs:Code,ds:Data,es:Data,ss:Stack
. . .
mov cx,N
cld
lea si,X
lea di,Y
repe cmpsb
je EQ; Строки X и Y равны
jb LT; Строка X меньше Y
ja GT; Срока X больше Y
В нашем примере сравниваемые строки для простоты расположены в одном сегменте (сегменте данных). Как видим, основная часть работы – последовательное сравнение в цикле символов двух строк до первых несовпадающих символов или до конца строк – выполняется двумя тесно связанными командами repe cmpsb .
Остальные строковые команды имеют формат регистр-память, но они тоже ориентированы на задачи обработки строк (массивов) коротких и длинных целых чисел. Команды сканирование строки являются командами сравнения и, при использовании в цикле, хорошо подходят для поиска в строке заданного короткого (scasb) или длинного (scasw) целого значения. Эти команды отличаютсяя только битом размера аргументов w и имеет два неявных операнда op1 и op2, где op1=al для w=0, и op1=ax для w=1, а второй неявный операнд op2=<es,di> является соответственно байтом или словом. Если обозначить буквой r соответственно регистры al или ax, то схему выполнения команд сканирования строки можно записать так:
cmp r,<es,di>; φ(di)
Для иллюстрации использования команды сканирования напишем фрагмент программы для реализации следующей задачи: в массиве длинных целых чисел найти номер последнего нулевого элемента (пусть элементы нумеруются с единицы). Если в массиве нет нулевых элементов, то будем в качестве ответа выдавать номер ноль.
N equ 20000
D segment
. . .
X dw N dup (?)
. . .
D ends
C segment
assume cs:C,ds:D,es:D,ss:Stack
Start:mov ax,D
mov ds,ax
mov es,ax
. . .
mov cx,N; Число элементов
sub ax,ax; Образец для поиска=0
lea si,X+2*N-2; Последний элемент
std ; Обратный просмотр
repne scasw
jnz NoZero; Нет нулевых элементов
inc cx; Номер последнего нулевого
NoZero:outword cx
Заметим, что выход из нашего цикла возможен при попадании на нулевой элемент массива, при исчерпании счётчика цикла, а также при совпадении обоих этих условий. Следовательно, после команд repne scasw необходимо проверить, имел ли место случай просто выхода из цикла без нахождения нулевого элемента, что мы и сделали командой условного перехода jnz NoZero .
Следующими рассмотрим команды загрузки элемента строки, которые являются командами пересылки и, при использовании в цикле, хорошо подходят для эффективной последовательной загрузки на регистр коротких (lodsb) или длинных (lodsw) элементов целочисленного массива. Эти команды отличаются только битом размера аргументов w, и имеют два неявных операнда op1 и op2, где op1=al для w=0, и op1=ax для w=1, а второй неявный операнд op2=<ds,si> является соответственно байтом или словом. Если обозначить буквой r регистры al или ax, то схему выполнения этих команд можно записать так:
mov r,<ds,si>; φ(si)
В качестве примера использования команды загрузки напишем фрагмент программы для реализации следующей задачи: найти сумму тех элементов беззнакового массива коротких целых чисел, значения которых больше 100. Если в массиве нет таких элементов, то будем в качестве ответа выдавать число ноль.
N equ 10000
D segment
. . .
X db N dup (?)
. . .
D ends
C segment
assume cs:C,ds:D,ss:Stack
Start:mov ax,D
mov ds,ax
. . .
mov cx,N; Число элементов
sub dx,dx; Сумма=0
lea si,X; Адрес первого элемента
cld ; Прямой просмотр
L: lodsb
cmp al,100
jbe NoSum
add dl,al; Суммирование
adc dh,0; Прибавление CF
NoSum:loop L
outword dx
При суммировании коротких целых чисел мы получаем в качестве результата длинное целое число на регистре dx.
Последними в этом формате SS рассмотрим команды сохранения элемента строки, которые являются командами пересылки и, при использовании в цикле, хорошо подходят для эффективного присваивания всем элементам массива заданного короткого (stosb) или длинного (stosw) целого значения. Эти команды отличаются только битом размера аргументов w, и имеют два неявных операнда op1 и op2, где второй неявный операнд op2=al для w=0, и op2=ax для w=1, а первый неявный операнд op1=<es,di> является соответственно байтом или словом. Если обозначить буквой r регистры al или ax, то схему выполнения команд можно записать так:
mov <es,di>,r; φ(di)
В качестве примера использования команды сохранения напишем фрагмент программы для присваивания всем элементам массива длинных целых чисел значения единица.
N equ 30000
D segment
. . .
X dw N dup (?)
. . .
D ends
C segment
assume cs:C,ds:D,es:D,ss:Stack
Start:mov ax,D
mov ds,ax
mov es,ax
. . .
mov cx,N; Число элементов
mov ax,1; Присваиваемое значение
lea di,X; Адрес первого элемента
cld ; Прямой просмотр
rep stosw
Рассмотрим ещё один пример. Напишем фрагмент программы для решения задачи присваивания всем элементам знакового массива целых чисел Y абсолютных значений соответствующих им элементов массива X, т.е. Y:=abs(X)(учтите, что этот оператор присваивания – это только иллюстрация, так в Паскале написать нельзя).
N equ 5000
D segment
X dw N dup (?)
Y dw N dup (?)
Diagn db 'Большое значение в X!$'
. . .
D ends
C segment
assume cs:C,ds:D,es:D,ss:Stack
Start:mov ax,D
mov ds,ax
mov es,ax
. . .
mov cx,N
cld ; Прямой просмотр
lea si,X
lea di,Y
L: lodsw
cmp ax,0
jge L1
neg ax
jno L1
lea dx,Diagn
outstr
finish
L1: stosw
loop L
В приведённом примере массивы X и Y находятся в одном сегменте, поэтому регистры ds и es имеют одинаковые значения. Так как не у каждого отрицательного числа есть соответствующее ему абсолютное значение, то при обнаружении такой ситуации выдаётся аварийная диагностика, и выполнение программы прекращается.
На этом мы закончим наше краткое изучение строковых команд и перейдём к следующему классу команд – логическим командам.
9.2. Логические команды.
Все логические команды рассматривают свои операнды как упорядоченные наборы битовых значений. В таком наборе может содержаться 8 бит или 16 бит, в зависимости от размера операнда.1 Бит со значением 1 может трактоваться как логическое значение True, а нулевой бит – как логическое значение False.
Сначала рассмотрим двухадресные логические команды, имеющие такой общий вид
КОП op1,op2
Ниже приведены допустимые операнды таких команд:
op1 | op2 |
r8 | r8, m8, i8 |
m8 | r8, i8 |
r16 | r16, m16, i16 |
m16 | r16, i16 |
Как видно, у этих команд допускаются такие же операнды, как и у команд сложения, вычитания и сравнения.
Будем нумеровать биты в операндах логических команд так же, как и в регистрах, от нуля до некоторого числа N. Число N равно 7 для коротких (байтовых) операндов и равно 15 для длинных (размером в слово):
N 0 | ||||||||
. . . |
Таким образом, операнды логической команды можно рассматривать как массивы логических элементов, проиндексированных от 0 до N. На языке Паскаль такие массивы можно, например, описать в следующем виде
Var op1,op2: array[0..N] of Boolean;
Схему выполнения команды логического умножения
and op1,op2
на языке Паскаль можно записать в виде
for i:=0 to N do op1[i]:=op1[i] and op2[i]
Схему выполнения команды логического сложения
or op1,op2
на языке Паскаль можно записать в виде
for i:=0 to N do op1[i]:=op1[i] or op2[i]
Схему выполнения команды неэквивалентности (её часто называют также командой сложения по модулю 2)
xor op1,op2
на языке Паскаль можно записать в виде
for i:=0 to N do op1[i]:=op1[i] <> op2[i]
Команда логического тестирования
test op1,op2
выполняется точно так же, как и команда логического умножения, но без записи результата на место первого операнда, т.е. как и у команды сравнения с кодом cmp её единственным результатом является установка флагов.
Все перечисленные выше логические команды устанавливают флаги так же, как команда вычитания, например, флаги CF и OF будут разны нулю (никакого переноса и переполнения, конечно, нет), во флаг знака SF переносится левый бит результата и т.д. Но, как мы вскоре увидим, интерес для программиста представляет здесь только флаг нулевого результата ZF, который, как обычно, равен единице (поднят) для полностью нулевого результата, и равен нулю (опущен), если в результате есть хотя бы один ненулевой бит.
Следует заметить, что мы использовали цикл языка Паскаль только для более строгого описания работы логических команд. Не следует понимать это слишком буквально: на самом деле логические команды на современных ЭВМ выполняют операции над битами своих операндов одновременно (чаще всего за один такт работы центрального процессора), а не в последовательном цикле. 2
9.3. Команды сдвига.
Команды сдвига предназначены для сдвига (изменения индексов) битов в своём операнде. Операнд при этом можно рассматривать как битовый вектор, элементы которого пронумерованы от 0 до N так же, как и для рассмотренных выше логических команд. Команды сдвига имеют формат
КОП op1,op2
где op1 может быть r8,m8,r16 или m16, а op2 – иметь значение единицы или быть коротким регистром cl.1 Мы рассмотрим сначала команды сдвига вида КОП op1,1 , а затем обобщим их на случай команд вида КОП op1,cl .
Команда сдвига операнда на один бит влево имеет показанный ниже вид
shl op1,1