В чем разница между = 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
    

    Пример с утверждениями.