В качестве второго операнда инструкция принимает 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.
Следует отметить, что первый вызов инструкции 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 (Move Not) работает аналогично MOV
за тем исключением, что она инвертирует все разряды числа
при загрузке в регистр, то есть 1 изменяет на 0, а 0 - на 1. Эта инструкция ценна тем, что может применяться для получения отрицательного значения числа, ведь отрицательное значение
по сути предполагает инвертирование разрядов и прибавление 1 к инвертированному числу. Что производительнее, чем использовать инструкцию умножения.
Кроме того, инструкция MOVN
за счет дополнительного знакового бита (17 бит вместо 16 бит для mov) позволяет использовать значение в два раза больше.
При этом если ассемблер видит, что указанное число превышает 16 бит, допустимых для инструкции MOV
,
то он автоматически преобразует инструкцию в MOVN
. Например:
MOV W1, #0xFFFFFFFE // (-2)
Здесь инструкция MOV преобразуется в MOVN, так как переданное значение