文章目录

  • 1. 汇编文件中的主要符号
  • 1.1 汇编指令
  • 1.2 伪指令
  • 1.3 伪操作
  • 2. 汇编指令的分类
  • 3. 基本汇编指令语法格式
  • 4. 数据操作指令
  • 4.1 数据搬移指令
  • 4.1.1 指令格式
  • 4.1.2 测试代码
  • 4.1.3 立即数
  • 4.1.4 ldr伪指令
  • 4.1.5 验证PC寄存器
  • 4.2 移位操作指令
  • 4.2.1 指令格式
  • 4.2.2 测试代码
  • 4.3 位运算指令
  • 4.3.1 指令格式
  • 4.3.2 测试代码
  • 4.4 算数运算指令
  • 4.4.1 指令格式
  • 4.4.2 测试代码
  • 4.5 比较指令
  • 4.5.1 指令格式
  • 4.5.2 测试指令代码
  • 5. 跳转指令
  • 5.1 指令格式
  • 5.2 测试代码
  • 5.3 实现跳转的其他方式
  • 5.4 练习
  • 5.4.1 求两个数的最大公约数
  • 5.4.2 实现for循环,求1-100的和
  • 6. 特殊功能寄存器操作指令
  • 6.1 指令格式
  • 6.2 测试代码
  • 7. Load/Store内存读写指令
  • 7.1 单寄存器指令
  • 7.1.1 指令码
  • 7.1.2 指令格式
  • 7.1.3 测试代码
  • 7.1.4 练习
  • 7.2 多寄存器操作指令
  • 7.2.1 指令码
  • 7.2.2 指令格式
  • 7.2.3 测试代码
  • 7.3 栈操作指令
  • 7.3.1 栈的类型
  • 7.3.2 栈空间的操作方式
  • 7.3.3 指令格式
  • 7.3.4 测试代码


1. 汇编文件中的主要符号

1.1 汇编指令

汇编指令:汇编指令是一条指令,编译器可以将其编生成32位的机器码,汇编指令占用代码段的内存空间,执行汇编指令可以完成某个特定的功能。

mov r0, r1 @将r1寄存器中的内容拷贝到寄存器r0中

1.2 伪指令

伪指令:本身不是一条指令,但是编译器可以将其编译生成多条汇编指令,通过多条汇编指令最终完成一条伪指令的功能。

ldr r0, =0x12345678 @ 汇编指令只有32位,0x12345678就32位了,也不是立即数,所以指令无法存储,所以需要伪指令

1.3 伪操作

伪操作:伪操作不占用代码段的内存空间,给编译器使用,直到编译器对代码进行编译的。

.text  .end   .data   .global  .globl  .word .short  .byte  .if 0/1  .else  .endif

2. 汇编指令的分类

1. 数据操作指令
    数据搬移指令
    算数运算指令
    移位操作指令
    位运算指令
    比较指令
2. 跳转指令
3. Load/Store内存读写指令
4. 软中断指令
5. 特殊功能寄存器读写指令

3. 基本汇编指令语法格式

<opcode>{cond}{s}  Rd, Rn, oprand_shifter

<opcode> :指令码  比如mov b add  sub 
{cond} : 条件标志, 实现汇编指令的有条件执行
{s} :状态位, 指令码后边加s,表示指令的执行结果影响CPSR的NZCV的变化;指令码后边不加s, 表示指令的执行结果不影响CPSR的NZCV位

Rd : 目标寄存器,只能是一个普通的寄存器 R0-R15
Rn : 第一个操作寄存器,只能是一个普通的寄存器 R0-R15
oprand_shifter : 第二个操作数
    1> 可以是一个普通的寄存器 R0-R15
    2> 可以是一个立即数
    3> 可以是经过移位操作的寄存器


注意:
<opcode>{cond}{s} : 连到一起写
Rd, Rn, oprand_shifter  :三个之间使用英文的逗号隔开
<opcode>{cond}{s}和Rd, Rn, oprand_shifter使用空格隔开

每条汇编指令单独占用一行。
汇编指令不区分大小写:
mov r0, #0xff  <==> MOV R0, #0xFF <==> MoV R0, #0xff

arm平台lxc容器创建 arm 教程_stm32

4. 数据操作指令

4.1 数据搬移指令

4.1.1 指令格式

{cond}{s} Rd, oprand_shifter

1> 没有第一个操作寄存器
2> oprand_shifter可以是寄存器也可以是立即数

mov : 将oprand_shifter赋值给Rd寄存器
mvn : 将oprand_shifter按位取反之后赋值给Rd寄存器

4.1.2 测试代码

@ 0xFF 是一个立即数,使用立即数时前边需要加#
    @ 第二个操作数是一个立即数
	mov r0, #0xFF  @ r0 = 0xFF 
	
    @ 第二个操作数是一个寄存器
	mov r1, r0     @ r1 = r0
	
    @ mvn指令
	mvn r2, #0xFF  @ r2 = ~0xFF
	mvn r3, r2     @ r3 = ~r2

4.1.3 立即数

mov r0, #0xF    @ OK
	mov r1, #0xFF	@ OK
	@mov r2, #0xFFF  @ ERROR
	@mov r3, #0xFFFF @ ERROR
	@mov r4, #0xFFFFF @ ERROR
	mov r5, #0xFFFFFF @ OK
	mov r6, #0xFFFFFFF @ OK
	mov r7, #0xFFFFFFFF @ OK
	mov r8, #0xFF000000
	@mov r9, #0xFFF00000
	mov r10, #0x1F800000

arm平台lxc容器创建 arm 教程_arm平台lxc容器创建_02

4.1.4 ldr伪指令

格式:ldr{cond}  Rd, =0-4G之间的数    @ Rd = 0-4G之间的数

ldr r0, =0x1234567

4.1.5 验证PC寄存器

@ mov pc, #0x10
    @ 给PC寄存器赋值时需要是4的整数倍,原因是一条ARM指令占4字节的空间
    @ 如果给PC寄存器赋值不是4的整数倍,编译内部会自动的忽略数据的第[1:0]位
    @ 给PC赋值时,如果第1位和第0位为1,都当成0处理。
	mov pc, #5      @ pc = 0x5
	mov pc, #6      @ pc = 0x6
	mov pc, #7      @ pc = 0x7

4.2 移位操作指令

4.2.1 指令格式

<opcode>{cond}{s}  Rd, Rn, oprand_shifter 

Rd = Rn lsl/lsr/ror/asr oprand_shifter 

lsl : 逻辑左移/无符号数左移 
lsr :逻辑右移/无符号数右移
asr :算数右移/有符号数的右移
ror :循环右移

4.2.2 测试代码

mov r0, #0xFF
	@ 高位移出,低位补0
	lsl r1, r0, #0x4   @ r1 = r0 << 4 = 0xFF0
	
	@ 低位移出,高位补0
	lsr r2, r0, #0x4   @ r2 = r0 >> 4 = 0x0F
	
	@ 低位移出,高位补符号位,因为数据在计算机中按补码存储
	asr r3, r0, #0x4   @ r3 = r0 >> 4 = 0x0F
	
	@ 低位移出,补到高位
	ror r4, r0, #0x4   @ r4 = r0 >> 4 = 0xF000000F
	
	mov r0, #-0x7F000000
	asr r5, r0, #0x4
	
	@ 移位操作指令中的立即数的范围为0-31
	lsl r6, r0, #31
	
	@ 第二个操作为一个移位操作的寄存器
	@ 第二个操作数只能是一个寄存器经过移位指令进行移位
	mov r0, r1, lsl #4   @ r0 = (r1 << 4)
	
	@ 如果第二个操作数是一个立即数,可以使用<< >> ~ 进行运算
	mov r0, #(0xFF << 4)

4.3 位运算指令

4.3.1 指令格式

<opcode>{cond}{s}  Rd, Rn, oprand_shifter  

@ Rd = Rn 与/或/异或  oprand_shifter  
and : 按位与运算  & 
orr : 按位或运算  |
eor : 按位异或运算  ^
bic : 按位清除

与运算

左操作数

位运算符

右操作数

运算结果

1

&

0

0

0

&

0

0

1

&

1

1

0

&

1

0

或运算

左操作数

位运算符

右操作数

运算结果

1

|

0

0

0

|

0

0

1

|

1

1

0

|

1

0

异或运算

左操作数

位运算符

右操作数

运算结果

1

^

0

0

0

^

0

0

1

^

1

1

0

^

1

0

与0清0,与1不变
或1置1,或0不变
异或1取反,异或0不变

4.3.2 测试代码

@ 假设你不知道R0寄存器中存储的值
	ldr r0, =0x12345678
	@ 31                                     0
	@  **** **** **** **** **** **** **** ****
	@ 1> 将R0寄存器中的值的第[3]位清0,保持其他位不变
        and r0, r0, #0xFFFFFFF7
        and r0, r0, #~(0x1 << 3)
	bic r0, r0, #(0x1 << 3)
	
	@ 2> 将r0寄存器中的值的第[29]位置1,保持其他位不变
	orr r0, r0, #(0x1 << 29)
	
	@ 3> 将R0寄存器中的值的第[7:4]位清0,保持其他位不变
	and r0, r0, #(~(0xF << 4))
	bic r0, r0, #(0xF << 4)

	@ 4> 将R0寄存器中的值的第[15:8]位置1,保持其他位不变
	orr r0, r0, #(0xFF << 8)
	
	@ 5> 将R0寄存器中的值的第[3:0]位按位取反,保持其他位不变
	eor r0, r0, #(0xF << 0)
	
	@ 6> 将R0寄存器中的值的第[11:4]位修改为10101011,保持其他位不变	
	@ 6.1> 先将对应的位清0
	and r0, r0, #(~(0xFF << 4))
	@ 6.2> 再将对应的位置1
	orr r0, r0, #(0xAB << 4)
	
	@ 一般情况不使用先置1,后清0
	@ 6.1 先将对应的位置1
	orr r0, r0, #(0xFF << 4)
	@ 6.2 再将对应的位清0
	and r0, r0, #(~(0x54 << 4))
	
	@ bic位清除指令
	@ 第二个操作数据的哪位是1,最终将第一个操作寄存器中的置的
	@ 哪一位清0,并将结果写到目标寄存器中
	bic r0, r0, #0xFF  @ 将R0的[7:0]位清0
	
	@ 如果目标寄存器的编号和第一个操作寄存器的编号相同,可以合并写一个
	and r0, #~(0xF << 4)    @ 等价于 r0 &= ~(0xF << 4)

4.4 算数运算指令

4.4.1 指令格式

<opcode>{cond}{s}  Rd, Rn, oprand_shifter

add : 普通的加法指令,不需要考虑进位标志位(C位)
adc :带进位的加法指令,需要考虑进位标志位(C位)
sub : 普通的减法指令,不需要考虑借位标志位(C位)
sbc : 带借位的减法指令,需要考虑借位标志位(C位)
mul : 乘法指令
div : 除法指令, ARM-V7之前架构之前的架构都不支持除法指令,
	ARM-v8架构之后架构支持除法指令

4.4.2 测试代码

@ 案例1:实现两个64位数相加
	@ r0和R1存放第1个64位的数 
	@ r2和r3存放第2个64位的数 
	@ r4和r5存放运算的结果
	mov r0, #0xFFFFFFFE   @ 低32位
	mov r1, #5     @ 高32位
	mov r2, #6     @ 低32位
	mov r3, #7     @ 高32位
	
	@ s : 指令的执行结果影响CPSR的NZCV位,修改NZCV值
	adds r4, r0, r2  @ r4 = r0 + r2 = 0x4
	@ adc : 带进位的加法指令,自动读取C位的值。
	adc r5, r1, r3  @ r5 = r1 + r3 + C = 0xD
	
	@ 案例2:实现两个64位数相减
	@ r0和R1存放第1个64位的数 
	@ r2和r3存放第2个64位的数 
	@ r4和r5存放运算的结果
	mov r0, #5     @ 低32位
	mov r1, #10    @ 高32位
	mov r2, #6     @ 低32位
	mov r3, #7     @ 高32位
	
	subs r4, r0, r2 @ r4 = r0 - r2 = 0xFFFFFFFF
	sbc r5, r1, r3  @ r5 = r1 - r3 - !C = 0x2
	
	mov r0, #4
	mov r1, #5
	mul r2, r0, r1   @ r2 = r0 * r1 = 0x14
	
	@ 乘法指令的第二个操作数只能是一个寄存器
	@ mul r2, r0, #5   @ error

4.5 比较指令

4.5.1 指令格式

cmp{cond}  Rn, oprand_shifter

1. cmp指令没有目标寄存器,只有第一个操作寄存器和第二个操作数
2. 比较指令比较的就是第一个操作寄存器和第二个操作数的大小,本质就是做减法运算
3. 比较指令的运算结果最终影响的是CPSR的NZCV位,并且CMP指令后不需要加S.
4. 使用比较指令之后的结果,必须和条件码配合使用,条件码如下如所示:

arm平台lxc容器创建 arm 教程_stm32_03


就是省略了回写的subs指令,可以直接用减法指令替换,其功能就是将cpsr寄存器的某些位置一清零。

4.5.2 测试指令代码

@ 比较R0和R1寄存器中的值
	@ 如果R0>R1,则R0 = R0-r1
	@ 如果R0<R1, 则R1 = r1-r0
	mov r0, #9
	mov r1, #15
	cmp r0, r1    @ 左操作数 比较运算符  右操作数
	
	@ 使用cmp指令和注记符可以实现指令的有条件执行
	subhi r0, r0, r1
	subcc r1, r1, r0

5. 跳转指令

5.1 指令格式

b/bl{cond}   Label(标签,汇编函数的名字,表示函数的入口地址)

b  ----> 不带返回值的跳转指令,执行跳转指令是不保存返回地址到LR寄存器中
bl ----> 带返回值的跳转指令,执行跳转指令是保存返回地址到LR寄存器中

b : 有去无回就用b,比如while(1)死循环
bl : 有去有回就用bl,比如函数的调用

跳转指令的本质就是修改PC值,执行跳转指令时将Label标签标识的地址赋值给PC.

5.2 测试代码

@ nop 空指令
	nop
	nop
	nop 
	@ 执行跳转指令时修改PC值执行add_func函数的第一条指令
	@ 同时保存跳转指令的下一条指令的地址到LR寄存器
	bl add_func
	nop 
	nop
	nop
	@ 跳转到loop标签下的第一条指令执行,
	@ 不保存返回地址到LR中
	b loop
	
add_func:
	mov r0, #0x3
	mov r1, #0x4
	add r2, r0 , r1
	mov pc, lr   @ mov r15, r14

5.3 实现跳转的其他方式

mov pc, lr     @ 一般用于函数的返回,

mov pc, oprand_shifter  @ 需要确定跳转的地址, oprand_shifter必须是4的整数倍

ldr pc, =Label   @ 等价于b Label

ldr r0, =Label
mov pc, r0       @ 等价于b Label

注意:

mov pc, #Label 
一般没人这么干,因为你不能保证你的标签是一个立即数

要么是使用寄存器直接给pc赋值
mov pc, rn 

要么是使用ldr伪指令
ldr pc, =Label

5.4 练习

5.4.1 求两个数的最大公约数

arm平台lxc容器创建 arm 教程_指令格式_04


思路:

  1. 先判断两个数是否相等,若相等则这个数就是最大公约数
  2. 若不相等,则让大数减小数
  3. 再次执行步骤1,直至找到最大公约数

代码实现:

mov r0, #9
	mov r1, #15
	mm:	
	cmp r0, r1
	beq loop
	subhi r0, r0, r1
	subcc r1, r1, r0
	b mm

5.4.2 实现for循环,求1-100的和

用C语言实现:

sum = 0;
for(i = 1;i <= 100; i++){
    sum += i;
}
for循环语句的执行过程
for (1;2;3)
{
	4;
}
[1,2][4,3,2][4,3,2][4,3,2]........

汇编实现:

mov r0, #0   @ 等价于sum
	mov r1, #1   @ 等价于i, 表达式i
	mm:
	cmp r1, #100 @ 表达式2
	bhi loop
	add r0, r0, r1 @ 表达式4
	add r1, r1, #1 @ 表达式3
	b mm

6. 特殊功能寄存器操作指令

6.1 指令格式

mrs  Rd, cpsr     @ Rd = cpsr
msr  cpsr, oprand_shifter @ cpsr = oprand_shifter

mrs : 将特殊功能寄存器中的值读到普通寄存器中 
msr : 将普通寄存器中的值读到特殊功能寄存器中

对于特殊功能寄存器的读写访问,只能使用mrs或者msr完成。

6.2 测试代码

@ 系统上电就是一个复位的信号,默认处理器工作在SVC模式
	
	@ 从SVC模式切换到用户模式,修改CPSR的模式位,保证其他位不变
	
	@ 在SVC模式下CPSR寄存器中的值 
	@ I[7] = 1   F[6] = 1  T[5] = 0  M[4:0] = 10011
	@ 修改模式位,切换到用户模式,保证其他位不变
	@ 修改之后,CPSR中的值应该为
	@ I[7] = 1   F[6] = 1  T[5] = 0  M[4:0] = 10000
	
	@ 方式1:之间修改CPSR的模式位,前提必须的确定其他位的值
	@ msr cpsr, #0xD0   @ 1101 0000
	
	@ 方式2: 只修改CPSR的模式位,其他位不变
	@ 1. 先将cpsr中的值读到普通寄存器中
	mrs  r0, cpsr
	@ 2. 修改普通寄存器中的[4:0]位
	bic r0, r0, #0x1F
	orr r0, r0, #0x10
	@ 3. 将结果写回到cpsr中
	msr cpsr, r0

7. Load/Store内存读写指令

7.1 单寄存器指令

7.1.1 指令码

ldr/str  : 一次读写1个字空间的大小的数据
ldrh/strh :一次读写半个字空间大小的数据
ldrb/strb :一次读写一个字节空间大小的数据

ld : Load(加载)
st : Store(存储)
r  : Register
h  : Half
b  : Byte

7.1.2 指令格式

ldr/ldrh/ldrb  Rd, [Rm]
	1> 将Rm寄存器中的数据当成一个内存的地址,
	2> 将[Rm]指向的空间的数据读到Rd寄存器。
	int a = 100;
	int *p = &a;
	int b = *p;
	----------------------
	Rm   <======>  p
	[Rm] <======>  *p;
	ldr Rd, [Rm] <=====>  b = *p;
	
str/strh/strb  Rn, [Rm]
	1> 将Rm寄存器中的数据当成一个内存的地址,
	2> 将Rn寄存器中的数据写到[Rm]指向的内存空间中。
	int a = 100;
	int *p = &a;
	int b = 200; 
	*p = b;
	----------------
	Rm   <======> p
	[Rm] <======> *p
	str Rn, [Rm] <=====>  *p = b;

7.1.3 测试代码

ldr r0, =0x12345678
	ldr r1, =0x40000800
	
	@ 将R0寄存器中的数据写到[R1]指向的地址空间中
	str r0, [R1]
	@ 将[R1]指向的地址空间中的数据读到R2寄存器中
	ldr r2, [R1]

arm平台lxc容器创建 arm 教程_操作数_05


arm平台lxc容器创建 arm 教程_寄存器_06

7.1.4 练习

ldr r0, =0x40000800
	ldr r1, =0x11111111
	ldr r2, =0x22222222
	ldr r3, =0x33333333
	
	@ 将r1寄存器中的数据写到[R0+4]指向的地址空间中,
	@ R0寄存器中的地址不变。
	@ [0x40000804] = 0x11111111   R0 = 0x40000800
	str r1, [r0, #4]
	
	
	@ 将R2寄存器中的数据写到[R0]指向的地址空间中,
	@ 同时更新R0寄存器中的地址,R0 = R0 + 4;
	@ [0x40000800] = 0x22222222   R0 = 0x40000804
	str r2, [r0], #4
	
	@ 将r3寄存器中的数据写到[R0+4]指向的地址空间中,
	@ 同时更新R0寄存器中的地址,R0 = R0 + 4;
	@ ! : 更新地址
	@ [0x40000808] = 0x33333333   R0 = 0x40000808
	str r3, [r0, #4]!
	
	@ 以上三种写法同样适用于ldr str ldrh strh ldrb strb
	@ 偏移地址必须是自己操作空间整数倍。
	
	@ 使用汇编验证大小端,
	@ 使用str存储数据,使用ldrb分别读取数据。
	
		
	ldr r0, =0x40000800
	ldr r1, =0x12345678
	str r1, [r0]
	ldrb r2, [r0, #0]
	ldrb r3, [r0, #1]
	ldrb r4, [r0, #2]
	ldrb r5, [r0, #3]

	
	ldrb r2, [r0], #1
	ldrb r3, [r0], #1
	ldrb r4, [r0], #1
	ldrb r5, [r0], #1
	

	ldr r0, =0x40000800
	ldrb r2, [r0]
	ldrb r3, [r0, #1]!
	ldrb r4, [r0, #1]!
	ldrb r5, [r0, #1]!

7.2 多寄存器操作指令

7.2.1 指令码

stm : 将多个寄存器中的值写到连续的地址空间中
ldm :将连续的地址空间中的数据读到多个寄存器中

m : multi

7.2.2 指令格式

stm  Rm, {寄存器列表}
	1> 将Rm寄存器中的数据当成一个地址看待
	2> 多个不同编号的寄存器
ldm  Rm, {寄存器列表}
	1> 将Rm寄存器中的数据当成一个地址看待
	2> 多个不同编号的寄存器

寄存器列表的书写方式:
	1> 如果寄存器列表中的寄存器编号连续使用“-”隔开, 比如:r0-r5
	2> 如果寄存器列表中的寄存器编号不连续使用“,”隔开,比如:r0-r5, lr
	3> 寄存器列表中的寄存器要求从小到大依次书写。
	4> 如果寄存器列表中的寄存器,按照从大到小进行书写,所有的寄存器必须单独书写,
		不可以使用"-",只能使用“,”,并且编译时会报警告。
		比如:
			r5-r0 : 错误书写格式
			r5,r4,r3,r2,r1,r0 : 正确,但是报警告。

7.2.3 测试代码

ldr r0, =0x40000800
	ldr r1, =0x11111111
	ldr r2, =0x22222222
	ldr r3, =0x33333333
	ldr r4, =0x44444444
	ldr r5, =0x55555555
	
	@ 将r1-r5寄存器中的数据写到R0指向的连续的20字节内存空间中
	stm r0, {r1-r5}
	
	@ 将r0指向的连续的20字节空间的数据读到r6-r10寄存器中。
	@ ldm r0, {r6-r9,r10}
	
	ldm r0, {r10,r9,r8,r7,r6}

arm平台lxc容器创建 arm 教程_操作数_07


arm平台lxc容器创建 arm 教程_寄存器_08

7.3 栈操作指令

7.3.1 栈的类型

增栈:压栈之后,栈指针向高地址方向移动。

减栈:压栈指针,栈指针向低地址方向移动。

满栈:当前栈指针指向的空间右有效的数据,需要先移动栈指针,让栈指针指向一个没有有效数据的空间,然后再压入数据,压入数据之后,栈指针指向的空间又包含有效数据了。

arm平台lxc容器创建 arm 教程_指令格式_09


空栈:当前栈指针指向的栈空间,没有有效的数据,可以先进行压栈,压入数据之后栈指针指向的空间就有有效的数据了,因此需要移动栈指针,让栈指针再次指向一个没有有效数据的空间。

arm平台lxc容器创建 arm 教程_寄存器_10

将定义两两结合就行成了:
满减栈
满增栈
空减栈
空增栈

7.3.2 栈空间的操作方式

满增栈 :Full Ascending 		stmfa/ldmfa
满减栈 :Full Descending		stmfd/ldmfd
空增栈 :Empty Ascending		stmea/ldmea
空减栈 :Empty Descending		stmed/ldmed

ARM处理器中默认使用的满减栈,stmfd/ldmfd。

7.3.3 指令格式

stmfd sp!, {寄存器列表}
ldmfd sp!, {寄存器列表}

	1> 将sp寄存器中的数据当成一个地址看待
	2> 多个不同编号的寄存器
	3> ! : 作用,每次压栈或者出栈完成之后都需要跟新栈指针寄存器中的地址。

寄存器列表的书写方式:
	1> 如果寄存器列表中的寄存器编号连续使用“-”隔开, 比如:r0-r5
	2> 如果寄存器列表中的寄存器编号不连续使用“,”隔开,比如:r0-r5, lr
	3> 寄存器列表中的寄存器要求从小到大依次书写。
	4> 如果寄存器列表中的寄存器,按照从大到小进行书写,所有的寄存器必须单独书写,
		不可以使用"-",只能使用“,”,并且编译时会报警告。
		比如:
			r5-r0 : 错误书写格式
			r5,r4,r3,r2,r1,r0 : 正确,但是报警告。
	5> 不管寄存器列表中的寄存器如何书写,永远都是小编号的寄存器对应这低地址,
		大编号的寄存器对应这高地址。

7.3.4 测试代码

@ 1. 初始化栈指针
	ldr sp, =0x40000820
	
	@ 2. 初始化r0,r1寄存器
	mov r0, #3
	mov r1, #4
	bl add_func
	add r2, r0, r1  @ r2 = r0 + r1 = 0x7
	nop
	nop 
	b loop
	
	add_func:
		stmfd sp!, {r0-r1,lr} @ 压栈保存现场
		mov r0, #5
		mov r1, #6
		bl sub_func 
		add r3, r0, r1  @ r3 = r0 + r1 = 0xB
		ldmfd sp!, {r0-r1,pc} @ 出栈恢复现场
		@ mov pc, lr
		
	sub_func:
		stmfd sp!, {r0-r1}   @ 压栈保存现场
		mov r0, #10
		mov r1, #7
		sub r4, r0, r1  @ r4 = r0 - r1 = 0x3
		ldmfd sp!, {r0-r1}   @ 出栈恢复现场
		mov pc, lr