ARM汇编(树莓派)加载数据的两种方法对比及相关问题
ARM汇编内存加载问题(树莓派环境)
我是ARM汇编语言初学者,正在树莓派环境下编写代码,当前需实现将内存数据加载至寄存器的操作。最初编写了如下代码:
/* -- load01.s */ /* -- Data section */ .data /* Ensure variable is 4-byte aligned */ .balign 4 /* Define storage for myvar1 */ myvar1: /* Contents of myvar1 is just 4 bytes containing value '3' */ .word 3 /* Ensure variable is 4-byte aligned */ .balign 4 /* Define storage for myvar2 */ myvar2: /* Contents of myvar2 is just 4 bytes containing value '4' */ .word 4 /* -- Code section */ .text /* Ensure code is 4 byte aligned */ .balign 4 .global main main: ldr r1, addr_of_myvar1 /* r1 ← &myvar1 */ ldr r1, [r1] /* r1 ← *r1 */ ldr r2, addr_of_myvar2 /* r2 ← &myvar2 */ ldr r2, [r2] /* r2 ← *r2 */ add r0, r1, r2 /* r0 ← r1 + r2 */ bx lr /* Labels needed to access data */ addr_of_myvar1 : .word myvar1 addr_of_myvar2 : .word myvar2
后续调研中发现另一种实现方式,可直接使用=操作符将地址加载到寄存器中,代码如下:
/* -- Data section */ .data /* Ensure variable is 4-byte aligned */ .balign 4 /* Define storage for myvar1 */ myvar1: /* Contents of myvar1 is just 4 bytes containing value '3' */ .word 3 /* Ensure variable is 4-byte aligned */ .balign 4 /* Define storage for myvar2 */ myvar2: /* Contents of myvar2 is just 4 bytes containing value '4' */ .word 4 /* -- Code section */ .text /* Ensure code is 4 byte aligned */ .balign 4 .global main main: ldr r1, =myvar1 /* r1 ← &myvar1 */ ldr r1, [r1] /* r1 ← *r1 */ ldr r2, =myvar2 /* r2 ← &myvar2 */ ldr r2, [r2] /* r2 ← *r2 */ add r0, r1, r2 /* r0 ← r1 + r2 */ bx lr
现提出以下技术问题:
- 这两种加载地址的方法有何区别?
- 哪种方法是更优的选择?
- 如何修改代码以自定义指定内存地址进行数据读写?
问题解答
1. 两种加载地址方法的区别
手动定义地址标签的方式:
需要手动在代码段末尾用.word指令存储变量的地址(比如addr_of_myvar1 : .word myvar1),再通过ldr r1, addr_of_myvar1从该内存位置读取地址值到寄存器。这是纯硬件指令的间接加载,需要额外内存存储地址常量,执行时会触发一次内存访问读取地址。ldr =伪指令的方式:
这是GNU汇编器提供的语法糖,汇编器会自动处理地址加载逻辑:- 如果目标地址符合ARM
mov指令的立即数规则(8位数值循环右移偶数位),会自动替换成mov r1, #myvar1,直接用寄存器操作完成,无需内存访问; - 如果地址超出立即数范围,汇编器会在代码段附近自动生成隐藏的常量池(类似第一种方法的地址标签),再用
ldr指令加载地址。
这种写法无需手动维护地址标签,底层细节由汇编器自动处理。
- 如果目标地址符合ARM
2. 更优的选择
优先选择第二种ldr =的写法,原因如下:
- 代码更简洁,减少冗余的地址标签定义,降低手动出错概率;
- 汇编器会自动优化指令:能用
mov就不用ldr,减少不必要的内存访问,提升执行效率; - 适配性更强,不管目标地址是否在立即数范围内,都能正确生成指令,无需开发者手动判断。
仅在需要手动控制常量池位置、或有特殊内存布局需求时,才需要使用第一种手动定义地址的方式。
3. 自定义指定内存地址进行数据读写
直接将目标内存地址作为立即数加载到寄存器,再用ldr(读)或str(写)指令操作即可。示例代码如下(以树莓派GPIO基地址0x3F200000为例):
.text .balign 4 .global main main: @ 加载自定义内存地址到r0 ldr r0, =0x3F200000 @ 读取该地址的4字节数据到r1 ldr r1, [r0] @ 准备要写入的数据(比如0x12345678) mov r2, #0x12345678 @ 将r2的数据写入到目标地址 str r2, [r0] @ 返回 bx lr
注意:如果目标地址符合ARM立即数规则,可直接用mov r0, #0x3F200000替换ldr r0, =0x3F200000,执行效率更高;若地址不符合规则,用ldr =方式即可,汇编器会自动处理常量池生成。
内容的提问来源于stack exchange,提问作者Issaki
相关产品推荐
相关产品推荐

