Загрузить переменную из пула букв в ассемблере с ldr

#c #assembly #arm #gnu

#c #сборка #arm #gnu

Вопрос:

Я хочу загрузить переменные из литерального пула. Пул литералов находится в конце файла asm.

 literal_pool_label:
.WORD POOL_EVENT_CHANNEL_2_START_REG_ADDR
.WORD POOL_EVENT_CHANNEL_4_START_REG_ADDR
  

В коде, который я написал:

 adr r12, literal_pool_label
ldr r5, [r12, #0]

ldr r5, [r12, #4]
  

В модуле C определение переменной выглядит следующим образом:

 const uint32_t POOL_EVENT_CHANNEL_2_START_REG_ADDR = 0x4100e030;
const uint32_t POOL_EVENT_CHANNEL_4_START_REG_ADDR = 0x4100e040;
  

Если я написал в пуле следующим образом, значение будет правильным.

 .WORD 0x4100e030 // POOL_EVENT_CHANNEL_2_START_REG_ADDR
.WORD 0x4100e040 // POOL_EVENT_CHANNEL_4_START_REG_ADDR
  

Что я должен сделать, чтобы получить значение из переменной с помощью одной инструкции?

Комментарии:

1. Эта конструкция помещает адрес константы в пул. Вы не можете ссылаться на значение, если оно определено таким образом. Чтобы получить значение, вам нужен дополнительный ldr r5, [r5] .

2. С помощью какой конструкции я могу загрузить значение за один шаг?

3. Вы можете переместить определение константы в файл asm, например, .equ POOL_EVENT_CHANNEL_2_START_REG, 0x4100e030 вместе с .globl POOL_EVENT_CHANNEL_2_START_REG и позволить стороне C использовать это как, например, #define POOL_EVENT_CHANNEL_2_START_REG_ADDR amp;POOL_EVENT_CHANNEL_2_START_REG , а стороне asm просто сделать ldr r5, =POOL_EVENT_CHANNEL_2_START_REG

4. Если эти элементы данных находятся в .data, то вы не можете сделать это с помощью одной инструкции, требуется одна, чтобы указать на значение, а затем одна, чтобы прочитать его. Если данные находятся в .text, то нет проблем с использованием относительной адресации ПК.

5. у arm нет режима двойной косвенной адресации.

Ответ №1:

У ARM нет режима двойной косвенной адресации, такого как, скажем, pdp11 или другой, о котором я не могу придумать (msp430?).

Ваш другой вопрос основан на cortex-m, и, возможно, именно поэтому вы пытаетесь сделать это в оперативной памяти, вы прилагаете к этому много усилий, не объясняя, зачем вам нужна эта функциональность, и если сохранение одной инструкции в проекте приведет к некоторому успеху или неудаче. Если это связано с производительностью, то есть другие способы обойти это, и, скорее всего, код, одна инструкция, не улучшит производительность заметным образом. (на самом деле это может усугубить ситуацию, зависит от обстоятельств).

итак

 ldr r0,hello
ldr r1,world_addr
ldr r2,[r1]
b .

hello: .word 0x12345678
world_addr: .word world_data
.data
world_data: .word 0x87654321

Disassembly of section .text:

00001000 <hello-0x10>:
    1000:   e59f0008    ldr r0, [pc, #8]    ; 1010 <hello>
    1004:   e59f1008    ldr r1, [pc, #8]    ; 1014 <world_addr>
    1008:   e5912000    ldr r2, [r1]
    100c:   eafffffe    b   100c <hello-0x4>

00001010 <hello>:
    1010:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000

00001014 <world_addr>:
    1014:   00002000    andeq   r2, r0, r0

Disassembly of section .data:

00002000 <__data_start>:
    2000:   87654321    strbhi  r4, [r5, -r1, lsr #6]!
  

Можно легко попросить сгенерировать адресацию относительно ПК в разделе. За пределами раздела обычно вы загружаете адрес относительно ПК, затем второй уровень косвенности заключается в доступе к самому элементу.

Если вы попробуете это, ассемблер gnu будет жаловаться.

 ldr r0,hello
ldr r1,world_addr
ldr r2,[r1]
ldr r3,world_data
b .

hello: .word 0x12345678
world_addr: .word world_data
.data
world_data: .word 0x87654321
  

Теперь да, это технически возможно, потому что существует режим адресации относительно ПК, который, если вы можете получить доступ к переменной таким образом, вы можете сделать это в одной инструкции, и это вопрос указания ассемблеру.

     1000:   e59f0008    ldr r0, [pc, #8]    ; 1010 <hello>
    ...
    1010:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000
  

with a much further reach.

 .cpu cortex-m4
.thumb
ldr r0,hello
b .
.space 0x20000000
.align
world_data: .word 0x87654321
  

But the assembler complains.

Since you are writing assembly language then you as well as I have the arm architectural reference manual open on your screen, you can see that the thumb encoding allows for a 5 bit offset and the thumb2 encoding a 12 bit offset (both signed) best case.

Определяет непосредственное смещение, добавляемое к значению или вычитаемое из него для формирования адреса. Допустимые значения кратны 4 в диапазоне 0-124 для кодирования T1, кратны 4 в диапазоне 0-1020 для кодирования T2, любое значение в диапазоне 0-4095 для кодирования T3 и любое значение в диапазоне 0-255 для кодирования T4. Для синтаксиса адресации со смещением <imm> может быть опущен, что означает смещение 0.

Код cortex-m ниже 0x20000000, а оперативная память выше 0x20000000 до некоторого предела, например 0x40000000.

Это больше, чем вы можете достичь в одной инструкции из flash, если бы вы могли заставить ассемблер и компоновщик работать вместе, чтобы сделать это (как они могут, например, с инструкциями перехода).

Итак, решение ram, вы отметили gnu, поэтому предполагаете, что gnu binutils.

 .cpu cortex-m4
.thumb
ldr r0,hello
b .
.align
hello: .word 0x87654321
.data
.word 0x12345

MEMORY
{
    rom : ORIGIN = 0x00000000, LENGTH = 0x1000
    ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
    .text   : { *(.text*)   } > rom
    .rodata : { *(.rodata*) } > rom
    .bss    : { *(.bss*)    } > ram
    .data : { *(.rodata*) } > ram AT > rom
}

Disassembly of section .text:

00000000 <hello-0x4>:
   0:   4800        ldr r0, [pc, #0]    ; (4 <hello>)
   2:   e7fe        b.n 2 <hello-0x2>

00000004 <hello>:
   4:   87654321

Disassembly of section .data:

20000000 <.data>:
20000000:   00012345

00000000  00 48 fe e7 21 43 65 87  45 23 01 00              |.H..!Ce.E#..|
0000000c

S00A0000736F2E7372656338
S30D000000000048FEE72143658775
S309000000084523010085
S70500000000FA
  

так что с .данные мы бы увидели что-то подобное, и вы можете видеть, что элементы .data находятся во флэш-памяти, затем вы добавляете метки / переменные в сценарий компоновщика, а затем используете эти метки / переменные для копирования инициализированных во время компиляции элементов на основе ОЗУ в ОЗУ перед выполнением основной программы (при условии, что C, но в вашем случае вы можете делать это в любое время, если это чисто программа сборки).

 .cpu cortex-m4
.thumb
.thumb_func
fun:
    ldr r0,something
    bx lr
.align
something: .word 0x11223344

MEMORY
{
    rom : ORIGIN = 0x00000000, LENGTH = 0x1000
    ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
    .text   : { so.o(.text*)  } > rom
    .rodata : { *(.rodata*) } > rom
    .bss    : { *(.bss*)    } > ram
    .data : { *(.rodata*) } > ram AT > rom
    .fun  : { fun.o(.text*) } > ram AT > rom
}

Disassembly of section .text:

00000000 <hello-0x4>:
   0:   4800        ldr r0, [pc, #0]    ; (4 <hello>)
   2:   e7fe        b.n 2 <hello-0x2>

00000004 <hello>:
   4:   87654321    strbhi  r4, [r5, -r1, lsr #6]!

Disassembly of section .fun:

20000004 <fun>:
20000004:   46c0        nop         ; (mov r8, r8)
20000006:   4770        bx  lr

(srec)
S00A0000736F2E7372656338
S30D000000000048FEE72143658775
S309000000084523010085
S3090000000CC04670472D
S70500000000FA
  

И как с .данные вы можете добавить переменные компоновщика и использовать их для копирования функции из флэш-памяти в ОЗУ перед ее выполнением, в идеале в начальной загрузке, но если это чисто asm-программа без C, то где угодно, прежде чем вы ее используете.

(нет, это не действующая программа cortex-m, просто демонстрирующая инструменты)

Вы, вероятно, захотите начать со скрипта компоновщика, связанного с используемой вами библиотекой C, поскольку именно там скрипт компоновщика и bootstrap обычно находятся в готовой настройке (часть SDK, набор инструментов, библиотека C и т.д.), Дублируют .data и начинают оттуда, но понимают, что вы столкнетесь с проблемами, которые я решил в описанном выше стиле.

 .text   : { *(.text*)   } > ram
  

захочет взять весь .text, в том числе из других разделов / файлов. И, по крайней мере, в моем случае возникла путаница с тем, что появилось первым (когда вы хотите управлять векторной таблицей и т.д., Возможно, вам придется проделать больше работы, а не просто поместить ее в .text и упорядочить файлы в командной строке). Таким образом, как и в случае с любым сценарием компоновки и начальной загрузкой, вам придется выполнять итерации, сборку и разборку, пока вы не получите это.

Если причиной этого является производительность (или предполагаемая производительность), то вы можете, как кто-то упоминал, исчерпать оперативную память, но вы можете запустить весь проект в оперативной памяти, и это облегчает жизнь, если у вас есть место, и вы должны получить наилучшую производительность выборки (хотя для cortex-m7 это может быть не лучшим).

 .cpu cortex-m4
.thumb
ldr r0,hello
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
b .
.align
hello: .word 0x87654321

MEMORY
{
    ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
    .text   : { *(.text*)   } > ram
    .rodata : { *(.rodata*) } > ram
    .data   : { *(.data*)   } > ram
    .bss    : { *(.bss*)    } > ram
}


Disassembly of section .text:

20000000 <hello-0x1c>:
20000000:   4806        ldr r0, [pc, #24]   ; (2000001c <hello>)
20000002:   46c0        nop         ; (mov r8, r8)
20000004:   46c0        nop         ; (mov r8, r8)
20000006:   46c0        nop         ; (mov r8, r8)
20000008:   46c0        nop         ; (mov r8, r8)
2000000a:   46c0        nop         ; (mov r8, r8)
2000000c:   46c0        nop         ; (mov r8, r8)
2000000e:   46c0        nop         ; (mov r8, r8)
20000010:   46c0        nop         ; (mov r8, r8)
20000012:   46c0        nop         ; (mov r8, r8)
20000014:   46c0        nop         ; (mov r8, r8)
20000016:   46c0        nop         ; (mov r8, r8)
20000018:   46c0        nop         ; (mov r8, r8)
2000001a:   e7fe        b.n 2000001a <hello-0x2>

2000001c <hello>:
2000001c:   87654321    strbhi  r4, [r5, -r1, lsr #6]!
  

A few lines of C code can take the output and generate this

 copybase: .word 0x20000000
copysize: .word 0x00000008
copydata:
.word 0x46C04806 @0x20000000
.word 0x46C046C0 @0x20000004
.word 0x46C046C0 @0x20000008
.word 0x46C046C0 @0x2000000C
.word 0x46C046C0 @0x20000010
.word 0x46C046C0 @0x20000014
.word 0xE7FE46C0 @0x20000018
.word 0x87654321 @0x2000001C
  

and within that same adhoc C program or outside you can then do this:

 .cpu cortex-m4
.thumb
.syntax unified
.globl _start
_start:
.word 0x20001000
.word reset
.thumb_func
reset:
    ldr r0,copybase
    ldr r1,copysize
    ldr r2,=copydata
    .align
copy_loop:
    ldr r3,[r0],#4
    str r3,[r2],#4
    subs r1,#1
    bne copy_loop
    ldr r0,copybase
    orr r0,#1
    bx r0

    copybase: .word 0x20000000
    copysize: .word 0x00000008
    copydata:
    .word 0x46C04806 @0x20000000
    .word 0x46C046C0 @0x20000004
    .word 0x46C046C0 @0x20000008
    .word 0x46C046C0 @0x2000000C
    .word 0x46C046C0 @0x20000010
    .word 0x46C046C0 @0x20000014
    .word 0xE7FE46C0 @0x20000018
    .word 0x87654321 @0x2000001C
  

и для этого не обязательно даже нужен скрипт компоновщика — Ttext= 0 должно быть достаточно, но если нет, то

 MEMORY
{
    rom : ORIGIN = 0x00000000, LENGTH = 0x1000
}
SECTIONS
{
    .text   : { *(.text*)   } > rom
}
  

в gnu linker действительно есть ошибка в отношении таких вещей, поэтому скрипт компоновщика более чистый.

В обоих случаях сценарии компоновщика становятся такими же тривиальными, как и начальная загрузка для C, если вы создадите ее правильно, ваша начальная загрузка может быть:

 reset:
   bl main
   b .
  

для программы, основанной на оперативной памяти.

Ваша производительность выборки обычно составляет один такт для sram, где flash работает медленно и может ухудшиться, поскольку вы используете более высокую тактовую частоту процессора во многих микроконтроллерах.

И вы получаете свой ldr за один цикл.

Если на armv6-m, а не на armv7-m, то это простая настройка … копирование / переход, очевидно, не будет работать как есть.

Обратите внимание, что если бы вам нужен был только ldr, вы могли бы просто сделать это

     ldr r0,something
...
something: .word 0x11223344
  

и то, и другое будет размещено в файле .text и идеально подходит для ПК в зависимости от набора команд и расстояния. Ничего из вышеперечисленного не требовалось. Если вы хотите прочитать-записать это значение откуда-то еще, и этот код просто прочитает его, тогда да, данные должны быть в оперативной памяти.