Assembly armv7-m裸机ldr/str符号存储器

Assembly armv7-m裸机ldr/str符号存储器,assembly,arm,armv7,Assembly,Arm,Armv7,所以我知道手臂上的ldr/str问题数不胜数。也许这是另一个转折(不太可能),或者我只是错过了一些东西(更有可能) 这是裸机,我想在内存中加载/存储一些变量。因为我坚持要给它起个名字。我天真地会写: .section .bss var: .word 0 .section .text str r0, var (具有自定义链接器脚本,可将.bss放入ram,将.text放入flash) 当然,这不起作用,因为指令是32位的,并且只有一些较小的立即数。我要说的是闪存中的指令,它是0x800000

所以我知道手臂上的ldr/str问题数不胜数。也许这是另一个转折(不太可能),或者我只是错过了一些东西(更有可能)

这是裸机,我想在内存中加载/存储一些变量。因为我坚持要给它起个名字。我天真地会写:

.section .bss
var: .word 0

.section .text
str r0, var 
(具有自定义链接器脚本,可将.bss放入ram,将.text放入flash)

当然,这不起作用,因为指令是32位的,并且只有一些较小的立即数。我要说的是闪存中的指令,它是0x8000000+x,变量要存储在内存中,在0x20000000+y的某个地方

我知道很多解决这个问题的方法:

  • 将变量地址存储在常量中(
    varaddr:.word 0x2001234;ldr r1,[pc,#varaddr];str r0,[r1]
  • 在寄存器中加载ram基址并对其进行相对寻址(
    ldr r1,#0x20000000;str r0,[r1,#varoffset]
  • 通过算术构造地址(
    movr1,#0x2000000;加上r1,#offset/orr/movw/movt something
  • 当然还有很多
这些变体中的每一个都可以工作,但这两个变体都不允许我使用我真正想要使用的标签


那么我在这里错过了什么呢。我对链接器脚本和标签的想法是假的吗?是否有我没有看到的汇编程序功能?完全不同的是?

在静态存储中使用变量符号名的一种方法是为变量定义一个结构。这允许您将结构的基址加载到寄存器中,然后使用相对于基址的符号名访问结构成员。例如,您可以执行以下操作:

        .struct 0          @ start a new structure
foo:    .skip 4            @ length of foo
bar:    .skip 4            @ length of bar
baz:    .skip 4            @ length of baz
len:                       @ total length of the structure

        .section .bss      @ switch to the BSS (uninitialised data) section
        .balign 4          @ align to 4 bytes
variables:
        .space len         @ reserve space for your variables

        .section .text     @ switch to the text (code) section

        ...
        ldr r0, =variables @ load r0 with the base address of your variables
        ldr r1, [r0+#foo]  @ access foo
        str r2, [r0+#bar]  @ access bar
        ldr r3, [r0+#baz]  @ access baz
这几乎是最接近静态存储中变量的符号名。如果变量在堆栈上,可以使用类似的方法,使用帧指针(或堆栈指针)作为基址。
.struct
的操作数是结构的基址,您可以为其选择任意值

至于
movw
movt
。在某些微体系结构上,它们比
ldr…,=…
提供了微小的性能优势,因为它们不需要将数据回迁到文本部分。据我所知,这对armv7-m目标没有影响;另外,
movw
movt
与使用
=
操作数的
ldr
相比,会额外消耗两个字节。因此,我建议您坚持使用
ldr
=
操作数。
movw
movt
的用法如下:

        movw r0, :lower16:foo  @ load lower 16 bit of foo's address into r0
        movt r0, :upper16:foo  @ or higher 16 bit of foo's address into r0
movw
清除高位16位时,必须按此特定顺序发出这两个命令。前缀
:lower16:
:upper16:
仅参考符号地址的下16位和上16位选择适当的重新定位类型。您可以制作一个宏,使其更易于键入:

        .macro addr reg, sym
        movw \reg, :lower16:\sym
        movt \reg, :upper16:\sym
        .endm
这让你可以写作

        addr r0, foo

要生成上述
movw
movt
对。

使用
ldr r1,=var
获取地址,然后使用该地址。或者,用C编写同样的程序,看看编译器做了什么,然后再做同样的事情。是的,当您实际修改变量时,将无法使用标签名称。如果你想要一个符号名,我建议为寄存器编号创建一个宏。ARMv7可以在寄存器中用
movw
(宽立即数)+
movk
或类似的东西构造任意32位值(例如地址)。在为某些ARM CPU进行优化时,编译器有时会使用它,而不是附近常量池中的PC相对负载。@Peter:我的第三个要点就是这个意思。@fuz:编译器执行我在第一个要点中的操作。它将ram地址存储在闪存中并使用它<代码>ldr r1,=var相同。这是最好的方法吗?你可以做的一件事是把所有变量放在一个结构中,然后用结构的基址加载一个寄存器。然后,您可以使用一个符号作为从结构开始的偏移量来象征性地访问所有变量。看起来很不错。您对这些指令有参考/教程/手册/更好的文档/阅读建议吗?“我发现的东西很薄。”谢因托德读了这篇文章。这就是我对薄的意思。你的例子告诉我的远不止这些:@Scheintod我使用的正是那个引用,仅此而已
.struct
允许您方便地定义绝对符号(如使用
.eq
),而无需手动计算地址。老实说,那个网站上的例子有点误导,但我也没有找到一个好的。似乎很少有人使用这个指令。除此之外,学习如何操作的最好技巧是用C编写代码,将其编译成汇编并检查输出。也有ABI文档,但它们通常很长,很难理解。