В чем разница между = label (знак равенства) и [label] (скобки) в сборке ARMv6?
Я следую за курсом Baking Pi из Кембриджского университета, в котором простая простая операционная система построена в наборе команд ARMv6, нацеливаясь на малину Пи.
Мы использовали два способа загрузки данных в регистры с помощью команды ldr
, и теперь я понимаю, что я использую их вместе, я не совсем понимаю, что они оба делают.
Итак, я использовал такие вещи, как ldr r0,=0x20200000
, которые я действительно понимал как "читать данные, хранящиеся в ячейке памяти 0x20200000, в регистр r0.
Затем я использовал такие вещи, как:
ldr r0,[r1,#4]
Я понял, что "прочитал данные, хранящиеся в адресе памяти, указанном r1, со смещением 4 байта, в регистр r0".
Затем я сталкиваюсь с этим:
ldr r0,=pattern
ldr r0,[r0]
pattern
здесь .int
в разделе .data
(растровое изображение, представляющее последовательность состояний включения/выключения для светодиода). Я понимаю, прочитав это, что мое предыдущее понимание =foo
должно быть неправильным, иначе обе вышеприведенные инструкции будут делать то же самое.
Синтаксис =x
в основном больше похож на указатель на C, а синтаксис [x]
выглядит так, как будто память, на которую указывает x
, действительно читается?
Скажем, ptr
в нижеприведенном C есть int*
, мои комментарии, думающие об эквивалентной сборке (концептуально, а не буквально), имеют какой-то смысл?
r0 = ptr; /* equivalent to: ldr r0,=ptr */
r0 = *ptr; /* equivalent to: ldr r0,[ptr] */
r0 = *(ptr+4) /* equivalent to: ldr r0,[ptr,#4] */
Ответы
Ответ 1
ldr r0,=something
...
something:
означает, что адрес метки помещается в регистр r0. Затем ассемблер добавляет слово где-то в досягаемости инструкции ldr и заменяет его
ldr r0,[pc,#offset]
инструкция
Итак, этот ярлык
ldr r0,=0x12345678
означает нагрузку 0x12345678 на r0.
в основном инструкции с фиксированной длиной, вы не можете загрузить полный 32-битный код в регистр в одну команду, он может взять несколько инструкций, чтобы полностью загрузить регистр с 32-разрядным номером. В значительной степени зависит от числа. Например
ldr r0,=0x00010000
будет заменен на ассемблер gnu одной командой mov r0, # 0x00010000, если это инструкция ARM, для команды большого пальца, хотя она все равно может быть ldr r0, [pc, # offset]
Эти ldr rd, = вещи являются ярлыками, псевдо-инструкциями, а не реальными.
ldr rd,[rm,#offset]
ldr rd,[rm,rn]
являются реальными инструкциями и означают чтение из памяти по адресу rm + offset или rm + rn и считывают значение и помещают его в регистр rd
the = что-то больше похоже на & something в C.
unsigned int something;
unsigned int r0;
unsigned int r1;
r0 = &something;
r1 = *(unsigned int *)r0;
и в сборке
something:
.word 0
ldr r0,=something
ldr r1,[r0]
Ответ 2
Стоит отметить, что существуют следующие альтернативные подходы для тех, кто по какой-то причине хочет избежать псевдоинструкций/литерального пула:
-
adr r0, label
(v7/v8): одиночная инструкция, сохраняет полный адрес метки в r0
. Относится к метке относительной адресацией ПК, см. Также: Какова семантика инструкций ADRP и ADRL в сборке ARM? | Пример с утверждениями.
В ARMv7, однако, невозможно получить доступ к меткам в разных разделах с помощью adr
, например .data
из .text
, по-видимому, потому что нет перемещения, которое позаботится об этом. ldr =
может сделать это Если вы попробуете, GAS потерпит неудачу с:
Error: symbol .data is in a different section
Доступ к сечению, однако, возможен в ARMv8 и генерирует перемещение типа R_AARCH64_ADR_PRE
. Пример.
-
movw
и movt
(V7) + ГНУ ГАЗ #:lower
:
movw r0, #:lower16:myvar
movt r0, #:upper16:myvar
Пример с утверждениями.
movk
+ сдвиги (v8) + GNU GAS :
movz x0, #:abs_g2:label // bits 32-47, overflow check
movk x0, #:abs_g1_nc:label // bits 16-31, no overflow check
movk x0, #:abs_g0_nc:label // bits 0-15, no overflow check
Пример с утверждениями.