Нередко функции выполняют некоторые вычисления и генерируют некоторый результат этих вычислений. Вполне вероятно, что мы захотим передать этот результат во внешний код. Во многих языках программирования высокого уровня есть механизм возвращения результата функции - обычно для этого применяется оператор return. В ассемблере такого механизма нет. Наиболее удобным местом для возврата результатов функции в архитектуре x86-64 являются регистры.
Как правило, результат функции помещается в регистр RAX (или его подрегистры EAX, AX, AL), хотя в реальности, если мы сами пишем всю программу на ассемблере, мы можем использовать для этой цели любой регистр общего назначения. Однако на регистр RAX завязано много соглашений. Так, именно в RAX большинство языков высокого уровня помещают результат функции. А согласно интерфейсам System ABI и Microsoft Windows ABI прицелочисленный результат помещается в регистр RAX.
Если нужно вернуть результат, размер которого превышает 64 бита (например, 128 бит или 256 бит) и соответственно не помещается в RAX или в другой 64-разрядный регистр общего назначения, то можно разделить результат на части и вернуть эти части в двух или более регистрах. Часто можно увидеть, как функции возвращают 128-битные значения в паре регистров RDX:RAX. .
Если надо вернуть большой объект, например, массив из 1000 элементов, то, очевидно, регистры не подходят. И в этом случае можно вместо значения возвратить его адрес (который занимает 8 байт).
Простейший пример возвращения результата в программе на Linux:
global _start section .text _start: ; устанавливаем параметры для функции sum mov rdi, 5 mov rsi, 6 call sum ; после вызова в RAX - результат сложения mov rdi, rax ; помещаем результат в RDI mov rax, 60 syscall ; Функция sum выполняет сложение двух чисел ; RDI - первое число ; RSI - второе число ; RAX - результат сложения sum: mov rax, rdi add rax, rsi ret
Здесь функция sum принимает через регистры RDI и RSI два параметра - два числа, складывает их и помещает результат в регистр RAX. После вызова функции sum мы можем использовать полученный результат в регистре RAX каким-нибудь образом, например, здесь копируем его в регистр RDI.
Аналогичная программа для Windows:
global _start section .text _start: ; устанавливаем параметры для функции sum mov rcx, 5 mov rdx, 6 call sum ; после вызова в RAX - результат сложения ret ; Функция sum выполняет сложение двух чисел ; RCX - первое число ; RDX - второе число ; RAX - результат сложения sum: mov rax, rcx add rax, rdx ret
Другой пример найдем в программе на Linux сумму чисел массива и поместим результат в RAX:
global _start section .data nums dq 10, 20, 30, 15, 15 count equ ($-nums)/numSize ; количество элементов numSize equ 8 ; размер каждого элемента section .text _start: ; устанавливаем параметры для функции sum mov rdi, nums ; в RDI адрес массива mov rsi, count ; в RSI - количество элементов массива call sum ; после вызова в RAX - результат сложения mov rdi, rax ; помещаем результат в RDI mov rax, 60 syscall ; Функция sum выполняет сложение чисел массива ; RDI - адрес массива ; RSI - размер массива ; RAX - результат сложения sum: xor rax, rax ; обнуляем RAX для хранения результата jmp while_condition ; переходим к проверке условия while_begin: dec rsi ; уменьшаем счетчик количества элементов массива add rax, [rdi + rsi * numSize] ; складываем значение по адресу [rdi + rsi*numSize] while_condition: cmp rsi, 0 ; сравниваем количество обработанных элементов jne while_begin ; если не все элементы обработали, переходим обратно ret
Здесь функция sum принимает адрес массива и количество элементов в массиве. В цикле проходит пор всем элементам массива, используя косвенную адресацию и счетчик элементов - регистр RDI и помещает в регистр RAX сумму элементов массива.
Аналогичный пример для Windows:
global _start section .data nums dq 10, 20, 30, 15, 15 count equ ($-nums)/numSize ; количество элементов numSize equ 8 ; размер каждого элемента section .text _start: ; устанавливаем параметры для функции sum mov rcx, nums ; в RCX адрес массива mov rdx, count ; в RDX - количество элементов массива call sum ; после вызова в RAX - результат сложения ret ; Функция sum выполняет сложение чисел массива ; RCX - адрес массива ; RDX - размер массива ; RAX - результат сложения sum: xor rax, rax ; обнуляем RAX для хранения результата jmp while_condition ; переходим к проверке условия while_begin: dec rdx ; уменьшаем счетчик количества элементов массива add rax, [rcx + rdx * numSize] ; складываем значение по адресу [rcx + rdx*numSize] while_condition: cmp rdx, 0 ; сравниваем количество обработанных элементов jne while_begin ; если не все элементы обработали, переходим обратно ret