Для поиска определенного элемента в строке применяется следующий набор инструкций:
scasb: поиск в строке байтов
scasw: поиск в строке слов
scasd: поиск в строке двойных слов
scasq: поиск в строке четверных слов
Данные инструкции принимают только один операнд - строку, в которую помещается результат поиска и адрес которой загружается в регистр RDI.
Строка для поиска загружается в регистре AL (scasb
), AX (scasw
), EAX (scasd
) или RAX (scasq
).
Инструкции scas
сравнивают значение в регистре AL/AX/EAX/RAX со значением, на которое указывает RDI, а затем увеличивает (или уменьшает) адрес в
RDI на 1, 2, 4 или 8. В соответствии с результатом сравнения процессор устанавливает флаги. Так, если символ найден, то устанавливается флаг нуля, а регистр RDI указывает на символ, следующий за найденным.
При применении префикса repe инструкции scas
просматривают строку в поисках элемента, не соответствующего значению в аккумуляторе.
При использовании префикса repne scas сканирует строку в поисках первого элемента, равного значению в аккумуляторе.
Регистр RCX в этом случае хранит количество элементов, а не байтов, которые необходимо обработать при сканировании.
Например, найдем индекс символа в строке:
.globl _start .data str: .asciz "test" message: .asciz "Hello" # исходная строка len = . - message # длина строки char: .byte 'l' # символ для поиска .text _start: movq $message, %rdi # загружаем адрес переменной message movb char, %al # в AL символ для поиска movq $len, %RCX # в RCX количество символов строки repne scasb # ищем байт jz found # если символ найден, устанавливается флаг нуля ZF movq $-1, %rdi # если символ не найден, в RDI число -1 jmp exit found: subq $message, %rdi # если символ найден, получаем индекс следующего символа после найденного decq %rdi # отнимает 1 байт - получем индекс найденного символа exit: movq $60, %rax syscall
Здесь в строке "Hello" ищем символ "l". Для этого строку для поиска загружаем в RDI, а в AL - символ (байт), который будем искать.
Если при выполнении инструкции scas
элемент был найден, то устанавливается флаг нуля. И мы можем это проверить и выполнить определенные действия. В данном случае,
если символ найден, от значения в RDI (адрес следующего символа за найденным) отнимаем начальный адрес строки message (адрес первого символа строки message) и таким образом получаем расстояние в байтах между первым символом и следующим за найденным символом.
Поскольку все элементы строки - байты, то это расстояние будет представлять индекс следующего символа за найденным. Соответственно, чтобы получить индекс найденного символа (а не следующего за ним), надо от этого расстояния отнять 1 (размер одного элемента в байтах). Например, при поиске символа "l" в строке "Hello" мы получим 2 - это индекс третьего символа.
Если символ не найден, то в регистр RDI помещаем -1.
Инструкция scasb очень удобна, если нам надо найти длину строку, которая завершется каким-нибудь концевым символом, например, нулевым байтом. К примеру:
.globl _start .data message: .asciz "Hello METANIT.COM" # исходная строка .text _start: movq $message, %rdi # загружаем адрес переменной message movq %rdi, %rsi # копируем адрес в RSI для последующего поиска индекса movb $0, %al # в AL символ для поиска movq $-1, %rcx # максимальное число repne scasb # ищем байт subq $message, %rdi # если символ найден, получаем индекс следующего символа после найденного decq %rdi # отнимает 1 байт - получем индекс найденного символа exit: movq $60, %rax syscall
В данном случае предполагаем, что наша строка завершается нулевым байтом (создается с помощью директивы .asciz), поэтому ищем в ней индекс нулевого байта. Основная сложность здесь заключается в том, что строки могут быть разные по длине, соответственно мы не знаем точное количество символов. Поэтому в регистр RCX помещаем очень большое положительное число, которое соответствует отрицательному числу -1 в дополнительном коде:
movq $-1, %rcx # максимальное число