Перейти к основному содержимому

Строковые инструкции и таблицы поиска

Разработчику

Контекст: x86/x86-64, NASM. Краткая таблица мнемоник есть в справочнике; здесь — как ими пользоваться осознанно.

Идея «строковых» команд

Группа инструкций с суффиксом B/W/D/Q (байт, слово, dword, qword) работает с парой адресов, заданных неявно:

РегистрРоль
RSI (или ESI, SI)источник — «откуда читаем»
RDI (или EDI, DI)приёмник — «куда пишем» или «что сканируем»
RCXсчётчик повторений (с префиксом REP)
AL / AX / EAX / RAXоперанд для STOS, SCAS, LODS

Одна мнемоника без REP выполняется один раз и сдвигает RSI/RDI на размер элемента. С префиксом REP / REPE / REPNE — в цикле, пока RCX ≠ 0 и (для E/NE) пока выполняется условие по ZF.


Направление — CLD и STD

Флаг DF (Direction Flag) в RFLAGS:

  • CLD — DF = 0: после операции RSI и RDI увеличиваются (обход вперёд).
  • STD — DF = 1: указатели уменьшаются (удобно при копировании с конца буфера).

Перед любым блоком строковых команд задайте направление явно, обычно CLD.


Копирование блока — MOVSB / MOVSD

Эквивалент memcpy для непересекающихся областей:

; rsi = src, rdi = dst, rcx = байт (для movsb)
cld
rep movsb

Для выровненных блоков кратных 4 или 8 байтам быстрее rep movsd / rep movsq, если границы позволяют.

Современные CPU часто быстрее на mov из обычного цикла или внутренней memcpy libc из-за микрокода и предвыборки; строковые инструкции остаются компактными в коде и полезны в ядрах, загрузчиках и учебных примерах.


Заполнение буфера — STOSB

; rdi = начало, rcx = длина, al = байт-заполнитель
cld
rep stosb

Обнуление области: xor al, al + rep stosb.


Длина C-строки — SCASB

Поиск нулевого терминатора:

; rdi = строка, al = 0
cld
xor rcx, rcx
not rcx ; rcx = -1 (максимальный счёт)
xor al, al
repne scasb
not rcx
dec rcx ; rcx = число байт до (не включая) '\0'

REPNE SCASB останавливается, когда найден AL или исчерпан «длинный» счётчик.


Поиск символа в буфере

; rdi = буфер, rcx = известная длина, al = '?'
cld
repne scasb
je .found
; не найдено (ZF=0 после repne scasb если не совпало на последнем)
.found:
; rdi указывает на байт *после* совпадения

Проверяйте ZF после REPNE: совпадение — ZF=1.


Сравнение двух блоков — CMPSB

cld
repe cmpsb ; пока равны и rcx > 0
jne .not_equal

Таблица поиска (lookup table)

Частая задача: по коду символа или индексу 0..255 получить значение из статической таблицы (классификация, шифр подстановки, палитра).

Подход 1 — явный индекс (64-бит):

section .rodata
; 256 байт: class_table[i] = категория символа i
class_table:
db 0, 0, 0, 0, 0, 0, 0, 0 ; 0..7
; ... остальные 248 байт ...

section .text
movzx rax, byte [rdi] ; rdi -> входной байт
lea rbx, [rel class_table]
mov al, [rbx + rax]

Подход 2 — XLATB (историческая мнемоника):

mov rbx, table_base ; в 64-битном режиме база таблицы в RBX
mov al, [rsi] ; индекс в AL (0..255)
xlatb ; AL := byte [RBX + AL]

XLATB эквивалентен mov al, [rbx + al] с ограничением индекса байтом. В новом коде чаще пишут явную форму — читаемее и проще для отладки.

Таблица слов (не байт): индекс умножают на размер элемента:

mov eax, [index]
lea rbx, [rel words_table]
mov edx, [rbx + rax*4]

См. типы данных про endianness и выравнивание.


Соглашения и осторожности

ТемаЗамечание
Перекрытие src/dst при movsпри пересечении регионов поведение как у memmove — нужен другой алгоритм
RSI/RDI в вызовах ABIв Linux x86-64 RSI/RDI — аргументы; сохраняйте callee-saved или используйте после настройки вызова
RCXcaller-saved; REP портит счётчик
Строки UTF-8побайтовый разбор; один символ — 1–4 итерации, не один SCAS

Связанные материалы


См. также

Другие статьи этого же раздела в боковом меню (как на странице «О разделе»).