RTOS学习笔记(2):简述ARM架构与RTOS运行原理&ARM常用汇编指令

(2)简述ARM程序运行&ARM常用汇编指令

        • 一、系统架构
        • 二、常用汇编指令
        • 1、寄存器组
        • 2、汇编指令
          • ① `MOV`指令(move)(传送指令)
          • ② `LDR`指令(load)(读取数据指令)
          • ③`STR`指令(store)(存储数据指令)
          • ④`ADD` / `SUB` / `MUL`指令(算术运算指令)
          • ⑤`CMP`指令(COMPARE)(比较指令)
          • ⑥`B` / `BL`指令(branch / branch&link)(跳转指令)
          • ⑦`PUSH` / `POP`指令(压栈 / 出栈指令)
          • ⑧启动文件中常用的ARM汇编指令汇总
        • 三、示例解析
        • 四、中断产生时的程序&硬件处理流程
          • ①将现场所有不受保护的寄存器入栈:
          • ②取向量并执行中断函数:
          • ③中断返回出栈与清除中断位:

该系列为学习笔记,旨在交流进步,如有错误烦请大家指出。

《Cortex M3权威指南》参考(8o27)

======专栏地址======

一、系统架构

以STM32F10X为例:
RTOS学习笔记(2):简述ARM架构与RTOS运行原理&ARM常用汇编指令_第1张图片
RTOS学习笔记(2):简述ARM架构与RTOS运行原理&ARM常用汇编指令_第2张图片
通过上第一图3个红框部分来说明简单的程序运行:
  1、M3内核(第二图为其简化视图):程序执行的核心。详细功能流程可参考《Cortex M3权威指南》。
  2、Flash:存储程序,指令,常量等。
  3、SRAM:存储变量。

u8 x = 1;
int main()
{
	u8 y;
	y = x;
	return 0;
}
/*
如上代码的执行顺序便是:
1、M3内核从flash(程序存储在flash)中获取指令。
2、M3内核执行指令。
3、M3内核去读SRAM中x的值。
4、M3内核去写SRAM中y的值。
*/

二、常用汇编指令

1、寄存器组

上述读写操作会将值存储在内核中的寄存器组(如下图)中,而做出这些读写操作就需要一些汇编指令。
RTOS学习笔记(2):简述ARM架构与RTOS运行原理&ARM常用汇编指令_第3张图片


R0-R12(通用寄存器)
R13(堆栈指针SP):
    主堆栈指针(MSP):复位后缺省使用的堆栈指针,用于操作系统内核以及异常处理例程(包括中断服务例程)
    进程堆栈指针(PSP):由用户的应用程序代码使用。

R14(连接寄存器LR):当呼叫一个子程序时,由 R14 存储返回地址。

R15(程序计数器PC):指向当前的程序地址。如果修改它的值,就能改变程序的执行流。(联想学习笔记(1)中的最后部分,应该就有大概的RTOS框架了)。

ARM 过程调用标准AAPCS中比较重要的几条:
1、R0~R3argument/scratch register用来传递参数和返回值,无需进行保护。
2、R4~R11:在需要使用这几个寄存器时,要先将其中数据入栈保护起来,并在使用结束前将数据出栈恢复。
3、R14(LR):在跳转到子函数时LR会储存返回地址,但如果在子函数中再调用别的函数,就要将LR中的地址先入栈保护起来,若再调用依次类推,出栈时也是先将最后入栈的LR地址先恢复,以此类推。

2、汇编指令

 部分指令格式以及注意事项也可参考《Cortex M3权威指南》中的第四章。

ARM 汇编器的最典型书写模式如下所示:
标号
  操作码   操作数 1,  操作数 2,;  注释。
MOV指令(move)(传送指令)
  • 格式:MOV{条件}   目的寄存器,<操作数>
  • 作用:设置初始值或者在寄存器间传送数据。
  • 目的操作数要与源操作数类型要一致,不能一个是字,一个是字节。
/*
操作数的表示方式有:立即数,寄存器地址,或寄存器加偏移。
立即数:即立即寻址的一个数据,前面加个#,如0x00002700,去掉#就是直接寻址;
寄存器:加个[],如[R1];
寄存器偏移:[R1,R2],或者[R1,#8],[R1,LSL #8]等。
*/
	MOV  R0,#050aH ;将十六进制数050a 传送到通用寄存器R0中
	MOV  PC,LR	;从子程序中返回
LDR指令(load)(读取数据指令)
  • 格式:LDR{条件}   目的寄存器,<存储器地址>
  • 作用:从对应存储器地址中读取32位的字数据到目的寄存器中。
  • LDRB是读取8位的字节数据,LDRH是16位的半字数据
	LDR R0,#0x00002700     ;将数据0x00002700读入寄存器R0。
	LDR R0,=0x00002700     ;将存储器地址0x00002700读入到寄存器R0。
	LDR R0,[R1,#8]       ;将存储器地址为R1+8的内存单元数据读入寄存器R0。
	LDR R0,[R1],#8       ;将存储器地址为R1的内存单元数据读入寄存器R0,然后R1=R1+8
STR指令(store)(存储数据指令)
  • 格式:STR{条件}   源寄存器,<存储器地址>
  • 作用:从源寄存器中将一个32位的字数据传送到存储器中
  • STRB是发送8位的字节数据,STRH是16位的半字数据
//使用方式可参考指令LDR
	STR R0,[R1],#8   ;将R0中的字数据写入以R1为地址的存储器中,然后R1=R1+8。
	STR R0,[R1,#8]   ;将R0中的字数据写入以R1+8为地址的存储器中。

ADD / SUB / MUL指令(算术运算指令)
//加
	ADD R0,R1,#4          ;R0 = R1 + 4
//减
	SUB R0,R1,R2          ;R0 = R1 - R2
//乘
	MUL R0,R1,R2          ;R0=R1*R2

RTOS学习笔记(2):简述ARM架构与RTOS运行原理&ARM常用汇编指令_第4张图片

CMP指令(COMPARE)(比较指令)
  • 作用:比较两个操作数,并把结果存入CPSR供下一句语句使用。
    (除R0~R15以外还有一些特殊功能寄存器,比如xPSR)
	CMP R0,R1    ;比较R0,R1
B / BL指令(branch / branch&link)(跳转指令)
  • B:格式: B{条件}  目标地址
  • 作用:立即跳转到给定的目标地址,从那里继续执行。
	B main			;跳转到main地址处执行
  • BL:格式: BL{条件}  目标地址
  • 作用:跳转之前,会在寄存器R14 (LR)中保存当前地址,当跳转代码结束后,用MOV PC,LR指令跳回来,这实际上就是C语言调用子函数的用法。
	BL delay
PUSH / POP指令(压栈 / 出栈指令)

汇编里把一段内存空间定义为一个栈,栈总是先进后出(FILO)。
RTOS学习笔记(2):简述ARM架构与RTOS运行原理&ARM常用汇编指令_第5张图片图自CM3权威手册

  • PUSH:格式:PUSH 操作数(操作数可以是寄存器或立即数)
  • 作用:寄存器的数据通过 PUSH 操作存入堆栈。俗称压栈。

  • POP:格式:POP 操作数(操作数只能是寄存器)
  • 作用:从堆栈中取回压入的数据。俗称出栈。
	PUSH  {R0 }  ;把 R0 存入栈 & 调整 SP
	PUSH  {R1}  ;把 R1 存入栈 & 调整 SP
	PUSH  {R2}  ;把 R2 存入栈 & 调整 SP
	//PUSH  {R0-R2,R4, R6} ;压入 R0-R2,R4,以及 R6;执行 程序 的功能,中途可以改变 R0-R2 的值
	POP {R2}  ;恢复 R2 早先的值 & 再次调整 SP
	POP {R1}  ;恢复 R1 早先的值 & 再次调整 SP
	POP {R0}  ;恢复 R0 早先的值 & 再次调整 SP
	/*
	PUSH  {R0-R2, LR}
	POP {R0-R2, PC}    ;PC即是程序下一步要执行的地址,因此直接绕过LR即可
	*/
⑧启动文件中常用的ARM汇编指令汇总

RTOS学习笔记(2):简述ARM架构与RTOS运行原理&ARM常用汇编指令_第6张图片

三、示例解析

以下示例代码是在keil环境,用模拟debug进行,MCU:STM32F103

int add(int a,int b)
{
    return a+b;
}

int main(void)
{		
	int a=1;
	int b=2;
	int c;
	c = add(a,b);
	return c;
}
//main函数的disassembly对应关系

//int main(void) 
0x080001B0 4770      BX       lr				
// {        
0x080001B2 B530      PUSH     {r4-r5,lr}		
//     int a=1; 
0x080001B4 2301      MOVS     r3,#0x01
//     int b=2; 
//     int c; 
0x080001B6 2402      MOVS     r4,#0x02
//     c = add(a,b); 
0x080001B8 4621      MOV      r1,r4
0x080001BA 4618      MOV      r0,r3
0x080001BC F7FFFFF6  BL.W     add (0x080001AC)
/*int add(int a,int b){
0x080001AC 4602      MOV      r2,r0
	    return a+b; 
0x080001AE 1850      ADDS     r0,r2,r1
}*/
0x080001C0 4605      MOV      r5,r0
//     return c; 
0x080001C2 4628      MOV      r0,r5
// } 
0x080001C4 BD30      POP      {r4-r5,pc}

汇编码拆解:

MOV加了S表示这个MOV指令会影响CPSR(当前程序状态寄存器)中的标志位。

	BX       lr						//= MOV PC,LR    
	PUSH     {r4-r5,lr}				//把r4,r5和lr中的数据压入栈,在这之后可以操作这几个寄存器
/*
入栈顺序:
高地址
LR的数据
R5的数据
R4的数据 <- 堆栈指针SP指向最后一个被压入堆栈的32位数值,在下一次压栈时,SP先自减4,再存入新的数值。
低地址
*/
	MOVS     r3,#0x01				;1赋值给r3	//r3=1
	MOVS     r4,#0x02				;2赋值给r4	//r4=2
/*
如果a,b声明时用了volatile关键字,那么会把r3(a),r4(b)的值STR到栈中,这里没有
*/
	MOV      r1,r4					;把r4赋值给r1	//r1=r4=2
	MOV      r0,r3					;把r3赋值给r0	//r0=r3=1
	BL.W     add (0x080001AC)		;跳转到add函数地址0x080001AC,并存储当前地址到lr
/*进入add函数
	MOV      r2,r0					;把r0赋值给r2  //r2=r0=1
	ADDS     r0,r2,r1				;r0=r2+r1=1+2
	(MOV PC,LR)
*/
	MOV      r5,r0					;r5=r0=3
	MOV      r0,r5					;return r0=r5=3
	POP      {r4-r5,pc}				;把先前入栈的数据弹出恢复到r4,r5,pc
/*
出栈顺序:
高地址
原先入栈的R4的数据  ->先从SP指针处读出上一次被压入的值,再把SP指针自增4。
原先入栈的R5的数据
原先入栈的LR的数据	//LR弹出到PC
*/
//将上述代码的c变量用volatile声明就会发现汇编的时候会将得到的c的值压入栈

    //volatile int c; 
0x080001B6 2402      MOVS     r4,#0x02
    //c = add(a,b); 
0x080001B8 4621      MOV      r1,r4
0x080001BA 4618      MOV      r0,r3
0x080001BC F7FFFFF6  BL.W     add (0x080001AC)
0x080001C0 9000      STR      r0,[sp,#0x00]		;当前r0值存入当前sp(即r4) //c=r4=r0=r1+r2=3
    //return c; 
0x080001C2 9800      LDR      r0,[sp,#0x00]		;读取当前sp指向的栈值到r0 //r0=c=3

四、中断产生时的程序&硬件处理流程

改该部分可参考《Cortex M3权威指南》的第9章中断/异常的处理。

①将现场所有不受保护的寄存器入栈:

例图
由于中断会随时产生,假设现在在红色箭头出产生了中断,那么在处理中断函数时就可能导致r0~r3的值被改变,因此中断产生前必须保存所有寄存器的值,而产生中断就会调用中断函数,在调用函数时会保证r4~r11受到保护(参考前面的AAPCS),那么就只需要由硬件保存其他几种寄存器。
RTOS学习笔记(2):简述ARM架构与RTOS运行原理&ARM常用汇编指令_第7张图片
注意这里PC才是中断返回的地址,LR只是之前储存的链接地址。

②取向量并执行中断函数:

数据总线执行入栈操作的时候,指令总线(I‐Code)正在从向量表中找到该中断函数的入口地址,然后执行该中断函数。

③中断返回出栈与清除中断位:

中断返回时先将之前保存的所有寄存器数值出栈,然后将NVIC寄存器中的中断标志位进行硬件清除。

你可能感兴趣的:(RT-Thread学习笔记,学习,arm,单片机,c语言)