Практика. Печать регистра на консоль

Последнее обновление: 28.12.2022

Для примера рассмотрим вывод содержимого регистра на консоль.

Печать значения 64-битного регистра на консоль

Предположим, что в регистр 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-битного регистра

Печать 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"

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850