Ассемблер для Linux. Строковые операции

22 July

И Old Programmer продолжает публиковать книгу о программирование на языке ассемблера в операционной системе Linux. Сегодня говорим об адресации x86-64.

Параграф 3.2.

Строковые операции процессора x86-64

Под строковыми операциями (иногда их называют цепочечными) будем понимать команды, позволяющие автоматизировать действиями над протяженными областями памяти. Перечислим эти команды: movs, lods, stos, cmps, scas. Каждая из команд выполняет только одну итерацию (одно элементарное действие). Но если использовать префикс repe (repne) (префикс повторения) можно распространить действие на последовательность данных. Разумеется, данные команды можно эффективно использовать и в обычных циклах, о которых мы будем говорить в параграфе 3.4. Подробнее остановимся на перечисленных операциях.

Команда movs - перенос последовательности данных из одной области в другую. Используется с суффиксами b (байт), w (слово), l (двойное слово), q (8 байтов). Соответственно для переноса байтов, слов, двойных слов и 8 -байтовую цепочку. Адрес исходных данных определяется содержимым регистра rsi (source), адрес, куда переносятся данные, лежит в регистре rdi (target). После переноса регистры rsi и rdi изменяются соответственно на 1, 2, 4 или 8. Направление изменения определяется значением флага DF регистра флагов (см. Параграф 3.3). Если флаг равен нулю, то над регистрами rsi и rdi осуществляется инкрементирование, если флаг установлен (равен 1), то содержимое регистров уменьшается на единицу. Сбросить флаг можно командой cld, установить std. При этом выполняется еще одна важная операция — содержимое регистра rcx уменьшается на 1. При использовании префикса повторения, итерации прекращаются если содержимое rcx становится равным нулю.

Рассмотрим фрагмент

mov $lng1, %rsi # откуда
mov $lng, %rdi # куда
mov $8000, %rcx # длина строки
cld # установили нужное направление переноса
repe movsb # выполнили операцию

Данный фрагмент кода осуществляет побайтовой копирование данных из одной области памяти в другую. Префикс repe не только повторяет команду, но уменьшает на каждом шаге содержимое регистра rcx. Повторение прекращается при сбрасывании флага ZF в 0 или, когда содержимое rcx становится равным нулю. Для данной команды флаг обнуляется только при обнулении rcx.

Команда lods, используется с суффиксами b, w, l, q - команда переносит байт (слово, двойное слово, 8-байтовую цепочку) из памяти, на которую указывает регистр rsi. После переноса содержимое rsi изменяется в соответствии со значением флага DF: если флаг равен 0, то осуществляется инкремент (на байт, слово, двойное слово или на 8 байтов), если флаг равен 1, то значение rsi уменьшается. Перенос байтов из памяти осуществляется соответственно в регистры al, ax, eax или rax.

Команда stos. Эта команда является командой, обратной команде lods. Она переносит байт, слово, двойное слово или 8-байтовую величину из регистра (al, ax, eax или rax) в память, которая адресуется регистром rdi. После выполнения команды осуществляется инкремент или декремент регистра rdi.

Команда cmps, команда сравнивает в зависимости от суффикса (b, w, l, q) два байта, два слова, два двойных слова или две 8-байтовых величины, расположенных по адресам, расположенным в регистрах rsi и rdi. Результат сравнения также влияет на флаги, как выполнение команды cmp или команды sub. После выполнение команды cmps содержимое регистров rsi и rdi инкрементируется или декрементируется в зависимости от значения флага DF и суффикса команды. Команда очень легко может быть приспособлена для сравнения двух областей данных. Рассмотрим один из вариантов такого сравнения.

cld # сбросить флаг
mov$lng1, %rsi # указатель на первую байтовую строку
mov$lng, %rdi # указатель на вторую байтовую строку
mov$80, %rcx # количество байтов в строках
repe cmpsb # осуществляем сравнение
jnzno_eq # переходим если строки не равны

Данный фрагмент сравнивает две цепочки длиной 80 байтов. При этом выход из процесса сравнения происходит в двух случаях: когда цепочки заканчиваются и при очередном сравнении байтов, если байты оказываются разными. Если цепочки не совпадают, то происходит переход на метку no_eq.

Команда scas – в зависимости от суффикса (b, w, l, q) сравнивает содержимое al, ax, eax или rax с байтом, словом, двойным словом, 8-байтовой величиной, адресуемой регистром rdi. Команда scas, как и команда cmps воздействует на соответствующие флаги. После выполнения команды в зависимости от значения флага DF изменяется содержимое регистра rdi.

Описанные выше команды хороши тем, что одновременно уменьшают объем кода и увеличивают его производительность.

В листинге 22 представлена функция, которая осуществляет копирование числового массива по указанному адресу. Функция на входе получает три параметра: указатель на массив — куда копируем, указатель на массив — откуда копируем, количество элементов массива (элементов, а не байтов). Предполагается, что массивы состоят из 8-байтовых чисел. Кроме того, функция возвращает указатель на результирующий массив.

<--Глава 3. Параграф 3.1 -->Глава 3. Параграф 3.3.

Заходите на мой канал Old Programmer, ставьте лайки, пишите комментарии.

Фрагмент программы l22.s
Фрагмент программы l22.s
Фрагмент программы l22.s