Если программа небольшая, то можно весь код определить в одном файле. Однако если программа содержит много кода, тогда расположение кода в одном файле может усложить разработку приложения. Кроме того, возможно отдельные части приложения - отдельные функции затем потребуется применить в других приложениях. В этом случае гораздо рациональнее вынесение функций в отдельные файлы.
Например, определим файл copy.s, который будет содержать определение функции copy
для копирования одной строки в другую:
// Функция copy, которая копирует символы из одной строки в другую // Параметры функции // X0 - адрес входящей строки // X1 - адрес исходящей строки // // Результат функции // X0 - длина строки .global copy // чтобы функция была видна извне copy: MOV X4, X1 // сохраняем адрес начала строки, чтобы потом вычислить ее длину // в цикле получаем все байты, пока не дойдем до нулевого байта loop: LDRB W5, [X0], #1 // загружаем из X0 один байт - один символ в W5 и увеличиваем адрес в X0 на 1 байт CMP W5, #0 // сравниваем с нулевым байтом B.EQ endloop // если нулевой байт, переход к метке endloop STRB W5, [X1], #1 // если символы равны, заменяем байт по адресу X1 и увеличиваем адрес в X1 на 1 байт B loop // переход обратно к метке loop для копирования следующего символа endloop: SUB X0, X1, X4 // помещаем в регистр X0 длину строки - X0 = X1 - X4 RET // выход из функции
В функции проходим по каждому байту из строки, адрес которой хранится в регистре Х0 и копируем его в строку, адрес которой хранится в регистре Х1. Когда дойдем до нулевого байта, то выходим из цикла, в регистр Х0 помещаем длину строки и завершаем выполнение функции.
Допустим, у нас есть главный файл программы, который называется main.s и который использует копирование с помощью вышеопределенной функции
copy
:
.global _start _start: LDR X0, =input // строка, их которой надо скопировать символы LDR X1, =output // строка, в которую надо скопировать символы BL copy // вызов функции copy // печать строки на консоль MOV X2, X0 // в регистр X2 передаем результ функции copy - длину строки из регистра X0 MOV X0, #1 // 1 = StdOut - стандартный поток вывода LDR X1, =output // загружаем адрес выводимой строки MOV X8, #64 // функция Linux для вывода в поток SVC 0 // вызываем функцию Linux // выход из программы MOV X0, 0 // код возврата - 0 MOV X8, #93 // устанавливаем функцию Linux для выхода из программы SVC 0 // Вызываем функцию Linux .data input: .asciz "Hello METANIT.COM!\n" output: .fill 256, 1, 0
При компиляции ассемблеру в этом случае передаются все компилируемые файлы, то есть main.s и copy.s:
aarch64-none-elf-as main.s copy.s -o main.o
Причем собираются они в один файл main.o. Линкеру передается только один файл main.o:
aarch64-none-elf-ld main.o -o main