ARM64 (AArch64) Assembler
// ============================================================================
// Работа со строками на ARM64 Assembler (Linux)
// ============================================================================
// Компиляция: as - o string_ops.o string_ops.s && ld - o string_ops string_ops.o
// Запуск: ./string_ops
// ============================================================================
. equ SYS_WRITE , 64
. equ SYS_EXIT , 93
. equ STDOUT , 1
. section .data
sample: .asciz "Программирование на Bash - это увлекательно!"
sample_len = . - sample - 1 // длина без нулевого байта
msg_orig: .asciz "Исходная строка: "
msg_orig_len = . - msg_orig - 1
msg_first10: .asciz "Первые 10 символов: "
msg_first10_len = . - msg_first10 - 1
msg_last5: .asciz "Последние 5 символов: "
msg_last5_len = . - msg_last5 - 1
msg_second: .asciz "Каждый второй символ: "
msg_second_len = . - msg_second - 1
msg_reverse: .asciz "Строка в обратном порядке: "
msg_reverse_len = . - msg_reverse - 1
newline: .asciz "\n"
// Буфер для обратной строки
.balign 16
reverse_buf: .skip 128
. section .text
. global _start
// ============================================================================
// Макрос для вывода строки
// ============================================================================
.macro print_str msg_ptr , msg_len
mov x0 , STDOUT
ldr x1 , =\msg_ptr
mov x2 , \msg_len
mov x8 , SYS_WRITE
svc # 0
.endm
.macro print_ptr ptr , len
mov x0 , STDOUT
mov x1 , \ptr
mov x2 , \len
mov x8 , SYS_WRITE
svc # 0
.endm
.macro print_newline
mov x0 , STDOUT
ldr x1 , =newline
mov x2 , # 1
mov x8 , SYS_WRITE
svc # 0
.endm
// ============================================================================
// Точка входа
// ============================================================================
_start:
// Сохраняем длину строки в регистре
ldr x19 , =sample
mov x20 , #sample_len
// -------------------------------------------------
// Исходная строка
// -------------------------------------------------
print_str msg_orig , msg_orig_len
print_ptr x19 , x20
print_newline
// -------------------------------------------------
// Первые 10 символов
// -------------------------------------------------
print_str msg_first10 , msg_first10_len
mov x0 , STDOUT
mov x1 , x19 // указатель на начало строки
mov x2 , # 10 // 10 байт
mov x8 , SYS_WRITE
svc # 0
print_newline
// -------------------------------------------------
// Последние 5 символов
// -------------------------------------------------
print_str msg_last5 , msg_last5_len
mov x0 , STDOUT
add x1 , x19 , x20 // x1 = указатель на конец строки
sub x1 , x1 , # 5 // смещаемся на 5 байт назад
mov x2 , # 5
mov x8 , SYS_WRITE
svc # 0
print_newline
// -------------------------------------------------
// Каждый второй символ
// -------------------------------------------------
print_str msg_second , msg_second_len
bl print_every_second
print_newline
// -------------------------------------------------
// Строка в обратном порядке
// -------------------------------------------------
print_str msg_reverse , msg_reverse_len
bl reverse_string
print_newline
// -------------------------------------------------
// Выход
// -------------------------------------------------
mov x0 , # 0
mov x8 , SYS_EXIT
svc # 0
// ============================================================================
// Вывод каждого второго символа (байта)
// ============================================================================
// Вход: x19 = указатель на строку , x20 = длина
// ============================================================================
print_every_second:
stp x29 , x30 , [ sp , # - 16 ] !
mov x29 , sp
mov x1 , x19 // указатель на текущий символ
mov x2 , # 0 // счётчик
.loop_second:
cmp x2 , x20
b.ge .done_second
// Выводим символ по адресу x1
mov x0 , STDOUT
mov x8 , SYS_WRITE
svc # 0
add x1 , x1 , # 2 // пропускаем 1 символ
add x2 , x2 , # 2
b .loop_second
.done_second:
ldp x29 , x30 , [ sp ], # 16
ret
// ============================================================================
// Разворот строки и вывод
// ============================================================================
// Вход: x19 = указатель на строку , x20 = длина
// ============================================================================
reverse_string:
stp x29 , x30 , [ sp , # - 16 ] !
mov x29 , sp
stp x19 , x20 , [ sp , # - 16 ] !
ldr x21 , =reverse_buf // буфер для обратной строки
add x22 , x19 , x20 // указатель на конец строки
sub x22 , x22 , # 1 // последний символ (без нулевого)
mov x23 , # 0 // индекс в буфере
.loop_reverse:
cmp x22 , x19
blt .done_reverse
// Копируем символ из строки в буфер
ldrb w0 , [ x22 ] // загружаем байт
strb w0 , [ x21 , x23 ] // сохраняем в буфер
sub x22 , x22 , # 1 // двигаемся назад по строке
add x23 , x23 , # 1 // двигаемся вперёд по буферу
b .loop_reverse
.done_reverse:
// Выводим обратную строку
mov x0 , STDOUT
mov x1 , x21
mov x2 , x23
mov x8 , SYS_WRITE
svc # 0
ldp x19 , x20 , [ sp ], # 16
ldp x29 , x30 , [ sp ], # 16
ret
Версия с комментариями на русском
// ============================================================================
// РАБОТА СО СТРОКАМИ НА ARM64 ASSEMBLER
// ============================================================================
// Архитектура: ARMv8 - A (AArch64)
// ОС: Linux
// ============================================================================
. equ SYS_WRITE , 64 // номер системного вызова write
. equ SYS_EXIT , 93 // номер системного вызова exit
. equ STDOUT , 1 // файловый дескриптор stdout
// ============================================================================
// СЕКЦИЯ ДАННЫХ
// ============================================================================
. section .data
// Основная строка (UTF - 8 , кириллица = 2 байта на символ)
sample: .asciz "Программирование на Bash - это увлекательно!"
sample_len = . - sample - 1
// Сообщения
msg_orig: .asciz "Исходная строка: "
msg_orig_len = . - msg_orig - 1
msg_first10: .asciz "Первые 10 символов: "
msg_first10_len = . - msg_first10 - 1
msg_last5: .asciz "Последние 5 символов: "
msg_last5_len = . - msg_last5 - 1
msg_second: .asciz "Каждый второй символ: "
msg_second_len = . - msg_second - 1
msg_reverse: .asciz "Строка в обратном порядке: "
msg_reverse_len = . - msg_reverse - 1
newline: .byte 10 // символ перевода строки
// Выравнивание буфера по 16 байт для оптимизации
.balign 16
reverse_buf: .skip 128 // буфер для обратной строки
// ============================================================================
// СЕКЦИЯ КОДА
// ============================================================================
. section .text
. global _start
_start:
// Инициализация
ldr x19 , =sample // x19 = адрес строки
mov x20 , #sample_len // x20 = длина строки
// ====== ИСХОДНАЯ СТРОКА ======
bl print_label_orig
mov x0 , STDOUT
mov x1 , x19
mov x2 , x20
mov x8 , SYS_WRITE
svc # 0
bl print_newline
// ====== ПЕРВЫЕ 10 СИМВОЛОВ ======
bl print_label_first10
mov x0 , STDOUT
mov x1 , x19
mov x2 , # 10
mov x8 , SYS_WRITE
svc # 0
bl print_newline
// ====== ПОСЛЕДНИЕ 5 СИМВОЛОВ ======
bl print_label_last5
mov x0 , STDOUT
add x1 , x19 , x20
sub x1 , x1 , # 5
mov x2 , # 5
mov x8 , SYS_WRITE
svc # 0
bl print_newline
// ====== КАЖДЫЙ ВТОРОЙ СИМВОЛ ======
bl print_label_second
bl every_second_char
bl print_newline
// ====== СТРОКА В ОБРАТНОМ ПОРЯДКЕ ======
bl print_label_reverse
bl reverse_and_print
bl print_newline
// ====== ЗАВЕРШЕНИЕ ПРОГРАММЫ ======
mov x0 , # 0 // код возврата 0
mov x8 , SYS_EXIT
svc # 0
// ============================================================================
// ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ
// ============================================================================
// Вывод метки "Исходная строка: "
print_label_orig:
stp x29 , x30 , [ sp , # - 16 ] !
mov x0 , STDOUT
ldr x1 , =msg_orig
mov x2 , msg_orig_len
mov x8 , SYS_WRITE
svc # 0
ldp x29 , x30 , [ sp ], # 16
ret
print_label_first10:
stp x29 , x30 , [ sp , # - 16 ] !
mov x0 , STDOUT
ldr x1 , =msg_first10
mov x2 , msg_first10_len
mov x8 , SYS_WRITE
svc # 0
ldp x29 , x30 , [ sp ], # 16
ret
print_label_last5:
stp x29 , x30 , [ sp , # - 16 ] !
mov x0 , STDOUT
ldr x1 , =msg_last5
mov x2 , msg_last5_len
mov x8 , SYS_WRITE
svc # 0
ldp x29 , x30 , [ sp ], # 16
ret
print_label_second:
stp x29 , x30 , [ sp , # - 16 ] !
mov x0 , STDOUT
ldr x1 , =msg_second
mov x2 , msg_second_len
mov x8 , SYS_WRITE
svc # 0
ldp x29 , x30 , [ sp ], # 16
ret
print_label_reverse:
stp x29 , x30 , [ sp , # - 16 ] !
mov x0 , STDOUT
ldr x1 , =msg_reverse
mov x2 , msg_reverse_len
mov x8 , SYS_WRITE
svc # 0
ldp x29 , x30 , [ sp ], # 16
ret
// Вывод перевода строки
print_newline:
stp x29 , x30 , [ sp , # - 16 ] !
mov x0 , STDOUT
ldr x1 , =newline
mov x2 , # 1
mov x8 , SYS_WRITE
svc # 0
ldp x29 , x30 , [ sp ], # 16
ret
// ============================================================================
// КАЖДЫЙ ВТОРОЙ СИМВОЛ
// ============================================================================
every_second_char:
stp x29 , x30 , [ sp , # - 16 ] !
mov x1 , x19 // указатель на строку
mov x2 , # 0 // индекс
.loop_second:
cmp x2 , x20 // индекс >= длина?
b.ge .end_second
// Выводим один символ (байт)
mov x0 , STDOUT
mov x8 , SYS_WRITE
svc # 0 // write( 1 , x1 , 1 )
add x1 , x1 , # 2 // пропускаем следующий символ
add x2 , x2 , # 2
b .loop_second
.end_second:
ldp x29 , x30 , [ sp ], # 16
ret
// ============================================================================
// РАЗВОРОТ СТРОКИ
// ============================================================================
reverse_and_print:
stp x29 , x30 , [ sp , # - 16 ] !
ldr x21 , =reverse_buf // буфер результата
add x22 , x19 , x20
sub x22 , x22 , # 1 // последний байт строки
mov x23 , # 0 // индекс в буфере
.loop_rev:
cmp x22 , x19
blt .print_rev
ldrb w24 , [ x22 ] // читаем байт с конца
strb w24 , [ x21 , x23 ] // пишем в буфер
sub x22 , x22 , # 1
add x23 , x23 , # 1
b .loop_rev
.print_rev:
mov x0 , STDOUT
mov x1 , x21
mov x2 , x23
mov x8 , SYS_WRITE
svc # 0
ldp x29 , x30 , [ sp ], # 16
ret
Справочник по регистрам ARM64
Регистр
Назначение
Сохранение
x0-x7
Аргументы функций и возвращаемые значения
Нет
x8
Номер системного вызова
Нет
x9-x15
Временные регистры
Нет
x16-x17
Внутрипроцедурные вызовы (IP)
Нет
x18
Регистр платформы
—
x19-x28
Callee-saved регистры
Да
x29
Указатель кадра стека (FP)
Да
x30
Адрес возврата (LR)
Да
sp
Указатель стека
—
# Ассемблирование
as -o string_ops.o string_ops.s
# Линковка
ld -o string_ops string_ops.o
# Запуск
./string_ops
Особенности UTF-8 в ассемблере
Символ
UTF-8 байты
Примечание
'П'
D0 9F
2 байта
'р'
D1 80
2 байта
'!'
21
1 байт
' '
20
1 байт
Важно: Bash работает с байтами, а не Unicode-символами. Поэтому "первые 10 символов" означает 10 байт, что равно 5 кириллическим буквам. Ассемблерный код делает то же самое для совместимости.