Для поиска определенного элемента в строке применяется следующий набор инструкций:
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 в этом случае хранит количество элементов, а не байтов, которые необходимо обработать при сканировании.
Например, найдем индекс символа в строке:
global _start section .data message db "Hello" ; исходная строка len equ $-message ; длина строки char db "l" ; символ для поиска section .text _start: mov rdi, message ; загружаем адрес переменной message mov al, [char] ; в AL символ для поиска mov rcx, len ; в RCX количество символов строки repne scasb ; ищем байт jz found ; если символ найден, устанавливается флаг нуля ZF mov rdi, -1 ; если символ не найден, в RDI число -1 jmp exit found: sub rdi, message ; если символ найден, получаем индекс следующего символа после найденного dec rdi ; отнимает 1 байт - получем индекс найденного символа exit: mov rax, 60 syscall
Здесь в строке "Hello" ищем символ "l". Для этого строку для поиска загружаем в RDI, а в AL - символ (байт), который будем искать.
Если при выполнении инструкции scas
элемент был найден, то устанавливается флаг нуля. И мы можем это проверить и выполнить определенные действия. В данном случае,
если символ найден, от значения в RDI (адрес следующего символа за найденным) отнимаем начальный адрес строки message (адрес первого символа строки message) и таким образом получаем расстояние в байтах между первым символом и следующим за найденным символом.
Поскольку все элементы строки - байты, то это расстояние будет представлять индекс следующего символа за найденным. Соответственно, чтобы получить индекс найденного символа (а не следующего за ним), надо от этого расстояния отнять 1 (размер одного элемента в байтах). Например, при поиске символа "l" в строке "Hello" мы получим 2 - это индекс третьего символа.
Если символ не найден, то в регистр RDI помещаем -1.
Инструкция scasb очень удобна, если нам надо найти длину строку, которая завершется каким-нибудь концевым символом, например, нулевым байтом. К примеру:
global _start section .data message db "Hello METANIT.COM", 0 ; исходная строка с концевым нулевым байтом section .text _start: mov rdi, message ; загружаем адрес переменной message mov rsi, rdi ; копируем адрес в RSI для последующего поиска индекса mov al, 0 ; в AL символ для поиска mov rcx, -1 ; максимальное число repne scasb ; ищем байт sub rdi, message ; если символ найден, получаем индекс следующего символа после найденного dec rdi ; отнимает 1 байт - получем индекс найденного символа - длину строки mov rax, 60 syscall
В данном случае предполагаем, что наша строка завершается нулевым байтом, поэтому ищем в ней индекс нулевого байта. Основная сложность здесь заключается в том, что строки могут быть разные по длине, соответственно мы не знаем точное количество символов. Поэтому в регистр RCX помещаем очень большое положительное число, которое соответствует отрицательному числу -1 в дополнительном коде:
mov rcx, -1 ; максимальное число