我正在跟随剑桥大学的Baking Pi课程,其中一个简单的操作系统是在ARMv6指令集中构建的,目标是Raspberry Pi。
到目前为止,我们一直使用两种方法通过ldr
指令将数据加载到寄存器中,现在我意识到我正在使用它们,我不完全理解它们都做了什么。
所以我使用了像ldr r0,=0x20200000
这样的东西,我实际上把它理解为“将存储在存储单元0x20200000的数据读入寄存器r0。
然后我使用了以下内容:
ldr r0,[r1,#4]
我已经理解为“读取存储在r1指向的存储器地址的数据,以4字节的偏移量进入寄存器r0”。
然后我遇到了这个:
ldr r0,=pattern
ldr r0,[r0]
pattern
这里是.int
部分中的.data
(表示LED的开/关状态序列的位图)。我在阅读本文时意识到,我之前对=foo
的理解必定是错误的,否则上述两条指令都会做同样的事情。
=x
语法基本上更像是C中的指针,而[x]
语法就好像实际读取x
指向的内存一样?
让我们说下面的C中的ptr
是int*
,我的评论是否考虑等效汇编(概念上,不是字面上的)是否有意义?
r0 = ptr; /* equivalent to: ldr r0,=ptr */
r0 = *ptr; /* equivalent to: ldr r0,[ptr] */
r0 = *(ptr+4) /* equivalent to: ldr r0,[ptr,#4] */
答案 0 :(得分:18)
ldr r0,=something
...
something:
表示将标签的地址加载到寄存器r0中。然后汇编程序在ldr指令的某个地方添加一个单词,并用
替换它ldr r0,[pc,#offset]
指令
所以这个快捷方式
ldr r0,=0x12345678
表示将0x12345678加载到r0。
大多数是固定长度的指令,你不能在一条指令中将一个完整的32位立即加载到一个寄存器中,它可能需要许多指令来完全加载一个32位数的寄存器。很大程度上取决于这个数字。例如
ldr r0,=0x00010000
将被gnu汇编程序替换为单个指令mov r0,如果是ARM指令则为#0x00010000,对于Thumb指令,可能仍然必须是ldr r0,[pc,#offset]
这些ldr rd,=事物是快捷方式,伪指令,不是真实的。
ldr rd,[rm,#offset]
ldr rd,[rm,rn]
是实数指令,意思是从地址rm + offset或rm + rn读取存储器并取值读取并将其放入寄存器rd
=某些东西更像是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]
答案 1 :(得分:0)
值得一提的是,对于出于某些原因想要避免伪指令/文字池的那些人,存在以下替代方法:
adr r0, label
(v7 / v8):单指令,将标签的完整地址存储到r0
中。通过相对PC寻址来引用标签,另请参见:What are the semantics of ADRP and ADRL instructions in ARM assembly? | Example with asserts。
但是在ARMv7中,无法使用adr
访问不同部分中的标签,例如.data
中的.text
,显然是因为没有relocation会照顾它。 ldr =
可以做到这一点。如果您尝试这样做,则GAS会失败,并显示以下信息:
Error: symbol .data is in a different section
但是在ARMv8中可以进行跨节访问,并且会生成类型R_AARCH64_ADR_PRE
的重定位。 Example。
movw
和movt
(v7)+ GNU气体#:lower
:
movw r0, #:lower16:myvar
movt r0, #:upper16:myvar
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