В прошлых темах черехз регистры в функции передавались параметры, которые представляли непосредственнр сами значения, то есть это была так называемая передача параметров по значению. Но также можно передавать адреса этих значений. Получив в функции адрес, необходимо сначала обратиться по этому адресу, чтобы получить хранящееся по этому адресу значение. А потом собственно и производить все необходимые операции с полученным значением. Рассмотрим, как в функцию можно передать адрес на примере функции обмена значениями:
.equ a, 0 .equ b, 8 .global _start _start: sub sp, sp, #16 // выделяем в стеке 16 байт для двух 8-байтных локальных переменных mov x0, 5 str x0, [sp, a] // сохраняем первую переменную - a (a= 5) mov x0, 101 str x0, [sp, b] // сохраняем вторую переменную - b (b= 101) add x0, sp, a // в Х0 адрес первой локальной переменной - переменной a add x1, sp, b // в Х1 адрес второй локальной переменной - переменной b bl swap // вызываем функцию swap для обмена значениями ldr x0, [sp, b] // проверяем значение переменной b add sp, sp, #16 // восстанавливаем стек mov x8, #93 // устанавливаем функцию Linux для выхода из программы svc 0 // Вызываем функцию Linux swap: ldr x2, [x0] // загружаем данные по адресу из X0 в X2 ldr x3, [x1] // загружаем данные по адресу из Х1 в Х3 str x2, [x1] // сохраняем данные из Х0 по адресу в Х1 str x3, [x0] // сохраняем данные из X1 по адресу в Х0 ret
Здесь в стек загружаются два значения - две локальных переменных, которые условно назовем a и b. Для переменной a в стеке применяется смещение 0, а для переменной b - смещение в 8 байт.
mov x0, 5 str x0, [sp, a] // сохраняем первую переменную - a (a= 5) mov x0, 101 str x0, [sp, b] // сохраняем вторую переменную - b (b= 101)
Для большей наглядности вместо конкретных смещений применяются константы a и b, которые при компиляции заменяются на 0 и 8 соответственно.
Затем в регистры X0 и X1 помещаем параметры для функции swap:
add x0, sp, a // в Х0 адрес первой локальной переменной - переменной a add x1, sp, b // в Х1 адрес второй локальной переменной - переменной b
В итоге в регистре X0 будет значение SP+a=SP+0=SP
, то есть тот адрес, который хранится в SP и по которому располагается переменная a. А в регистр Х1 помещаем адрес SP+b=SP+8
,
то есть смещение относительно значения SP на 8 байт - тот адрес, по которому располагается переменная b. Таким образом, регистры Х0 и Х1 хранят не сами значения, а адреса этих значений.
Далее вызываем функцию swap:
В функции swap загружаем собственно значения по переданным адресам в регистры Х2 и Х3:
ldr x2, [x0] // загружаем данные по адресу из X0 в X2 ldr x3, [x1] // загружаем данные по адресу из Х1 в Х3
Для простоты и меньшего количества кода здесь данные загружаются и хранятся в регистрах, хотя для этой цели также можно использовать стек.
Затем загруженные данные сохраняются по противоположным адресам:
str x2, [x1] // сохраняем данные из Х0 по адресу в Х1 str x3, [x0] // сохраняем данные из X1 по адресу в Х0
То есть регистр Х2 хранит значение из адреса в Х0 и сохраняется в адрес из регистра Х1. А регистр Х3 хранит значение по адресу из Х1 и сохраняет это значение в адрес из регистра Х0. Таким образом произойдет обмен значениями.