Для примера рассмотрим вывод содержимого регистра на консоль.
Предположим, что в регистр X4 помещается число 0x1234FEDC4F5D6E3A
, и нам надо вывести
содержимое регистра X4 на консоль:
// Программа для печати содержимого 64-битного регистра на консоль .global _start _start: MOV X4, #0x6E3A MOVK X4, #0x4F5D, LSL #16 MOVK X4, #0xFEDC, LSL #32 MOVK X4, #0x1234, LSL #48 LDR X1, =hexstr // начало строки ADD X1, X1, #17 // переходим на первый байт числа // в цикле проходим от W5 = 16 до W5=1 с приращением -1 MOV W5, #16 // 16 символов для печати loop: AND W6, W4, #0xf // накладываем маску - на число в W4 // если W6 >= 10, тогда переходим к метке letter CMP W6, #10 // узнаем, число равно 0-9 или A-F B.GE letter ADD W6, W6, #'0' // преобразуем в цифровой символ ASCII 0-9, если число меньше 10 B endif // переходим к метке endif letter: // преобразуем в символы от A до F, если число больше 10 ADD W6, W6, #('A'-10) endif: // end if STRB W6, [X1] // сохраняем один символ ascii SUB X1, X1, #1 // вычистаем из адреса в X1 единицу для перехода к следующему символу LSR X4, X4, #4 // сдвиг влево в X4 для получения следующего байта // next W5 SUBS W5, W5, #1 // уменьшаем счетчик W5 на 1 - для печати следующего символа B.NE loop // переходим к метке loop, если W5 не равно 1 // установка параметров и вызов функции Linux для вывода шестнадцатеричного числа MOV X0, #1 // 1 = StdOut - стандартный поток вывода LDR X1, =hexstr // строка для печати MOV X2, #19 // длина строки MOV X8, #64 // функция Linux для вывода в поток SVC 0 // вызываем функцию Linux // выход из программы MOV X0, #0 // 0 - код возврата MOV X8, #93 // функция Linux для выхода из программы SVC 0 // вызываем функцию Linux .data hexstr: .ascii "0x123456789ABCDEFG\n"
В данном случае выводим в выходной поток содержимое регистра X4. И вначале загружаем в него само число, которое будем выводить на консоль:
MOV X4, #0x6E3A MOVK X4, #0x4F5D, LSL #16 MOVK X4, #0xFEDC, LSL #32 MOVK X4, #0x1234, LSL #48
Поскольку будем выводить 64-битное число, то для его загрузки в регистр используем инструкции MOVK со сдвигов влево командой LSL.
Чтобы вывести число на экран, вначале поместим его в строку. И для этого определим в секции .data
строку hexstr
с произвольным содержимым - некоторым произвольным числом,
которое по длине аналогично числу из X4. И алрес этой
строки загрузим в регистр X1
LDR X1, =hexstr
Далее переходим в этой строке к байту с первым числом, с которого начнем замену на число из регистра X4:
ADD X1, X1, #17 // переходим на первый байт числа
Число из X4, которое надо напечатать, состоит из 64 бит или 16 байт, поэтому в качестве счетчика байт устанавливаем регистр W5:
MOV W5, #16 // 16 символов для печати
Чтобы вывести число на консоль, надо перевести его цифры в символы ASCII. Поэтому далее нам получаем первую шестнадцатеричную цифру (0-F) из числа, которое хранится в регистре. Для этого накладываем на число маску с помощью операции поразрядного умножения:
AND W6, W4, #0xf // накладываем маску - на число в W4
В регистре X4 хранится число 0x1234FEDC4F5D6E3A
, соответственно в 32-битном регистре хранится значение 0x4F5D6E3A
(младшие 32 бита).
Выражение AND W6, W4, #0xf
поразрядно умножает значение из регистра W4 на #0xf
и посещает результат в регистр W6
0x4F5D6E3A * #0xf = 0b0100 1111 0101 1101 0110 1110 0011 1010 * 0b0000 0000 0000 0000 0000 0000 0000 1111 = 0b0000 0000 0000 0000 0000 0000 0000 1010
То есть в регистре W6 будет храниться число 0xA (10 в десятичной системе)
Полученное значение сравниваем с числом 10
CMP W6, #10 // узнаем, число равно 0-9 или A-F B.GE letter
Если значение из регистра W6 больше и равно 10, то это шестнадцатеричная цифры/символы A - F, тогда переходим к метке letter
и прибавляем к данному числу
расстояние между числовыми кодами в таблице ASCII #('A'-10)
и таким образом получаем числовой код символа:
letter: // преобразуем в символы от A до F, если число больше 10 ADD W6, W6, #('A'-10)
Если значение из регистра W6 меньше 10, то это стандартные цифры 0-9. Тогда для получения числового кода символа прибавляем к числу числовой код символа "0" из таблицы ASCII и
переходим к метке endif
:
ADD W6, W6, #'0' // преобразуем в цифровой символ ASCII 0-9, если число меньше 10 B endif // переходим к метке endif
После сохранения в регистр W6 числового кода символа в обоих случаях (будь то символ A-F или число 0-9) переходим к метке endif
, где сохраняем текущий символ по адресу,
который хранится в регистре X1 (по сути в строку hexstr):
endif: STRB W6, [X1] // сохраняем один символ ascii
Затем вычитаем из адреса в X1 один байт и переходим в строке hexstr к предыдущему символу, что в следующий раз сохранить символ на эту позицию:
SUB X1, X1, #1
Далее нам надо перейти к обработке следующей цифры из числа в регистре X4. Для этого выполняем сдвиг вправо на 4 разраяда (одна шестнадцатеричная цифра)
LSR X4, X4, #4 // сдвиг влево в X4 для получения следующего байта
То есть в начале регистр X4 содержит число 0x1234FEDC4F5D6E3A
. После сдвига вправо на 4 разряда оно равно 0x01234FEDC4F5D6E3
.
Таким образом, в следующий раз мы сможем выше описанным способом новую цифру, которая располагается с краю справа.
В конце смотрим, сколько цифр из числа осталось обработать. Если это число НЕ равно 1, то переходим в начала и начинаем обрабатывать новую цифра
SUBS W5, W5, #1 // уменьшаем счетчик W5 на 1 - для печати следующего символа B.NE loop // переходим к метке loop, если W5 не равно 1
Остальные действия - выводим измененную строку hexstr на консоль и выходим из программы.
Печать 32-битного регистра на консоль будет во многом аналогично, только теперь нам надо вывести 8 символов числа:
// Программа для печати содержимого 32-битного регистра на консоль .global _start _start: MOV W4, #0x6E3A MOVK W4, #0x4F5D, LSL #16 //MOVK X4, #0xFEDC, LSL #32 //MOVK X4, #0x1234, LSL #48 LDR X1, =hexstr // начало строки ADD X1, X1, #9 // переходим на первый байт числа MOV W5, #8 // 8 символов для печати loop: AND W6, W4, #0xf // накладываем маску - на число в W4 // если W6 >= 10, тогда переходим к метке letter CMP W6, #10 // узнаем, число равно 0-9 или A-F B.GE letter ADD W6, W6, #'0' // преобразуем в цифровой символ ASCII 0-9, если число меньше 10 B endif // переходим к метке endif letter: // преобразуем в символы от A до F, если число больше 10 ADD W6, W6, #('A'-10) endif: // end if STRB W6, [X1] // сохраняем один символ ascii SUB X1, X1, #1 // вычистаем из адреса в X1 единицу для перехода к следующему символу LSR W4, W4, #4 // сдвиг влево в X4 для получения следующего байта // next W5 SUBS W5, W5, #1 // уменьшаем счетчик W5 на 1 - для печати следующего символа B.NE loop // переходим к метке loop, если W5 не равно 1 // установка параметров и вызов функции Linux для вывода шестнадцатеричного числа MOV X0, #1 // 1 = StdOut - стандартный поток вывода LDR X1, =hexstr // строка для печати MOV X2, #11 // длина строки MOV X8, #64 // функция Linux для вывода в поток SVC 0 // вызываем функцию Linux // выход из программы MOV X0, #0 // 0 - код возврата MOV X8, #93 // функция Linux для выхода из программы SVC 0 // вызываем функцию Linux .data hexstr: .ascii "0x12345678\n"