ARM体系结构及接口技术介绍(二)数据操作指令、跳转指令-二、汇编指令

Cotex-v7架构

<opcode>{cond}{s} Rd,Rn,oprand2_shifter
	|	   |   |  |  |      |->第二个操作数:普通寄存器/立即数/经过移位操作的寄存器
	|	   |   |  |  |->第一个操作寄存器
	|	   |   |  |->目标寄存器
	|	   |   |-> 状态位,可有可无,指令码后加s时,指令的执行结果影响CPSR的NZCV位
	|	   |->条件码,可有可无,实现汇编之类的有条件的执行
	|->指令码,指令的名字
ARM汇编指令条件码

在这里插入图片描述

  • 注:
    1. <opcode>{cond}{s}连到一起写,如,movs
    1. Rd,Rn,oprand2_shifter 使用英文都会隔开
    1. 每条汇编指令单独占用1行
    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地址必须为一个立即数
上一篇:QT调节屏幕亮度


下一篇:【js面试题】深入理解浏览器对象模型(BOM)