ARM伪指令
什么是伪指令
- 为了编程方便定义的伪指令,并不是ARM指令集中的指令
- 编译时会将这些伪指令等效为一条或多条机器指令
4条伪指令
- ADR R0, LOOP; 将标号地址放到R0:[0,1024]、[0, 4096]
- ADRL:中等范围的地址读取伪指令[0,64k]
- LDR:大范围的地址读取伪指令[0, 4G],常用来加载外部设备的寄存器地址,参数带有=号:LDR R0, =0x22e00c00
- NOP:空操作伪指令,常用来延时:MOV R0, R0
LDR、MOV、LDR伪指令的区别
指令格式
- LDR伪指令的指令格式为:
- LDR R0,=0x22e00c00; 有=表示伪指令,常数0x22e00c00-->R0
- LDR R0,=loop; 将标号loop所表示的内存地址-->R0
- LDR指令通常用于寄存器间接寻址:
- LDR R0,[R1]; 将R1值作为内存地址,取该内存地址内容-->R0
- LDR R0,loop; 将标号loop表示的内存地址处的内容-->R0
- MOV通常用于寄存器间传输数据:
- MOV R0, R1; 将寄存器R1的值-->R0
- MOV R0, #20; 将立即数20-->R0
用途
- ARM是RISC结构,不能直接操作内存数据,要通过LDR/STD指令在内存和寄存器之间搬运数据
- MOV用来在寄存器之间搬运数据
- LDR伪指令常用来加载一个32位立即数或地址到指定寄存器
思考
问题思考
- 为什么不能LDR R0, 0x30008000?
- 或者有了MOV和LDR指令,为什么还要使用LDR伪指令?
分析
- 在32位ARM中,外设寄存器地址是一个32位的地址
- ARM指令通常是32位 = 操作码 + 操作数
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
0 0 1 0 0 Rd imm8
LDR伪指令
转化为MOV指令形式
- 当立即数小于8bit数值范围时
- LDR R0,=200
- MOV R0,#200
转换为LDR指令+文字池形式
- LDR R0,=0X30008000; 当前指令运行地址 = PC - 8
- LDR R0,[PC, #offset]
- ...
- DCD 0X30008000
- 在内存中定义一个字的存储单元,将32bit地址0x30008000存放在存储单元中,该存储单元通常也叫做文字池(literal pool)
- 算出该存储单元到LDR伪指令之间的偏移offset,即可用相对寻址,将这个地址送到R0寄存器
ADR伪指令
小范围地址读取伪指令
- 将基于PC相对偏移的地址值读取到寄存器中
- 相对寻址,做到代码位置无关
示例
- ADR R0, _start
- ...
- _start
-
b_start
- 采用相对寻址,ADR伪指令当前地址+标号_start与ADR伪指令的偏移
- ADR当前指令地址是已知(PC - 8)
- 偏移量:offset = _start - (PC - 8)
- 该指令被翻译成一条适合的指令:ADD R0, PC, #offset
LDR和ADR比较
相同点
- 都是ARM伪指令,加载一个地址到指定寄存器
不同点
- LDR伪指令通过被翻译为LDR或MOV指令,而ADR伪指令通常被翻译为ADD或SUB指令
- 用途上,LDR主要用来操作外设寄存器。
- ADR主要是通过相对寻址,生成位置无关的代码,只要各标号相对位置不变,就可以做到位置无关
- 实现上,LDR使用绝对地址,而ADR使用相对地址
- 地址范围:LDR为[0,4G],但ADR要求标号必须在同一段中,地址偏移范围:字节对齐[0,1020],未对齐[0,4096]