ARM汇编指令:
指令基本格式: <opcode> {<cond>{s} <Rd>, <Rn>{,<operand2>}}
其中<>为必选项, {}为可选项
<opcode>: 助记符, 如LDR, STR等
cond: 执行条件, 如BEQ, BNE等
助记符后跟S, 影响CPSR的值, 如SUB加S后为SUBS
Rd: 目标寄存器
Rn: 第一个操作数的寄存器
operand2: 第二个操作数
存储器访问指令:
加载/存储体系结构:
ARM是RISC结构, 无法直接对存储数据进行操作, 首先要加载到寄存器才能处理
对寄存器访问使用加载指令LDR和存储指令STR实现
可以实现半字, 字, 无符号/有符号字节操作
基本命令:
LDR R1, [R0]: 将R0值作为内存地址, 取出该地址的值保存到R1
STR R1, [R0]: 将R1的值保存到以R0中的值为地址的存储单元中
LDRB/STRB: 读取一字节
LDM/STM: 数据块传输指令, 实现一组寄存器和连续内存单元之间的数据传输
加载(LDM)或存储(STM)一组寄存器
支持堆栈操作
SWP R1, R1, [R0]: 将R1与R0指向的存储单元内容进行互换
SWP R1, R2, [R0]: 将[R0]读取到R1, 将R2写入到[R0]存储单元中
数据传输指令:
MOV R1, #1: 将立即数1传送到寄存器R1中
MOV R1, R0: 将R0中的值传送到R1寄存器中
MOV PC, LR: 子程序返回
MVN R1, #0xff: 将0xff取反后赋值给R1
算术逻辑运算指令:
加减指令:
ADD R1, R1, #1: R1 = R1 + 1
ADC R1, R1, #1: R1 = R1 + 1 + C(CPSR中的进位/借位标志位), 用于64位数据的相加
SUB R1, R1, R2: R1 = R1 - R2
SBC R1, R1, R2: R1 = R1 - R2 - C, 用于64位数据的相减
与或非指令:
AND R0, R0, #3: 将R0与立即数3(00000011)相与, 即保留R0中的0, 1位, 其余位置零(两数相与, 见零则零, 全一则一)
ORR R0, R0, #3: 置位R0中的0, 1位, 其余位不变(两数相或, 见一则一, 全零则零)
异或指令:
EOR R0, R0, #3: 反转R0中的0, 1位, 其余位不变(两数异或, 同零异一, 与零异或为原数, 与一异或为取反)
清除指令:
BIC R0, R0, #3: R0与取反的立即数3(11111100)相与, 即置零R0中的0, 1位, 其余位不变
乘法指令:
MUL R2, R1, R0: R2 = R1 * R0
带累加的乘法:
MLA R3, R2, R1, R0: R3 = R2 * R1 +R0
比较指令:
影响CPSR状态寄存器的N(31: 负数标志位), Z(30: 零标志位), C(29: 进位标志位), V(28: 溢出标志位)
CMP R1, #10: R1 - 10
CMP R1, R2: R1 - R2
CMN R0, #1: R0 - (-1), 与负数比较
TST R1, #1: R1 & 1, 位测试, 测试0位为0或1
TEQ R1, R0: R1 ^ R0(异或), 测试两数是否相等
条件执行:
几乎所有ARM指令都可以根据CPSR的标志位, 有条件地执行
|-------------------------------------------------|
| 条件码 | CPSR标志位 | 含义 |
|-------------------------------------------------|
| EQ | Z = 1 | 相等 |
|---------|----------------|----------------------|
| NE | Z = 0 | 不相等 |
|---------|----------------|----------------------|
| CS/HS | C = 1 | 无符号数大于或等于 |
|---------|----------------|----------------------|
| CC/LO | C = 0 | 无符号数小于 |
|---------|----------------|----------------------|
| MI | N置位 | 负数 |
|---------|----------------|----------------------|
| PL | N清零 | 整数或零 |
|---------|----------------|----------------------|
| VS | V置位 | 溢出 |
|---------|----------------|----------------------|
| VC | V清零 | 未溢出 |
|---------|----------------|----------------------|
| HI | C置位, Z清零 | 无符号数大于 |
|---------|----------------|----------------------|
| LS | C清零, Z置位 | 无符号数小于或等于 |
|---------|----------------|----------------------|
| GE | N = V | 有符号数大于或等于 |
|---------|----------------|----------------------|
| LT | N != V | 有符号数小于 |
|---------|----------------|----------------------|
| GT | Z清零, N = V | 有符号数大于 |
|---------|----------------|----------------------|
| LE | Z置位, N != V | 有符号数小于或等于 |
|---------|----------------|----------------------|
| AL | - | 无条件执行 |
|---------|----------------|----------------------|
| NV | - | 从不执行 |
|-------------------------------------------------|
跳转指令:
B label: 跳转到label处, 跳转范围为前后32M
BL label:
带链接跳转, 先将下一条指令保存到LR, 然后再跳转
MOV PC, LR: 常用于函数调用, 然后从子程序返回
BX Rm:
带状态切换跳转, Rm保存的是跳转地址, 处理器根据Rm的0位决定切换到ARM或Thumb状态
0位状态: 0: 目标地址为ARM指令, 1: 目标地址为Thumb指令
BLX label: 带链接和状态切换跳转
伪指令:
为了编程方便定义的伪指令, 并不是ARM指令集中的指令
编译时会将这些伪指令等效为一条或多条机器指令
四条伪指令:
ADR R0, label: 将标号地址放入R0
将基于PC的相对偏移地址值存入寄存器中
相对寻址, 与代码位置无关
ADR当前指令地址为PC-8
偏移量为标号地址-(PC-8)
ADRL: 中等范围的地址读取伪指令
LDR: 大范围的地址读取伪指令, 常用来加载外部设备的寄存器地址, 参数带有"="
若等号后立即数小于8bit, 则等价于MOV
若等号后立即数大于8bit:
在内存中定义一个字的存储单元, 将立即数存放在存储单元中, 该存储单元通常叫做文字池(literal pool)
计算出该存储单元到LDR伪指令之间的偏移地址offset, 即可用相对寻址, 将这个立即数存入寄存器
NOP: 空操作伪指令, 常用来延时
LDR, MOV和LDR伪指令的区别:
指令格式:
LDR伪指令的指令格式为:
LDR R0, =0xffffffff: 有等号表示是伪指令, 将立即数0xffffffff存入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/STR指令在内存中和寄存器之间存取数据
MOV指令用于在寄存器之间存取数据
LDR伪指令用于加载一个32位立即数或地址到指定寄存器
LDR指令与ADR指令比较:
相同点:
都是ARM伪指令, 加载一个地址到指定寄存器
不同点:
LDR伪指令通常被被翻译为LDR或MOV指令, 而ADR伪指令通常被翻译为ADD所SUB指令
LDR主要用来操作外设寄存器, ADR主要通过相对寻址, 生成与位置无关的代码, 只要各标号相对位置不变, 就可以做到与位置无关
LDR使用绝对地址, ADR使用相对地址
LDR范围为4G, ADR要求标号必须在同一个段中, 偏移地址对齐时为1020, 未对齐时为4096