Строковые инструкции и таблицы поиска
Контекст: 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 или используйте после настройки вызова |
RCX | caller-saved; REP портит счётчик |
| Строки UTF-8 | побайтовый разбор; один символ — 1–4 итерации, не один SCAS |
Связанные материалы
- Взаимодействие с C — строки как
char *. - Чтение листинга — как выглядит
rep movsbв дизассемблере. - Векторная обработка нескольких байт параллельно — числа с плавающей точкой и SIMD.
См. также
Другие статьи этого же раздела в боковом меню (как на странице «О разделе»). Полный отказ от высокоуровневых языков нецелесообразен. Поэтому большинство компиляторов поддерживают встроенный ассемблер — механизм вставки ассемблерных инструкций непосредственно в код на C/C++. %macro, %define и %if в NASM — шаблоны инструкций без дублирования исходного текста. Разделение программы на .asm-файлы, global и extern, сборка объектников и линковка в ELF. Вызов ассемблерных функций из C и наоборот — System V AMD64 ABI, выравнивание стека, сборка. Секции ELF, символы, objdump и сопоставление дизассемблирования с исходным NASM-кодом. SSE2 для float и double, регистры XMM, выравнивание; кратко про стек x87 и AVX. Microsoft x64 calling convention, shadow space, вывод в консоль и файлы через API вместо syscall. Основы ассемблера - синтаксис Intel/AT&T, базовые инструкции и принципы низкоуровневого программирования. Архитектура ассемблерных программ - взаимодействие с ОС, вызовы библиотек и организация низкоуровневого кода. Типизация, набор правил определения типа данных значений языка. Управляющие конструкции и команды процессора в ассемблере - регистр команд, переходы и управление потоком исполнения. Команды и подпрограммы в ассемблере - передача параметров, соглашения вызовов и работа со стеком.История ассемблерных языков
Макросы и условная сборка
Несколько модулей и линковка
Взаимодействие с C и C++
Чтение исполняемого файла и листинга
Числа с плавающей точкой и SIMD
Windows x64, WinAPI и отличия от Linux
Основы ассемблера
Архитектура ассемблерных программ
Типы данных и регистры
Управляющие конструкции и команды процессора
Команды и подпрограммы