Cotex-v7架构
<opcode>{cond}{s} Rd,Rn,oprand2_shifter
| | | | | |->第二个操作数:普通寄存器/立即数/经过移位操作的寄存器
| | | | |->第一个操作寄存器
| | | |->目标寄存器
| | |-> 状态位,可有可无,指令码后加s时,指令的执行结果影响CPSR的NZCV位
| |->条件码,可有可无,实现汇编之类的有条件的执行
|->指令码,指令的名字
ARM汇编指令条件码
- 注:
-
-
<opcode>{cond}{s}
连到一起写,如,movs
-
-
- Rd,Rn,oprand2_shifter 使用英文都会隔开
-
- 每条汇编指令单独占用1行
-
- 在汇编代码中不严格区分大小写
(一)数据操作相关指令
1. 数据搬移指令
(1)MOV
<opcode>{cond}{s} Rd,oprand2_shifter
@ 将oprand2_shifter的值赋值给Rd目标寄存器
(2)MVN
<opcode>{cond}{s} Rd,oprand2_shifter
@ 将oprand2_shifter的值按位取反之后赋值给Rd目标寄存器
- 注:
- 数据在内存中是以补码形式存储的
- 即 -0xFF
- 原码:0x1000 0000 0000 0000 0000 0000 1111 1111
- 反码:0x1111 1111 1111 1111 1111 1111 0000 0000
- 补码:0x1111 1111 1111 1111 1111 1111 0000 0001
- MVN是按位取反后赋值,因此MVN r4,#-0xFF 执行后,r4最后的结果是0xFE
(3)立即数
如何判断某个数是否为立即数?
首先需要根据需要判断的数找到一个0~255之间的数,然后将找到的0~255之间的数进行循环右移偶数位,如果可以得到需要判断的这个数,就说明该数是一个立即数。
如果一个数取反以后是一个立即数,说明它是一个有效数,有效数也可以写到#
后面
(4)伪指令
指令码:ldr
指令格式:ldr Rd,=number(0x0~0xFFFFFFFF)
2. 移位操作指令
lsl
:逻辑左移/无符号数左移
高位移出,低位补0
lsr
:逻辑右移/无符号数右移
低位移出,高位补0
asr
:算术右移/有符号数右移
低位移出,高位补符号位
ror
:循环右移
低位移出补到高位
指令格式:<opcode>{cond}{s} Rd,Rn,oprand2_shifter
- 注:
- oprand2_shifter取值范围是#0x0 - #0x31
- 在第二个操作数是一个经过移位的寄存器时
① 第二个操作数必须是一个寄存器,才可以使用lsl移位指令进行移位运算
mov r0, r0, lsl #4 @ r0 = r0 << 4
② 第二个操作数如果是一个立即数,可以使用<<(左移) >>(右移) 运算符
mov r0, #(0xFF << 4) @ r0 = 0xFF << 4
3. 位运算指令
and
:按位与运算(&)orr
:按位或运算(|)eor
:按位异或运算(^)mvn
:按位取反运算(~)bic
:按位清除指令
指令格式:<opcode>{cond}{s} Rd,Rn,oprand2_shifter
使用示例:
/* 3. 位运算指令 */
@假设有一个R0寄存器,R0寄存器中存储了一个未知的整数。
ldr r0, =0x12345678
@ 1. 将r0寄存器的第[4]个bit位清0,其他位保持不变
@ 思想:第4位与0,其他位与1
@ r0 = r0 & (~(0x1 << 4))
and r0, r0, #(~(0x1 << 4))
@ 2. 将r0寄存器的第[12]个bit位置1,其他位保持不变
@ 思想:第12位或1,其他为或0
@ r0 = r0 | (0x1 << 12)
orr r0, r0, #(0x1 << 12)
@ 3. 将r0寄存器的[24:20]bit位清0,其他位保持不变
and r0, r0, #(~(0x1F << 20))
@ 4. 将r0寄存器的[15:12]bit位置1,其他位保持不变
orr r0, r0, #(0xF << 12)
@ 5. 将r0寄存器的[11:4]bit位按位取反,其他位保持不变
@ 思想:将[11:4]位异或1,其它异或0
eor r0, r0, #(0xFF << 4)
@ 6. 将r0寄存器的第[23:16]bit位修改1101 0101, 其他位保持不变
@ 思想:先将[23:16]位全部清0,然后再将[23:16]位或0xD5
@ 6.1 先将[23:16]位全部清0
and r0, r0, #~(0xFF << 16)
@ 6.2 再将对应的位置1
orr r0, r0, #(0xD5 << 16)
@ 7. bic:按位清除指令
@ 第二个操作数的哪位为1,就将第一个操作寄存器中的值哪位清0
@ 然后将结果写回到目标寄存器中
@ 7.1 将r0寄存器的第[4]个bit位清0,其他位保持不变
bic r0, r0, #(0x1 << 4)
@ 7.2 将r0寄存器的[24:20]bit位清0,其他位保持不变
bic r0, r0, #(0x1F << 20)
4. 算术运算指令
add
:普通加法指令,不需要考虑进位的标志位(C位)
adc
:带进位的加法指令,需要考虑进位的标志位(C位)
sub
:普通减法指令
sbc
:带借位的减法指令
mul
:乘法指令,(乘法指令的第二个操作数不能是立即数,只能是寄存器)
div
:除法指令
指令格式:<opcode>{cond}{s} Rd,Rn,oprand2_shifter
使用示例:
实现一个64位的加法:
@第一个64位数
ldr r0, =0x00000004 @高32位
ldr r1, =0xfffffffe @低32位
@第二个64位数
ldr r2, =0x00000005 @高32位
ldr r3, =0x00000006 @低32位
@64位加法
@低32位无需考虑进位,但是需要置标志位s
adds r1, r1, r3
@高32位无需置标志位,但是需要考虑进位
adc r0, r0, r2
实现一个64位减法器
.globl _start
_start:
@第一个64位数
ldr r0, =0x00000004 @高32位
ldr r1, =0xfffffffe @低32位
@第二个64位数
ldr r2, =0x00000005 @高32位
ldr r3, =0x00000006 @低32位
@64位减法
@低32位无需考虑借位,但是需要置标志位s
subs r1, r3, r1
@高32位无需置标志位,但是需要考虑借位
sbc r0, r2, r0
stop:
b stop
.end
使用示例2(该示例是为了演示错误用法)
.globl _start
_start:
/*此次运算会产生进位*/
@第一个64位数
ldr r0, =0x00000004 @高32位
ldr r1, =0xfffffffe @低32位
@第二个64位数
ldr r2, =0x00000005 @高32位
ldr r3, =0x00000006 @低32位
@64位加法
@低32位无需考虑进位,但是需要置标志位s
adds r1, r1, r3
@高32位无需置标志位,但是需要考虑进位
adc r0, r0, r2
/*此次运算不会产生进位,且低位运算后不置标志位*/
/*此时不会CPSR的C位不会改变,如果以adc关心标志位的方式加,结果会产生错误*/
@第一个64位数
ldr r0, =0x00000004 @高32位
ldr r1, =0xffffff00 @低32位
@第二个64位数
ldr r2, =0x00000005 @高32位
ldr r3, =0x00000006 @低32位
@64位加法
@低32位没有置标志位s
add r1, r1, r3
@高32位考虑进位
adc r0, r0, r2
stop:
b stop
.end
5. 比较指令
指令格式
cmp Rn, oprand_shifter
| | |---> 第二个操作数,可以是立即数,可以是寄存器
| |---> 第一个操作寄存器
|---> 指令码
- 注:
- 比较用于比较两个数的大小,没有目标寄存器
- 比较指令的本质就是做减法运算,Rn - oprand_shifter
- 比较指令执行的结果最终影响的是CPSR的NZCV位,并且不需要加S
- 比较指令经常和条件码配合使用,实现汇编指令的有条件执行
使用示例
.globl _start
_start:
ldr r0, =0x04 @高32位
ldr r1, =0xfe @低32位
@比较两数大小,置标志位
cmp r0,r1
@如果r0>r1执行
subhi r0,r0,r1
@如果r0<r1执行
subcc r0,r1,r0
stop:
b stop
.end
(二)跳转指令
b
:不带返回值的跳转指令,发生跳转指令时,不保存返回地址到LR中bl
:带返回值的跳转指令,发生跳转时,自动保存返回地址到LR中
LR:链接寄存器,用于保存返回地址
指令格式
b/bl{cond} Label(标签)
Label:
/*asm code*/
- 注:
- 跳转到Label标签下的第一条指令处开始执行
- 跳转指令的本质就是修改PC寄存器指向 PC=Label
- b跳转指令不保存返回地址到LR中
- bl跳转指令自动保存返回地址到LR中
使用场合:
有去无回就用b
;有去有回用bl
跳回LR的指令需要自己写
使用示例
实现函数跳转
.globl _start
_start:
ldr r0, =0x09
ldr r1, =0x06
bl swap_func
nop @空语句
nop
b stop
@执行完成后,pc会继续加4跳转到下一条指令
@如果此处不跳转到stop,下一条指令会继续执行swap_func
swap_func:
mov r2, r0
mov r0, r1
mov r1, r2
mov pc, lr
@bl指令会自动保存要返回的位置到LR寄存器中
@但是跳转指令仍需自己完成
stop:
b stop
.end
其他跳转方式
mov pc,lr @直接操作pc寄存器的值
ldr pc,=Label @Label本质就是一个值,等价于b Label
mov pc,#Label @不建议使用,这种方式需要保证Label地址必须为一个立即数