Загрузка констант в регистры

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

MOVK

В качестве второго операнда инструкция принимает 16-битное значение. Такое значение еще называется immediate или непосредственный операнд. Для числа 1 требуется не больше 16 бит, поэтому проблем не возникнет. Но что если мы захотим поместить в регистр гораздо большее значение, например, 400000000, памятуя о том, что регистры у нас могут содержать 64-битные значения:

mov X0, #400000000

При компиляции через GCC GNU мы получим ошибку:

Error: immediate cannot be moved by a single instruction

Чтобы загрузить значения гораздо большей разрядности нам может помочь инструкция MOVK.

Инструкция MOVK (move keep) представляет разновидность инструкции MOV и позволяет загрузить все 64 бита регистра. В реальности она загружает 16-битные операнды в одну из 4 частей регистра, не оказывая никакого влияния на другие 48 бит. Собственно поэтому в названии инструкции используется буква K - сокращение от "keep" - хранить, так как инструкция сохраняет другие 16-битные части регистра. Например, допустим, мы хотим загрузить в регистр X2 64-битное шестнадцатеричное значение 0x0004000300020001. В этом случае мы могли бы написать:

.global _start
_start: 
    mov x0, #0x0001             // в младшие 16 бит с нулевого бита загружаем число 0x0001
    movk x0, #0x0002, LSL #16   // начиная с 16 бита - число 0х0002
    movk x0, #0x0003, LSL #32   // начиная с 32 бита - число 0х0003
    movk x0, #0x0004, LSL #48   // начиная с 48 бита - число 0х0004

    mov x8, #93             // завершаем программу
    svc 0

Команда MOVK для заполнения нужной части регистра выполняет сдвиг на определенное количество бит, которое должно быть кратно 16.

MOVZ

Следует отметить, что первый вызов инструкции MOV в предыдущем листинге кода в реальности эквивалентен инструкции MOVZ, которая аналогична MOVK за тем исключением, что заполняет оставшиеся 48 байт нулями. Используя эту инструкцию с выражениями сдвига, можно заполнить только какую-то часть регистра, а остальные биты обнулить:

.global _start
_start: 
    mov x0, #0x0123             // в младшие 16 бит с нулевого бита загружаем число 0x0001
    movz x0, #0x0002, LSL #16   // начиная с 16 бита - число 0х0002

    mov x8, #93             // завершаем программу
    svc 0

В данном случае в регистре X0 будет число 0x00020000, поскольку инструкция movz поместить число 0х0002 начиная с 16 бита, но обнулит ранее добавленное число 0x0123 в первых 16 битах.

MOVN

Инструкция MOVN (Move Not) работает аналогично MOV за тем исключением, что она инвертирует все разряды числа при загрузке в регистр, то есть 1 изменяет на 0, а 0 - на 1. Эта инструкция ценна тем, что может применяться для получения отрицательного значения числа, ведь отрицательное значение по сути предполагает инвертирование разрядов и прибавление 1 к инвертированному числу. Что производительнее, чем использовать инструкцию умножения.

Кроме того, инструкция MOVN за счет дополнительного знакового бита (17 бит вместо 16 бит для mov) позволяет использовать значение в два раза больше. При этом если ассемблер видит, что указанное число превышает 16 бит, допустимых для инструкции MOV, то он автоматически преобразует инструкцию в MOVN. Например:

 MOV W1, #0xFFFFFFFE // (-2)

Здесь инструкция MOV преобразуется в MOVN, так как переданное значение

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