Функция %hi(...)
умнее, чем выглядит. Он не просто возвращает верхние 20 бит данного аргумента.
%hi
предполагает использование в инструкции lui
, за которой вскоре последует другая инструкция, такая как add %lo(...)
, которая завершит 32-битную загрузку регистра, предоставив младшие 12 бит.
Во время выполнения команды add
эти младшие 12 битов будут расширяться знаком для получения 32-битного значения, которое затем добавляется к исходному содержимому регистра. Когда верхний бит младшего 12 равен 0, этот шаг расширения знака не влияет на существующие старшие 20 битов регистра. Однако, когда старший бит младшего 12 равен «1», расширение знака приводит к вычитанию единицы из существующего значения старших 20 битов регистра.
В этом случае функция %hi(0x0001ff00)
видит, что крайний левый бит младшего 12 равен «1». Поэтому он ожидает вычитание, которое будет выполнено следующим add %lo(0x0001ff00)
и записывает 0x00020
в верхние 20 битов регистра. Это 0x00020
будет преобразовано в желаемое 0001f
ожидаемым расширением знака add
.
Если вы хотите, чтобы ваша программа генерировала одну и ту же инструкцию для всех трех операторов, задайте %hi
аргумент, младшие 12 бит которого имеют 0 в крайнем левом положении. Что-то вроде lui %hi(0x0001f700)
сделает это.