【STM32】ARM汇编指令

在嵌入式的学习中,编程时,总会遇到一些汇编代码。。。

不过一想也是,嵌入式本身就偏底层,和硬件接触,汇编代码效率更高,总会接触的。

-----------------------------

汇编语言是什么?

汇编语言是程序设计语言的基础语言,是唯一可以直接与计算机硬件打交道的语言。

汇编语言根据CPU的不同,也分为:ARM汇编语言、x86汇编语言

ARM汇编指令: 
	EQU			给数字常量名取一个符号名,相当于C语言中的define
	AREA		汇编一个新的代码段或者数据段
	SPACE 		分配内存空间
	PRESERVE8	当前文件栈需按照 8 字节对齐
	EXPORT		声明一个标号具有全局属性,可被外部的文件使用
	DCD			以字为单位分配内存,要求 4 字节对齐,并要求初始化这些内存
	PROC		定义子程序,与 ENDP 成对使用,表示子程序结束
	IMPORT		声明标号来自外部文件,跟 C 语言中的 EXTERN 关键字类似
	B 			跳转到一个标号
	END         到达文件末尾 ,文件结束
	
	IF,ELSE,
	   ENDIF    汇编条件分支语句,跟 C 语言的 if else 类似
	
	MRS			加在特殊功能寄存器的值到通用寄存器
	MSR			存储通用寄存器的值到特殊功能寄存器
	CBZ			比较,比较结果为0就转移
	CBNZ		比较,比较结果非0就转移
	LDR			从存储器中加载字到寄存器中l 是
	LDR[伪指令] 加载一个立即数或者一个地址值到一个寄存器。例如:ldr rd, = lable 如果 label 是立即数,那 Rd 等于立即数,如果 labe一个标识符,比如指针,那存到 Rd 的就是 label 这个标识符的地址
	LDRH 		从存储器中加半字到一个寄存器中
	LDRB		从存储器中加载字节到寄存器中
	STR			把一个寄存器按字存储到存储器中
	STRH 		把一个寄存器存的低【半】字存储到存储器中
	STRB 		把一个寄存器的低字节存储到存储器中
	LDMIA 		将多个字从存储器加载到 CPU 寄存器,先操作,指针在递增。
	STMDB 		将多个字从 CPU 寄存器存储到存储器,指针先递减,再操作
	ORR 		按位或
	BX 			直接跳转到由寄存器给定的地址
	BL 			跳转到 标号对应的地址,并且把跳转前的下条指令地址保存到 LR
	BLX 		跳转到由寄存器 REG 给出的的地址,并根据 REG 的 LSB 切换处理器状
					态 , 还 要 把 转 移 前 的 下 条 指 令 地 址 保 存 到 LR 。 ARM(LSB=0) ,
					Thumb(LSB=1)。CM3 只在 Thumb 中运行,就必须保证 reg 的 LSB=1 否则一个fault 打过来


编译器指令:
	WEAK		弱定义,如果外部文件声明了一个标号,则优先使用外部文件定义的标号,如果外部文件没有定义也不出错。

	ALIGN		编译器对指令或者数据的存放地址进行对齐,一般需要跟一个立即数,缺省表示 4 字节对齐

------再看汇编代码并注释

 __asm void xPortPendSVHandler( void ) //这个函数完成:上下文切换:上文保存、切换下文
 {
 extern pxCurrentTCB;       (1)//  
 extern vTaskSwitchContext; (2)//
 
 PRESERVE8              (3)//当前文件栈需按照 8 字节对齐
 
 mrs r0, psp            (4)//加在特殊功能寄存器psp的值到通用寄存器r0 
 isb                    //指令同步隔离。最严格:它会清洗流水线,以保证所有它前面的指令都执行完毕之后,才执行它后面的指令
 
 ldr r3, =pxCurrentTCB   (5)//加载 pxCurrentTCB 的地址到 r3
 ldr r2, [r3]            (6)//加载r3指向的内容到r2,即 r2 等于 pxCurrentTCB
 
 stmdb r0!, {r4-r11}     (7)//db:decrease before. 以 r0 作为基址(指针先递减,再操作)将 CPU 寄存器 r4~r11 的值存储到任务栈,同时更新 r0 的值
 str r0, [r2]            (8)//将 r0 的值存储到 r2 指向的内容,r2 等于 pxCurrentTCB。
                       //到这步结束,上下文切换的上文保存,完成。
					   
 stmdb sp!, {r3, r14}    (9)//将 R3 和 R14 临时压入堆栈
 mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY  (10)//将 configMAX_SYSCALL_INTERRUPT_PRIORITY 的值存储到r0
 msr basepri, r0         (11)//关中断(???)将r0的值到特殊功能寄存器basepri

 dsb   //数据同步隔离。比 DMB 严格: 仅当所有在它前面的存储器访问操作都执行完毕后,才执行在它后面的指令
 isb   //指令同步隔离
 
 bl vTaskSwitchContext   (12)//跳转到vTaskSwitchContext的地址,即执行该函数,
 mov r0, #0              (13)//退出临界段,开中断,直接往 r0 写 0
 msr basepri, r0             //再将r0中的0写入到basepri中
 ldmia sp!, {r3, r14}    (14)//将多个字从存储器加载到 CPU 寄存器,先操作,指针在递增:从主堆栈中恢复寄存器 r3 和 r14 的值,此时的 sp 使用的是 MSP
 
 ldr r1, [r3]            (15)//加载 r3 指向的内容到 r1。r3 存放的是 pxCurrentTCB 的地址,即 让 r1 等于 pxCurrentTCB。pxCurrentTCB 在上面的 vTaskSwitchContext 函数中被更新,指向了下一个将要运行的任务的 TCB。
 ldr r0, [r1]            (16)//加载 r1 指向的内容到 r0,即下一个要运行的任务的栈顶指针
 ldmia r0!, {r4-r11}     (17)//以 r0 作为基地址(先取值,再递增指针,LDMIA 的 IA 表示Increase After),将下一个要运行的任务的任务栈的内容加载到 CPU 寄存器 r4~r11。
 msr psp, r0             (18)//将r0的值写到psp中:更新 psp 的值,等下异常退出时,会以 psp 作为基地址,将任务栈中剩下的内容自动加载到 CPU 寄存器
 
 isb  //指令同步隔离
 
 bx r14                  (19)//直接跳转到由寄存器r14给定的地址
 nop  //NOP空操作伪指令
 }
发布了89 篇原创文章 · 获赞 68 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/zDavid_2018/article/details/103920135