И опять Old Programmer продолжает публиковать книгу о программирование на языке ассемблера в операционной системе Linux. Сегодня говорим об адресации x86-64.
Параграф 3.1.
Команды процессора архитектуры x86-64. Адресация памяти
Во многих командах процессора может присутствовать операнд из памяти. При обращении к памяти важны два аспекта: размер операнда и его адрес. О размере операндов мы подробно писали в параграфе 2.2. Размеры операндов определяется по умолчанию, если это однозначно ясно из команды. Например add %ax, %bx - сложение двух операндов. Очевидно, что речь о сложении двух двухбайтовых чисел. В тех случаях, когда это важно для читаемости программы, используются суффиксы: b — байт, w — слово, l - двойное слово (32-битовое число), q — 64-битовое число. Например, movl %eax, num выполняет копирование 4-х байтового числа, находящегося в регистре eax, в область памяти, адрес которой определяется меткой num. Конечно, мы можем написать и так: movl %eax, num и для ассемблера все понятно, поскольку есть 32-битовый регистр eax. Мы просто лишний раз подчеркиваем, что операция выполняется над 32-битовым числом. Но есть ситуации, когда суффиксы должны однозначно показать ассемблеру, над каким операндом нужновыполнять действие. Например dec num для ассемблера неприемлемая запись, поскольку не понятно над операндом какого размерадолжна выполняться операция декремента. Мы должны непременно уточнить, например так decl num, если речь идет о декременте 4-х байтового числа. Этим ассемблер GAS отличается в положительную сторону от некоторых других ассемблеров. Синтаксис GAS вынуждает программиста непосредственно в тексте программы показывать размер операнда.
Перейдем теперь к вопросу адресации, т.е. способам указания на ту или иную область памяти.
Вместо того, чтобы долго объяснять разные виды адресации, мы представляем программу, которая бы все эти виды адресации демонстрирует. Эта программа из листинга 20. В ней представлено 6 способов адресации. С помощью каждой из них код символа перебрасывается из одной ячейки памяти в другую, а потом выводится на консоль. Для чистоты эксперимента после каждого вывода последняя ячейка памяти чистится (функция cle). Вывод символа осуществляется функцией pr. Соответственно, правильность выполненных действий подтверждается выводом на консоль шести букв 'A'. В программе каждый блок с адресаций отделяется комментарием. Ниже остановимся на каждом блоке.
1-й способ. Прямая адресация.
Доступ к памяти происходит непосредственно с помощью адреса (метки) области памяти. В примере это msg. Т.е. mov msg, %al помещает содержимое однобайтовой ячейки памяти с адресом msg в регистр al. При чем адресацию можно указывать со смещением, например так mov msg+1, %al.
2-й способ. Косвенная адресация.
Для доступа к памяти предварительно получить адрес ячейки. Адрес получаем так: mov $msg, %rbx и далее mov (%rbx), %al - содержимое ячейки, адрес которой находится в %rbx помещается в регистр al.
3-й способ. Косвенная адресация.
При использовании регистра, для хранения адреса можно явно указывать смещение: mov 8(%rbx), %al - адрес получается путем сложения содержимого регистра rbx и числа 8.
4-й способ. Косвенная адресация.
Похоже на 3-й способ, но смещение хранится в другом регистре. Т.е. mov (%rbx,%rax), %al - результирующий адрес получается путем сложения содержимого регистров rbx и rax. Иногда можно встретить такой термин: адресация по базе со сдвигом.
5-й способ. Косвенная адресация.
Данный вариант получается путем комбинации 3-го и 4-го вариантов: mov 4(%rbx,%rax), %al - результирующий адрес ячейки получается сложением содержимого регистров rax, rbx и числа 4.
6-й способ. Косвенная адресация.
Не дает ничего нового в самой адресации. В нем показан другой способ получения адреса с использованием команды lea: lea msg(%rip), %rbx, что, в сущности, эквивалентно команде mov $msg, %rbx.
Компилируем пример из листинга 20 обычными и много апробированными нами командами
as --64 l20.s -o l20.o
ld -s l20.o -o l20
Приведенные примеры адресации, однако еще не исчерпывают все возможности системы x86-64. Речь идет об адресации с масштабированием.
Обращаемся теперь к листингу 21. В листинге представлена функция sum_ar. Функция получает на входе два параметра: адрес начала числового массива и количество элементов в нем. Функция возвращает сумму элементов массива. add (%rdi, %rbx, 8), %rax - вот на эту команду обращаем особое внимание. К содержимому rax прибавляется содержимое 8-байтового числа, адрес которого формируется следующим образом: к содержимому rdi (начало массива) прибавляется содержимое rbx (значение индекса) умноженное на 8 (масштабирование). Вот это очень важная возможность процессора x86-64 помогает легко обрабатывать числовые массивы. В данном случае мы получаем сумму элементов массива, которая как и полагается возвращается через регистр rax.
<--Глава 2. Параграф 2.12 --> Глава 3. Параграф 3.2
Заходите на мой канал Old Programmer, ставьте лайки, пишите комментарии.
#программирование #программисты #assembler #ассемблер #языки программирования