1
Лабораторная работа по курсу "Организация ЭВМ и систем"
Использование команд обработки строк в ассемблере IBM PC
Цель работы: познакомиться с командами обработки блоков байтов (слов, двойных слов).
1. Команды обработки строк
Команды обработки строк производят операции пересылки, сравнения, сканирования, загрузки и сохранения над блоками байтов и слов (в 32-битных МП Intel - и двойных слов) памяти длиной до 64 Кбайт (в 32-битных МП - до 4 Гбайт).
Команды обработки строк обрабатывают строку по одному байту, слову или двойному слову за прием. При этом оба операнда находятся в памяти: строка-источник находится в сегменте данных и адресуется при помощи DS:SI, строка-приемник - в дополнительном сегменте данных и адресуется при помощи ES:DI. (В 32-битных МП - при помощи DS:ESI и ES:EDI соответственно.) Команды обработки строк автоматически модифицируют указатели для адресации следующих элементов строки-источника и строки-приемника по завершении выполнения команды.
Флаг направления. Флаг направления DF в регистре флагов FLAGS (EFLAGS) определяет, будут ли значения SI и DI увеличены или уменьшены по завершении выполнения команды обработки строк. Если DF=0, это соответствует прямому направлению (от меньших адресов к бoльшим), значения регистров SI и DI увеличиваются после обработки одного элемента строки; если DF=1, это соответствует обратному направлению, значения SI и DI уменьшаются. Состоянием флага DF можно управлять с помощью двух команд:
CLD (CLear Direction flag) |
- сбросить флаг направления в ноль; |
STD (SeT Direction flag) |
- установить флаг направления в единицу. |
Префиксы повторения. Одна команда обработки строк может обработать группу последовательных элементов памяти. Для этого перед ней требуется указать префикс повторения REP (от REPeat), который заставляет процессор выполнить повторения команды CX/ECX раз. Например, последовательность команд
mov |
cx, 500 |
rep movs |
dest,source |
заставит процессор выполнить команду MOVS 500 раз, уменьшая значение регистра СХ после каждого повторения.
Остальные префиксы повторения используют при принятии решения о продолжении или прекращении повторений флаг нуля ZF. Поэтому они применимы только к командам сравнения строк и поиска значения в строке, которые воздействуют на флаг ZF.
Префикс REPNE (REPeat while Not Equal - повторять, пока не равно), имеющий синоним REPNZ (REPeat while Not Zero - повторять, пока не нуль), дают указание повторять команду, пока флаг ZF равен 0 и значение регистра CX не равно 0. Если приписать префикс REPNE команде сравнения строк CMPS, то операция сравнения будет повторяться до первого совпадения элементов строк или уменьшения значения в регистре CX до нуля. Например, последовательность команд
mov cx,100 |
dest,source |
repne cmps |
будет поэлементно сравнивать строки SOURCE и DEST до тех пор, пока не будет просмотрено 100 пар элементов или пока в строке DEST не найдется элемент, совпадающий с соответствующим ему элементом строки SOURCE.
Действие префикса REPE (REPeat while Equal - повторять, пока равно), имеющего синоним REPZ (REPeat while Zero - повторять, пока нуль) обратно действию префикса REPNE (REPNZ). Он дает указание повторять следующую за ним команду, пока флаг ZF равен 1 и значение в регистре CX не равно 0. Последовательность команд
mov cx,100
2
repe |
cmps |
dest,source |
будет поэлементно сравнивать строки SOURCE и DEST до тех пор, пока не будет просмотрено 100 пар элементов или пока в строке DEST не найдется элемент, НЕ совпадающий с соответствующим ему элементом SOURCE. Действие префиксов и флага направления распространяется на все команды обработки строк.
Команды пересылки строк. Команда MOVS копирует байт или слово (а также двойное слово в 32-битных МП) из одной части памяти в другую. Она имеет формат:
movs dest, source
Здесь SOURCE - строка в сегменте данных, а DEST - строка в дополнительном сегменте. Как и в случае команды CMPS, МП использует регистр SI (ESI) для адресации в сегменте данных и регистр DI (EDI) для адресации в дополнительном сегменте. Указание в качестве операндов стро- ки-приемника и строки-источника не освобождает от необходимости инициализировать сегменты ES:DI и DS:SI перед выполнением команды MOVS. По виду операндов определяется только размер копируемых элементов - байты, слова, двойные слова.
Можно заменить сегмент, соответствующий регистру SI, но не регистру DI. Например, можно добиться копирования строки из одной части дополнительного сегмента в другую.
Следующий фрагмент копирует 100 байтов из строки SOURCE, находящейся в сегменте данных, в строку DEST, которая находится в дополнительном сегменте.
|
cld |
si,source |
; DF=0 для обработки в прямом направлении. |
|
lea |
;Занести смещение source в SI, |
|
|
lea |
di,es:dest |
;а смещение dest - в DI |
rep |
mov |
cx,100 |
;CX - счетчик элементов |
movs |
dest,source |
;Скопировать 100 байтов |
При этом в программе должен быть описан сегмент данных со строкой-источником и дополнительный сегмент со строкой-приемником, например:
dest db |
100 dup(?) |
В данном случае команда MOVS пересылает байты, т.к. указана директива DB - резервирование байтов. Если бы на месте DB стояла директива DW, то команда MOVS пересылала бы слова, а не байты. Для 32-разрядных МП наиболее эффективной является пересылка двойных слов.
На самом деле кодового эквивалента команде MOVS не существует. Она преобразуется либо в команду MOVSB (пересылка байтов), либо в команду MOVSW (пересылка слов), либо MOVSD (пересылка двойных слов, только для 32-битных МП). Аргументы в команде MOVS нужны только для извлечения информация о размере пересылаемых элементов, которая используется транслятором для принятия решения об использовании той или иной формы команды.
Команды MOVSB, MOVSW и MOVSD могут быть применены и в явном виде. Аргументы им в таком случае не требуются. Пример, приведенный выше, можно переписать в виде:
|
cld |
si, |
source |
|
lea |
||
|
lea |
di, |
es:dest |
rep |
mov |
cx, |
100 |
movsb |
|
|
В виде кодов оба примера будут выглядеть идентично. Для большей надежности при написании программ рекомендуется пользоваться именно явными формами команд MOVSB, MOVSW и MOVSD. Остальные строковые команды - SCAS, CMPS, LODS, STOS - тоже имеют явные формы, и все сказанное относительно MOVS справедливо и для них.
Команды сравнения строк. Действие команды CMPS похоже на действие обычной команды CMP. Подобно команде CMP, команда CMPS сравнивает операнды, вычитая их один из другого. Однако CMP вычитает операнд-источник (SOURCE) из операнда-приемника (DEST), а CMPS - наоборот, операнд-приемник (DEST) из операнда-источника (SOURCE). Команда CMPS имеет формат
cmps |
dest, source |
DEST и SOURCE имеют тот же смысл, что и в описании команды MOVS.
3
Команды сканирования строк. Основная команда группы команд сканирования строк SCAS (SCAn String) имеет формат
scas dest
где DEST идентифицирует строку в дополнительном сегменте, смещение первого элемента которой загружается в регистр DI/EDI. Сканирование заключается в последовательном просмотре элементов строки и сравнении их с шаблоном поиска, который хранится в регистре AL (если это байт), AX (если это слово) или EAX (двойное слово - в 32-битных МП). Следующий фрагмент
|
cld |
di, es:s_string |
|
|
lea |
;Код пробела |
|
|
mov |
al, 32 |
|
repe |
mov |
cx, 100 |
|
scas |
s_string |
|
|
|
je |
notfound |
|
|
... |
|
|
notfound:...
просматривает до 100 элементов строки байтов S_STRING в поисках элемента, отличного от пробела. Если такой элемент обнаружен, то сканирование прекращается, даже если строка просмотрена не полностью, в регистре DI остается смещение следующего за найденным элемента, а флаг ZF сбрасывается в 0 (что указывает на несовпадение при сравнении). Последующая команда JE покажет, найден такой элемент (ZF=0 - отсутствие перехода) или не найден (ZF=1 - переход).
Команды загрузки и сохранения элемента строки. Команда LODS (LoaD String) пересыла-
ет элемент операнда-источника, адресованный регистром SI/ESI, из сегмента данных в регистр AL (при пересылке байта), в AX (при пересылке слова) или EAX (при пересылке двойного слова), а затем изменяет SI (ESI) так, чтобы он указывал на следующий элемент строки. Следующий фрагмент программы сравнивает строки DEST и SOURCE длиной 50 байтов каждая в поисках первой пары несовпадающих элементов. Если обнаружено несовпадение, то элемент строки SOURCE загружается в регистр AL.
|
cld |
di, es:dest |
;DF=0 - прямое направление |
|
lea |
;Загрузка смещения приемника |
|
|
lea |
si, source |
;Загрузка смещения источника |
repe |
mov |
cx, 500 |
;Загрузка счетчика элементов |
cmpsb |
match |
;Искать до несовпадения |
|
|
jcxz |
;Несовпадение обнаружено? |
|
|
dec |
si |
;Да. Подправить регистр SI |
|
lods |
source |
;Считать элемент в регистр AL |
|
... |
|
|
match: ...
Команда сохранения элемента строки STOS (STOre String) пересылает байт из регистра AL или слово из регистра AX/EAX в элемент операнда DEST, находящийся в дополнительном сегменте и адресуемый регистром DI/EDI, а затем изменяет значение регистра DI/EDI так, чтобы оно указывало на следующий элемент строки. Будучи многократно повторенной, команда STOS удобна для заполнения строки заданным значением. Например, следующий фрагмент программы сканирует строку W_STRING длиной в 200 слов в поисках первого ненулевого элемента. Если такой элемент обнаружен, то он и следующие за ним пять слов обнуляются.
|
cld |
di, es:w_string |
;Адрес строки |
|
lea |
||
|
mov |
ax, 0 |
;Искомое значение |
repe |
mov |
cx, 200 |
;Счетчик поиска |
scasw |
w_zero |
;Искать |
|
|
jcxz |
;Нашли не нуль? |
|
|
sub |
di, 2 |
;Да. Подправить DI, |
rep |
mov |
CX, 6 |
;и шесть слов |
stos |
w_string |
;заполнить нулями. |
|
w_zero: ... |
|
;Нет, Продолжить отсюда. |
Команда JCXZ (переход, если CX=0; у 32-битных МП есть команда JECXZ, проверяющая на 0
4
регистр ECX) удобна для контроля достижения конца строк при многократно повторяемых строковых операциях. Так как эта команда не реагирует на состояние флагов, то несовпадение последних элементов строк (или одноэлементных строк) или несоответствие образцу может быть обнаружено уже после выполнения перехода (на метку MATCH - в первом примере, на W_ZERO - во втором), где необходимо предусмотреть дополнительную проверку.
2. Пример
Задание: считать с клавиатуры строку символов, преобразовать все большие латинские буквы в маленькие и вывести результат на экран.
sseg |
segment para stack 'stack' |
|
|
||
sseg |
db |
64 dup |
|
|
|
ends |
|
|
|
|
|
data |
segment |
|
|
|
|
buffer |
db |
80,0 |
|
|
|
string |
db |
80 dup(0),'$' |
|
|
|
prompt |
db |
'Вариант Х- Считать строку и преобразовать',13,10 |
|||
|
db |
'прописные буквы в строчные ',13,10 |
|
||
|
db |
'Студент Иванов И.И., группа МП-25',13,10,10 |
|||
message |
db |
'Введите строку > $' |
|
|
|
db |
13,10,'Результат > ' |
|
|
||
res_str |
db |
80 dup(0) |
|
|
|
ending |
db |
13,10,'Конец',13,10,'$' |
|
|
|
letter |
db |
13,10,'Символ ' |
|
|
|
sym |
db |
?,'$' |
|
|
|
tranx |
db |
' преобразован в строчный$' |
|
|
|
data |
ends |
|
|
|
|
main |
segment |
|
|
|
|
start: |
assume cs:main,ds:data,ss:sseg,es:data |
|
|||
mov |
ax, data |
; ds = адрес сегмента data |
|||
|
mov |
ds, ax |
|||
|
mov |
es, ax |
; es = адрес сегмента data |
||
|
mov |
dx, offset prompt ; приглашение |
|
|
|
|
mov |
ah, 9h |
|
|
|
|
int |
21h |
|
|
|
|
mov |
dx, offset buffer ; куда считывать |
|
||
|
mov |
ah, 0Ah |
; считать строку |
|
|
|
int |
21h |
mov si,offset string |
||
|
lea |
si, string |
; эквивалентно команде |
||
|
lea |
di, es:res_str |
; эквивалентно команде |
mov si,offset es:string |
|
|
mov |
cx, 0 |
|
; длина считанной строки |
|
next: |
mov |
cl, byte ptr buffer+1 |
|||
lodsb |
|
; считать очередной символ |
|||
|
mov |
byte ptr sym, al |
|
|
|
|
push |
ax |
|
; сообщение |
|
|
mov |
dx, offset letter |
|||
|
mov |
ah, 9h |
|
|
|
|
int |
21h |
|
|
|
|
pop |
ax |
|
; Буква? |
|
|
cmp |
al, 'A' |
|
|
|
|
jl |
nosym |
|
|
|
|
cmp |
al, 'Z' |
|
|
|
|
jg |
nosym |
|
|
|
|
push |
ax |
|
; сообщение |
|
|
mov |
dx, offset tranx |
|||
|
mov |
ah, 09h |
|
|
|
|
int |
21h |
|
|
|
|
pop |
ax |
|
; да, преобразование |
|
nosym: |
xor |
al, 00100000B |
|
||
stosb |
|
; нет |
|
5
|
loop |
next |
; на следующий |
|
mov |
al, '$' |
; фиксируем конец строки |
|
stosb |
dx, offset message |
|
|
mov |
; результат |
|
|
mov |
ah, 9h |
|
|
int |
21h |
; завершающее сообщение |
|
mov |
dx, offset ending |
|
|
mov |
ah, 9h |
|
|
int |
21h |
; Функция 4Ch завершения программы |
|
mov |
ax,4C00h |
|
main |
int |
21h |
|
ends |
start |
|
|
|
end |
|
3.Порядок выполнения работы
1.При подготовке к лабораторной работе (дома) занесите в отчет текст программы своего варианта и краткие сведения о командах обработки строк;
2.Выполните программу своего варианта. Результаты покажите преподавателю.
3.Запишите в отчет шестнадцатеричное значение байта, соответствующего различным вариантам префикса повторения в командах обработки строк.
4. Варианты заданий.
Программа должна быть написана с использованием команд обработки строк. Размер строк по-
лагать равным 80 байтам. В начале программы вывести ФИО, группу и описание задания.
1, 10, 19. Считать с клавиатуры строку символов, затем каждый пробел в ней заменить символом подчеркивания. Вывести результат на экран.
2, 11, 20. Считать с клавиатуры строку символов, копировать ее в другую строку, исключая пробелы и знаки препинания: точки, запятые, точки с запятой, двоеточия. Вывести результат на экран.
3, 12, 21. Считать с клавиатуры две строки символов, сформировать третью строку, состоящую из совпадающих символов первой и второй строк. Вывести результат на экран.
4, 13, 22. Считать с клавиатуры две строки символов, сформировать третью строку, состоящую из символов первой строки, не совпадающих с соответствующими символами из второй строки. Вывести результат на экран.
5, 14, 23. Считать с клавиатуры строку символов, сформировать другую строку, представляющую собой первую строку в обратном порядке. Подсчитать количество знаков препинания и вывести результат на экран.
6, 15, 24. Моделирование ввода пароля: считать посимвольно с клавиатуры строку символов без эхо-печати. Если вводится пробел, то выдать звуковой сигнал, иначе отпечатать на экране звездочку и сохранить введенный символ в строке. Затем вывести строку на экран.
7, 16, 25. Считать с клавиатуры строку символов, затем запросить еще один символ, подсчитать количество его появлений в строке и вывести на экран.
8, 17, 26. Считать с клавиатуры строку символов. В случае появления среди первых 15 символов символа * вывести на экран его порядковый номер.
9, 18. Сравнить введенную строку со строкой, находящейся в памяти, и вывести на экран позиции несовпадающих символов.
---------------------------------------------------------------------------------------------------
© Лабораторная работа подготовлена А.Я. Пьянзиным.