目录
前言
一、ARM、Thumb、Thumb2指令集有什么不同?
二、如何判断CPU使用什么指令集?
1.引入寄存器概念
2.特殊寄存器
三、统一汇编语言(UAL)
四、对于学习汇编的看法
总结
指令集,就是CPU中用来计算和控制计算机系统的一套指令的集合,而每一种新型的CPU在设计时就规定了一系列与其他硬件电路相配合的指令系统。而指令集的先进与否,也关系到CPU的性能发挥,它也是CPU性能体现的一个重要标志。指令的强弱也是CPU的重要指标,指令集是提高微处理器效率的最有效的工具之一。从现阶段的主流体系结构讲,指令集可分为复杂指令集和精简指令集两部分 。
对于学习了解过Cortex-M系列的同学应该都知道,ARM芯片属于精简指令集计算机(RISC)
它所用的指令比较简单,有如下特点:
① 对内存只有读、写指令
② 对于数据的运算是在CPU内部实现
③ 使用RISC指令的CPU复杂度小一点,易于设计
而X86架构的芯片使用的是复杂指令集计算机(CISC)
在CISC微处理器中,程序的各条指令是按顺序串行执行的,每条指令中的各个操作也是按顺序串行执行的。顺序执行带来的好处就是控制简单,但计算机各部分的利用率不高,执行速度受影响
。
(表格来源fds大大)
ARM指令集(32位),每条指令占据32位,高效,但是太占空间。
Thumb指令集(16位),每条指令占据16位,节省空间。
Thumb2指令集,它支持16位指令、32位指令混合编程。
一开始,ARM公司发布了ARM和Thumb两个指令集,这就出现一个问题,当我们CPU运行时,怎么判断我们使用的是ARM指令集还是Thumb指令集。
我们都知道在处理器内核中都有多个执行数据处理和控制的寄存器,这些寄存器大都以寄存器组的形式进行了分组。每个数据处理指令都指定了所需的操作和源寄存器,而且若需要,还有目的寄存器。对于 ARM 架构,若处理的是存储器中的数据,就需要将其从存储器加载到寄存器组中的寄存器里。在处理器内处理完后,若有必要,还要写回存储器,这种方式一般被称作“加载一存储架构”。由于寄存器组中有丰富的寄存器,这种设计使用起来非常方便,而且可以用C编译器生成高效的程序代码。例如,在进行其他的数据处理时,寄存器组中可以临时存储一些数据变量,而无须更新到系统存储器及在使用时将它们读回。
Cortex-M3和Cortex-M4处理器的寄存器组中有16个寄存器其中13个为32位通用目的寄存器,其他3个则有特殊用途,如图所示。
在特殊寄存器中有一个PSR寄存器即程序状态寄存器
程序状态寄存器包括以下三个状态寄存器
应用PSR(APSR)
执行 PSR(EPSR)
中断PSR(IPSR)
这三个寄存器可以通过一个组合寄存器访问,即xPSR。对于ARM汇编器,访问xPSR时,使用的是PSR。
(组合程序状态寄存器结构)
那我们怎么通过程序状态寄存器知道当前所使用的指令是ARM还是Thumb呢?
由下表可知
31 | 30 | 29 | 28 | 27 | 26:25 | 24 | 23:20 | 19:16 | 15:10 | 9 | 8 | 7 | 6 | 5 | 4:0 | |
Cortex-M3 | N | Z | C | V | Q | ICI/IT | T | ICI/IT | 异常编号 | |||||||
Cortex-M4 | N | Z | C | V | Q | ICI/IT | T | GE[3:0] | ICI/IT | 异常编号 |
寄存器各个位的作用
程序状态寄存器中有一位,名为“T”(第24位),它等于1时表示当前运行的是Thumb指令。
特殊寄存器只能通过MSR
和MRS
指令访问,它们没有内存地址:
我们可以通过汇编指令读/写当前PSR寄存器的状态
MRS r0,PSR ;读组合程序状态字
MSR PSR,r0 ;写组合程序状态字
也可单独访问每个PSR,例如:
MRS r0,APSR ;将标志状态读入r0
MRS r0,IPSR ;读取异常/中断状态
MSR APSR,r0 ;写状态异常
注意
软件代码无法直接使用MRS(读出为0)或MSR直接访问EPSR。
APSR可读可写。而IPSR和EPSR为只读,可以从组合PSR(xPSR)中读出。
由此可以看出,当我们使用一个Thumb指令时PSR中T为1,使用ARM指令时该位为0。
因为ARM指令比Thumb指令效率更高,假设当我们使用ARM指令写函数A,Thumb指令写函数B,此时我的CPU该如何在执行函数A时进入ARM状态,执行函数B时进入Thumb状态?
我们可以在调用函数A时,让PC寄存器的BIT0等于1,即:PC=函数A地址+(1<<0);
调用函数B时,让PC寄存器的BIT0等于0:,即:PC=函数B地址。但这样就显得很麻烦,所以ARM公司又推出了Thumb2指令集,它能够进行16/32位的混合编程,更加强大,高效,轻佻。
通过前面的学习我们了解到了ARM有这么多指令集:ARM、Thumb、Thumb2,
各指令集之间也不好记啊!
所以ARM公司推出了: Unified Assembly Language(UAL,统一汇编语言)通过UAL你不需要去区分这些指令集。
在Thumb-2
技术出现后,几乎所有的指令都分为2个版本,一个更新APSR,另一个不更新APSR。因此传统的Thumb
语法无法适用于Thumb-2
的软件开发,为了提高架构间的可移植性,并使不同架构的ARM处理器符合同一种汇编语言语法,较新的ARM开发工具开始支持统一汇编语言UAL
,对于之前的汇编来讲,主要区别在于:
S
后缀变得更为明确。过去的汇编,S后缀即使不加,也会更新APSR,对于UAL语法,更新APSR的指令都应具有S后缀,以指明所需的操作,提供不同架构的兼容性。例如:
Thumb指令
ADD R0,R1 ;R0 = R0 + R1,更新APSR
UAL指令
ADDS R0,R0,R1 ;R0 = R0 + R1,更新APSR
在Thumb‐2 指令集中,有些操作既可以由16 位指令完成,也可以由32 位指令完成。例如,R0=R0+1 这样的操作,16 位的与32 位的指令都提供了助记符为“ADD”的指令。在UAL下,我们既可以人汇编器决定用那个也可以自己手动指定使用16位还是32位。
ADDS R0,#1 ;默认使用16位Thumb指令,减小代码体积
ADDS.N R0,#1 ;使用16位的 Thumb 指令,N = Narrow
ADDS.W R0,#1 ;使用32位的 Thumb-2 指令,W = wide
并且如果没有给出后缀,汇编器会先试着用16 位指令以缩小代码体积,如果不行再使用32 位指令。
由于指令集过于繁杂,有兴趣的同学可以自己去寻找一些资料学习,这里就不再过多介绍了。
在我看来,因为汇编语言是最接近于机器语言的编程语言。汇编语言操作直接面向硬件,所以,我们在使用汇编语言的时候,我们能够感知计算机的运行过程和原理,从而能够对计算机硬件和应用程序之间的联系和交互形成一个清晰的认识。就像我们开发STM32时使用库函数/HAL库开发一样,这种开发方法固然方便,但这都是对一种已经封装好API函数,你只需要知道使用时应该调用哪一个函数即可。虽然库函数开发也是操作寄存器,但我们如果使用寄存器开发,就有一种透过现象看本质的效果。因为单片机的开发本质上来说就是操作寄存器。
俗话说得好地基不牢地动山摇,要想学好单片机必须要非常熟悉底原理,毕竟我们底层开发接触到越底层越好,能让我们知道每一句代码在做什么。这也是我对于汇编学习的看法,虽然汇编学习枯燥无味,但是这是作为一个开发者必须学习的东西,你不可能学了这么久的单片机连一个PUSH和POP指令都不知道是干嘛的,这就有点空中楼阁的感觉了。
当然,汇编的学习也不是应该完全扎进去,你只需要懂得一些常用的指令,比如MOV、LDR、STR、BL、DCD、ADD、SUB等基础常见的指令。这对将来程序调试,错误查找都有很大的帮助。比如通过Keil生成的反汇编代码并结合栈中保存错误地址分析程序出错/崩溃的原因。
以上就是今天要讲的内容,本文仅仅简单介绍了ARM的指令集以及个人对于汇编学习的看法,第一次写博,望多多包涵,也欢迎各位大佬提出改进意见。