D8. 栈的种类与应用-ARM体系结构与接口技术-嵌入式学习LV9

DAY8. 栈的种类与应用


如果出现图片无法查看可能是网络问题,我用的GitHub+图床保存的图片,可以参考我另外一篇文章GitHub的使用方法含网络问题解决
GitHub使用教程含网络问题_github加速器_肉丸子QAQ的博客-CSDN博客


  • 从之前章节代码可以看到每段运行程序后面会有这段代码,为什么?

D8. 栈的种类与应用-ARM体系结构与接口技术-嵌入式学习LV9_第1张图片

  • 当程序运行最后一条指令时,PC会接着自增,会出问题

D8. 栈的种类与应用-ARM体系结构与接口技术-嵌入式学习LV9_第2张图片

1. 多寄存器内存访问指令

连续寄存器

		
		@ 多寄存器内存访问指令
		 MOV R1, #1
		 MOV R2, #2
		 MOV R3, #3
		 MOV R4, #4
		 MOV R11,#0x40000020
		 STM R11,{R1-R4}
		@ 将R1-R4寄存器中的数据写入到以R11为起始地址的内存空间中
		 LDM R11,{R6-R9}
		@ 将以R11为起始地址的内存空间中的数据读取到R6-R9寄存器中
  • 写:
  • 读:
    D8. 栈的种类与应用-ARM体系结构与接口技术-嵌入式学习LV9_第3张图片

STM:多寄存器使用花括号

不连续寄存器

当寄存器编号不连续时,使用逗号分隔

		 MOV R1, #1
		 MOV R2, #2
		 MOV R3, #3
		 MOV R4, #4
		 MOV R11,#0x40000020
	@ 当寄存器编号不连续时,使用逗号分隔
	 STM R11,{R1,R2,R4}
	@ 不管寄存器列表中的顺序如何,存取时永远是低地址对应小编号的寄存器
	 STM R11,{R3,R1,R4,R2} 
	@ 自动索引照样适用于多寄存器内存访问指令
	 STM R11!,{R1-R4}
  • 写:
  • 寄存器编号打乱:不管寄存器列表中的顺序如何,存取时永远是低地址对应小编号的寄存器

D8. 栈的种类与应用-ARM体系结构与接口技术-嵌入式学习LV9_第4张图片

  • 自动索引:
  • R1-R4一共有16个字节,那么自动索引后,R11也会自加16

2. 多寄存器内存访问指令的寻址方式

	@ 多寄存器内存访问指令的寻址方式
	 MOV R1, #1
	 MOV R2, #2
	 MOV R3, #3
	 MOV R4, #4
	 MOV R11,#0x40000020
	 STMIA R11!,{R1-R4}
	@ 先存储数据,后增长地址
	
	 STMIB R11!,{R1-R4}
	@ 先增长地址,后存储数据
	
	 STMDA R11!,{R1-R4}
	@ 先存储数据,后递减地址
	
	 STMDB R11!,{R1-R4}
	@ 先递减地址,后存储数据
  • 先存储数据,后增长地址:STMIA和STM效果一样

    • 储存完后值再变化

    D8. 栈的种类与应用-ARM体系结构与接口技术-嵌入式学习LV9_第5张图片

  • 先增长地址,后存储数据

    • 先变值后储存

    D8. 栈的种类与应用-ARM体系结构与接口技术-嵌入式学习LV9_第6张图片

  • 先存储数据,后递减地址

    • 先存后减(从20存)

D8. 栈的种类与应用-ARM体系结构与接口技术-嵌入式学习LV9_第7张图片

  • 先递减地址,后存储数据

    • 先减再存(从20减)

    D8. 栈的种类与应用-ARM体系结构与接口技术-嵌入式学习LV9_第8张图片

3. 栈的种类与使用

概念

  • 栈的概念 :栈的==本质就是一段内存==,程序运行时用于保存一些临时数据 如局部变量、函数的参数、返回值、以及程序跳转时需要保护的寄存器等

分类

  • 增栈:压栈时栈指针(往高地址走)越来越大,出栈时栈指针越来越小
  • 减栈:压栈时栈指针越来越大,出栈时栈指针越来越小
  • 满栈:栈指针指向最后一次压入到栈中的数据,压栈时需要**移动栈指针**到相邻位置然后再压栈
  • 空栈:栈指针指向最后一次压入到栈中的数据的相邻位置,压栈时可直接压栈,之**将栈指针移动到相邻位置**

栈分为空增(EA)空减(ED)满增(FA)、**满减(FD)**四种 ARM处理器一般使用满减栈

D8. 栈的种类与应用-ARM体系结构与接口技术-嵌入式学习LV9_第9张图片

		@ 栈的种类与使用
		 MOV R1, #1
		 MOV R2, #2
		 MOV R3, #3
		 MOV R4, #4
		 MOV R11,#0x40000020
		 STMDB R11!,{R1-R4}
		 LDMIA R11!,{R6-R9}

D8. 栈的种类与应用-ARM体系结构与接口技术-嵌入式学习LV9_第10张图片

注意:!!!

  • 为了避免写程序容易出错,在arm指令集里面将空增(EA)空减(ED)满增(FA)、**满减(FD)**设置了单独的后缀
		@ 栈的种类与使用
		 MOV R1, #1
		 MOV R2, #2
		 MOV R3, #3
		 MOV R4, #4
		 MOV R11,#0x40000020
		 STMFD R11!,{R1-R4}
		 LDMFD R11!,{R6-R9}

D8. 栈的种类与应用-ARM体系结构与接口技术-嵌入式学习LV9_第11张图片

结果相同

  • FD这些不是新指令,通过反编译可以看到会被替换

D8. 栈的种类与应用-ARM体系结构与接口技术-嵌入式学习LV9_第12张图片

4. 栈的应用举例

叶子函数的调用过程举例

.text				@表示当前段为代码段
.global _start		@声明_start为全局符号
_start:				@汇编程序的入口

MAIN:
		MOV R1, #3
		MOV R2, #5
		BL FUNC
		ADD R3, R1,R2
		B STOP

FUNC:
		MOV R1, #10
		MOV R2, #20
		SUB R3, R2, R1
		MOV PC, LR


STOP:	
		B STOP		@死循环,防止程序跑飞	

.end				@汇编程序的结束

从结果来看可以看出子函数和主函数寄存器使用一样的寄存器,主函数所使用的寄存器值被覆盖掉了

D8. 栈的种类与应用-ARM体系结构与接口技术-嵌入式学习LV9_第13张图片

为了避免这种情况就可以使用栈来暂时储存主函数的值

.text				@表示当前段为代码段
.global _start		@声明_start为全局符号
_start:				@汇编程序的入口

		@ 初始化栈指针
		MOV SP, #0x40000020 @压栈的地址
MAIN:
		MOV R1, #3
		MOV R2, #5
		BL FUNC
		ADD R3, R1,R2
		B STOP

FUNC:
		@ 压栈保护现场
		STMFD SP!, {R1,R2}
		MOV R1, #10
		MOV R2, #20
		SUB R3, R2, R1
		
		@ 出栈恢复现场
		LDMFD SP!, {R1,R2}@将破坏的R1,R2再重新赋值
		MOV PC, LR


STOP:	
		B STOP		@死循环,防止程序跑飞	

.end				@汇编程序的结束

非叶子函数的调用过程举例

		@ 2.非叶子函数的调用过程举例

		 MOV SP, #0x40000020
 MIAN:
		 MOV R1, #3
		 MOV R2, #5
		 BL  FUNC1
		 ADD R3, R1, R2
		 B STOP		
 FUNC1:
		 STMFD SP!, {R1,R2,LR} @如果不能将LR压栈,在第二级函数返回时就会出现死循环,无法回到主函数
		 MOV R1, #10
		 MOV R2, #20
		 BL  FUNC2
		 SUB R3, R2, R1
		 MOV PC, LR
 FUNC2:
		 STMFD SP!, {R1,R2}
		 MOV R1, #7
		 MOV R2, #8
		 LDMFD SP!, {R1,R2}
		 MOV PC, LR
		
		@ 执行叶子函数时不需要对LR压栈保护,执行非叶子函数时需要对LR压栈保护

5. 作业

1.以下代码为使用汇编语言模拟C语言叶子函数的调用过程,按照如下要求补全代码
注:
使用满减栈

MOV SP, #0x40000020
MIAN:
MOV R1, #3
MOV R2, #5
@调用FUNC子程序
1______________
ADD R3, R1, R2
B STOP

FUNC:
@压栈保护现场
2______________
MOV R1, #10
MOV R2, #20
SUB R3, R2, R1
@出栈恢复现场
3______________
MOV PC, LR

STOP:
B STOP

1. BL FUNC
2. STMFD SP!, {R1,R2}
3. LDMFD SP!, {R1,R2}

你可能感兴趣的:(学习,arm开发,驱动开发,linux)