Cortex-M系列:非中断、特权模式下的汇编语言

通过Keil5,在stm32H743非中断模式下,使用主堆栈指针下的汇编语句查看。理解这个汇编逻辑后,将有利于更自由的进行程序调试,查找BUG。

文章涉及的汇编指令可以在工程中左侧:book→Device Data Books→Cortex-M7 Generic User Guide中查找。

目录

 


1 查看堆栈

通过调试模式下的Memory窗口,可以看到:

1、单片机是小端模式,

2、入栈是从后往前压,显然出栈应该是从前往后出。

3、栈是向下增长的。

4、从地址上还能看出,栈是存储在RAM中的

 

Cortex-M系列:非中断、特权模式下的汇编语言_第1张图片

函数运行流程与汇编语言

2.1 没有输入参数而有输出参数的函数调用

这是C语言:

HAL_StatusTypeDef HAL_Init(void)
{
  /* Set Interrupt Group Priority */
  HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);

  /* Use systick as time base source and configure 1ms tick (default clock after Reset is HSI) */
  if(HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK)
  {
    return HAL_ERROR;
  }

  /* Init the low level hardware */
  HAL_MspInit();

  /* Return function status */
  return HAL_OK;
}

这是C语言对应的汇编:

Cortex-M系列:非中断、特权模式下的汇编语言_第2张图片

首先,调用者通过跳转指令修改了PC、LR寄存器。

然后,被调用者内如果会调用其他函数,则会把LR寄存器压入栈中,如果有相关运算还需要使用寄存器,则也把这些寄存器压到栈中。程序运行结束时,把通用寄存器推出,并把有LR寄存器压入栈的值推导PC寄存器中。

状态

C语言

(箭头表示程序中断的位置)

黑色汇编,蓝色为说明

函数进入

→HAL_Init();

BL.W HAL_Init(0x08004818)

PC=0x800AC5E

MSP=0x240268D8

下一指令:0x800AC62

函数开始

HAL_StatusTypeDef HAL_Init(void)

→{

PUSH {r4,lr}

PC=0x8004818

LR=0x800AC63

MSP=0x240268D8

退出结束

 → return HAL_OK;

}

 

return HAL_OK;

→}

 

→return HAL_ERROR;

  }

MOVS r0,#0x00

B 0x0800482A

 

POP {r4,pc}

 

MOVS r0,#0x01

可见,第一个返回指令后紧接着一个出栈指令,其他后续返回处用跳转指令到这个第一个出栈指令处。出栈指令对应C语言中的函数结束出的“}”。入栈指令对应函数入口处的“{”。

在汇编窗口处的单步和在C窗口处的单步是不一样的。汇编处的单步可能会比C语言处的步长短一些或同等长度。

2.2 带输入参数和输出参数的函数调用

这是C语言:

__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
  /* Configure the SysTick to have interrupt in 1ms time basis*/
  if (HAL_SYSTICK_Config(SystemCoreClock / (1000U / uwTickFreq)) > 0U)
  {
    return HAL_ERROR;
  }

  /* Configure the SysTick IRQ priority */
  if (TickPriority < (1UL << __NVIC_PRIO_BITS))
  {
    HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0U);
    uwTickPrio = TickPriority;
  }
  else
  {
    return HAL_ERROR;
  }

  /* Return function status */
  return HAL_OK;
}

这是C语言对应的汇编:

Cortex-M系列:非中断、特权模式下的汇编语言_第3张图片

2.2.1 截取说明

状态

C语言

(箭头表示程序中断的位置)

黑色汇编,蓝色为说明

函数进入

→if(HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK)

MOVS r0,#0x0F

BL.W HAL_InitTick (0x08004834)

PC=0x08004822

下一条指令:0x8004826

MSP=0x240269D0

函数开始

__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)

→{

PUSH {r4-r6,lr}

LR:0x8004827

PC:0x08004834

退出结束

→ return HAL_ERROR;

  }

 

return HAL_OK;

→}

 

 

→  return HAL_OK;

}

 

MOVS r0,#0x01

 

 

POP {r4-r6,pc}

 

 

MOVS r0,#0x00

B 0x08004856 ;即错误返回下面的一个出栈指令处

退出后

→if(HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK)

CBZ r0,0x0900482c

2.2.2 规律总结

Cortex-M系列:非中断、特权模式下的汇编语言_第4张图片

图片来自Arm Cortx-M3与Cortx-M4 权威指南 第三版的第p189页。

1、若参数小于4个,则调用者直接把参数一次写到R0,R1,R2,R3中;参数不足4个时,后面几个寄存器就不修改。若参数大于4个,则调用者通过压栈的方式来存参数输入参数,被调用者通过出栈的方式来获取输入参数。返回值根据其类型,选择使用R0或R1来返回参数。

2、被调用者需要使用的寄存器数自己心里有数。因此可以提前把需要修改的被调用者保存寄存器(R4~R11)压如栈中,返回时再把这些寄存器压出栈。也就是说被调用者对上图右侧的深色寄存器的修改是免责的,包括输入参数所在的寄存器。若调用者的后续程序还需要使用这些不被被调用者保存的寄存器(称为调用者保存寄存器),则需自行保存。

那么返回后调用者的寄存器内的值和被调用前是一致的,因此可以继续运行调用者剩余的程序。

 

3 业务逻辑

3.1 顺序结构

全局变量自增

全局变量从文本池获取全局变量的地址后,读内存,再修改值,最后协会内存。

 

3.2 分支结构

  /* Use systick as time base source and configure 1ms tick (default clock after Reset is HSI) */

  if(HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK)

  {

    return HAL_ERROR;

  }

  /* Init the low level hardware */

  HAL_MspInit();

 

Cortex-M系列:非中断、特权模式下的汇编语言_第5张图片

3.3 循环结构

	int i =0,j=0;
	
	for(i=0;i<10;i++)
	{
		j++;
	}
	i=j;

Cortex-M系列:非中断、特权模式下的汇编语言_第6张图片

 

3 订正与补充

1、订正了没有区分调用者寄存器和被调用者寄存器的错误。订正单片机名称撰写笔误。

2、添加修改全局变量的例子

你可能感兴趣的:(嵌入式,STM32H743,堆栈,汇编)