поля. Программа включала три команды для инициализации и пять команд для
цикла. Команда MOVS с префиксом REP и длиной в регистре CX может выполнять
пересылку любого числа символов более эффективно.
Для области, принимающей строку, сегментным регистром, является
pегистр ES, а регистр DI содержит относительный адрес области, передающей
строку. Сегментным регистром является регистр DS, а регистр SI содержит
относительный адрес. Таким образом, в начале программы перед выполнением
команды MOVS необходимо инициализировать регистр ES вместе с регистром DS,
а также загрузить требуемые относительные адреса полей в регистры DI и SI.
В зависимости от состояния флага DF команда MOV S производит увеличение
или уменьшение на 1 (для байта) или на 2 (для слова) содержимого регистров
DI и SI.
Приведем команды, эквивалентные цепочечной команде REP MOVSB:
JCXZ LABEL2
LABEL1: MOV AL,[SI]
MOV [DI],AL
INC/DEC DI ;Инкремент или декремент
UNC/DEC SI ;Инкремент или декремент
LOOP LABEL1
LABEL2: ...
В программе на рис.11.1 процедура C10MVSB использует команду MOVSB
для пересылки содержимого десятибайтового поля NAME1 в поле NAME2. Первая
команда CLD сбрасывает флаг направления в 0 для обеспечения процесса
пересылки слева направо. В нормальном состоянии флаг DF обычно имеет
нулевое значение и команда CLD используется из предосторожности.
__________________________________________________________________________
page 60,132
TITLE STRING (EXE) Проверка строковых операций
; ---------------------------------------------------
STACKSG SEGMENT PARA STACK 'Stack'
DW 32 DUP(?)
STACKG ENDS
; ---------------------------------------------------
DATASG SEGMENT PARA 'Data'
NAME1 DB 'Assemblers' ;Элементы данных
NAME2 DB 10 DUP(' ')
NAME3 DB 10 DUP(' ')
DATASG ENDS
; ---------------------------------------------------
CODESG SEGMENT PARA 'Code'
BEGIN PROC FAR ;Основная процедура
ASSUME CS:CODESG,DS:DATASG,SS:STACKSG,ES:DATASG
PUSH DS
SUB AX,AX
PUSH AX
MOV AX,DATASG
MOV DS,AX
MOV ES,AX
CALL C10MVSB ;Подпрограмма MVSB
CALL D10MVSW ;Подпрограмма LODS
CALL E10LODS ;Подпрограмма LODS
CALL F10STOS ;Подпрограмма CMPS
CALL H10SCAS ;Подпрограмма SCAS
RET
BEGIN ENDP
; Использование MOVSB:
; -------------------
C10MVSB PROC NEAR
CLD
LEA SI,NAME1
LEA D1,NAME2
MOV CX,10 ;Переслать 10 байтов
REP MOVSB ; из NAME1 в NAME2
RET
C10MVSB ENDP
; Использование MOVSW:
; -------------------
D10MVSW PROC NEAR
CLD
LEA SI,NAME2
LEA DI,NAME3
MOV CX,05 ;Переслать 5 слов
REP MOVSW ; из NAME2 в NAME3
RET
D10MVSW ENDP
; Использование LODSW:
; -------------------
E10LODS PROC NEAR
CLD
LEA SI,NAME1 ;Загрузить первое слово
LODSW ; из NAME1 в AX
RET
E10LODS ENDP
; Использование STOSW:
; -------------------
F10STOS PROC NEAR
CLD
LEA D1,NAME3
MOV CX,05
MOV AX,2020H ;Переслать пробелы
REP STOSW ; в NAME3
RET
F10STOS ENDP
; Использование CMPSB:
; -------------------
G10CMPS PROC NEAR
CLD
MOV CX,10
LEA SI,NAME1
LEA DI,NAME2
REPE CMPSB ;Сравнить NAME1 и NAME2
JNE G20 ;Не равны?
MOV BH,01
G20: MOV CX,10
LEA SI,NAME2
LEA DI,NAME3
REPE CMPSB ;Сравнить NAME2 и NAME3
JE G30 ;Если равны, то выйти
MOV BL,02
G30: RET
G10CMPS ENDP
; Использование SCASB:
; -------------------
H10SCAS PROC NEAR
CLD
MOV CX,10
LEA DI,NAME1
MOV AL,'m' ;Поиск символа 'm'
REPNE SCASB ; в NAME1
JNE H20 ;Если не найден - выйти
MOV AH,03
H20: RET
H10SCAS ENDP
CODES ENDS
END BEGIN
__________________________________________________________________________
Рис.11.1. Использование цепочечных команд.
Две команды LEA загружают регистры SI и DI относительными адресами
NAME1 и NAME2 соответственно. Так как регистры DS и ES были ранее
инициализированы адресом DATASG, то полные адреса полей NAME1 и NAME2
будут в регистрах ES:DI и DS:SI. (COM программа автоматически
инициализирует регистры ES и DS). Команда MOV заносит в регистр CX
значение 10 - длину полей NAME1 и NAME2. Команда REP MOVSB выполняет
следующее:
- Пересылает самый левый байт из поля NAME1 (адресованного
pегистрами ES:DI) в самый левый байт поля NAME2 (адресованного
регистрами DS:SI).
- Увеличивает на 1 адреса в регистрах DI и SI для следующего
байта.
- Уменьшает CX на 1.
- Повторяет перечисленные действия (в данном случае 10 раз),
пока содержимое регистра CX не станет равным нулю.
Поскольку флаг DF имеет нулевое значение, команда MOVSB увеличивает
адреса в регистрах DI и SI, и в каждой итерации процесс переходит на байт
вправо, т.е. пересылает байт из NAME1+1 в NAME2+1 и т.д. Если бы флаг DF
был равен 1, тогда команда MOVSB уменьшала бы адреса в регистрах DI и SI,
выполняя процесс справа налево. Но в этом случае регистры SI и DI
необходимо инициализировать адресами последних байтов полей, т.е. NAME1+9
и NAME2+9 соответственно.
В процедуре D10MVSW (рис.11.1) используется команда MOVSW,
пересылающая одно слово за одно выполнение. Так как команда MOVSW
увеличивает адреса в регистрах DS и SI на 2, операция требует только пять
циклов. Для процесса пересылки справа налево регистр SI должен быть
инициализирован адресом NAME1+8, а регистр DI - NAME2+8.
LODS: ЗАГРУЗКА СТРОКИ
________________________________________________________________
Команда LODS загружает из памяти в регистр AL один байт или в регистр
AX одно слово. Адрес памяти определяется регистрами DS:SI. В зависимости
от значения флага DF происходит увеличение или уменьшение регистра SI.
Поскольку одна команда LODS загружает регистр, то практической пользы
от префикса REP в данном случае нет. Часто простая команда MOV полностью
адекватна команде LODS, хотя MOV генерирует три байта машинного кода, а
LODS - только один, но требует инициализацию регистра SI. Можно
использовать команду LODS в том случае, когда требуется продвигаться вдоль
строки (по байту или по слову), проверяя загружаемый регистр на конкретное
значение.
Команды, эквивалентные команде LODSB:
MOV AL,[SI]
INC SI
На рис.11.1 процедура E10LODS демонстрирует использование команды
LODSW. В примере обрабатывается только одно слово: первый байт из области
NAME1 (содержащий As) заносится в регистр AL, а второй байт - в регистр
AH. В результате в регистре AX получится значение sA.
STOS: ЗАПИСЬ СТРОКИ
________________________________________________________________
Команда STOS записывает (сохраняет) содержимое регистра AL или AX в
байте или в слове памяти. Адрес памяти всегда представляется регистрами
ES:DI. В зависимости от флага DF команда STOS также увеличивает или
уменьшает адрес в регистре DI на 1 для байта или на 2 для слова.
Практическая польза команды STOS с префиксом REP - инициализация
области данных конкретным значением, например, очистка дисплейного буфера
пробелами. Длина области (в байтах или в cловах) загружается в регистр AX.
Команды, эквивалентные команде REP STOSB:
JCXZ LABEL2
LABEL1: MOV [DI],AL
INC/DEC DI ;Инкремент или декремент
LOOP LABEL1
LABEL2: ...
На рис.11.1 процедура F10STOS демонстрирует использование команды
STOSW. Операция осуществляет запись шест. 2020 (пробелы) пять раз в
область NAME3, причем значение из регистра AL заносится в первый байт, а
из регистра AH - во второй. По завершении команды регистр DI содержит
адрес NAME3+10.
CMPS: СРАВНЕНИЕ СТРОК
________________________________________________________________
Команда CMPS сравнивает содержимое одной области памяти (адресуемой
регистрами DS:SI) с содержимыми другой области (адресуемой как ES:DI). В
зависимости от флага DF команда CMPS также увеличивает или уменьшает
адреса в регистрах SI и DI на 1 для байта или на 2 для слова. Команда CMPS
устанавливает флаги AF, CF, OF, PF, SF и ZF. При использовании префикса
REP в регистре CX должна находиться длина сравниваемых полей. Команда CMPS
может сравнивать любое число байт или слов.
Рассмотрим процесс сравнения двух строк, содержащих имена JEAN и
JOAN. Сравнение побайтно слева направо приводит к следующему:
J : J Равно
E : O Не равно (E меньше O)
A : A Равно
N : N Равно
Сравнение всех четырех байт заканчивается сравнением N:N -
pавно/нуль. Так как имена "не равны", операция должна прекратиться, как
только будет обнаружено условие "не равно". Для этих целей команда REP
имеет модификацию REPE, которая повторяет сравнение до тех пор, пока
сравниваемые элементы равны, или регистр CX не pавен нулю. Кодируется
повторяющееся однобайтовое сравнение следующим образом:
REPE CMPSB
На рис.11.1 в процедуре G10CMPS имеются два примера использования
команды CMPSB. В первом примере происходит сравнение содержимого полей
NAME1 и NAME2. Так как ранее команда MOVSB переслала содержимое поля NAME1
в поле NAME2, то команда CMPSB продолжается на всех десяти байтах и
завершается состоянием pавно/нуль: флаг SF получает значение 0
(положительно) и флаг ZF - 1(нуль).
Во втором примере сравнивается поля NAME2 и NAME3. Ранее команда
STOSW заполнила поле NAME3 пробелами, поэтому команда CMPB завершается
после сравнения первых же байт с результатом "больше/неравно": флаг SF
получает значение 0 (положительно) и флаг ZF - 0 (не нуль).
Первый пример заканчивается с результатом "равно/нуль" и заносит 01 в
регистр BH. Второй пример заканчивается с результатом "неравно" и заносит
02 в регистр BL. При трассировке команд с помощью отладчика DEBUG можно
увидеть, что в конце процедуры G10CMPS регистр BX будет содержать значение
0102.
Предупреждение! Показанные примеры используют команду CMPSB для
сравнения одного байта за одно выполнение. При использовании команды CMPSW
для сравнения одного слова, необходимо инициализиpовать регистр CX
значением 5. Кроме того следует помнить, что команда CMPSW при сравнении
слов переставляет байты. Например, сравнивая имена SAMUEL и ARNOLD команда
CMPSW выбирает вместо SA и AR переставленные значения, т.е. AS и RA. В
результате вместо "больше" получится "меньше", т.е. неправильный
результат. Таким образом команда CMPSW работает правильно только при