电子说
对于嵌入式开发者来说,了解汇编语言和内核寄存器是对内核深入理解的基础
..增加 2.2 汇编伪指令 章节 2021/12/12
..完善 2.3 ARM汇编指令集 2021/12/12
..增加 3.1 不同编译器的反汇编 2021/12/14
..增加 3.2 C和汇编 比较分析 2021/12/15
从开始写起也没想到内容有这么多,其中有很多干货的东西,希望自己能够说明到了,
其中有很多推荐的博文和网站,在此要特别感谢韦东山老师的视频,绝对干货满满
开头直接来看几个简单的汇编指令:
MOV R0,R1``MOV PC,R14
上面的指令中使用了汇编 MOV
指令,但是其中的 R0,R1,R14,PC分别是什么?哪来的?怎么用?
要讲 ARM 汇编语言,必须得先了解ARM的内核寄存器,内核处理所有的指令计算,都需要用到内核寄存器,所以ARM汇编里面指令大都是基于寄存器的操作。
文章前推荐韦东山老师的单片机核心视频,视频可以在韦东山老师官网里面找到:百问网
ARM版本简单介绍:
内核(架构)版本 | 处理器版本 |
---|---|
ARMv1 | ARM1 |
ARMv2 | ARM2、ARM3 |
ARMv3 | ARM6、 |
ARMv4 | ARM7、StrongARM |
ARMv5 | ARM9、ARM10E |
ARMv6 | ARM11 |
ARMv7 | ARM Cortex-A、ARM Cortex-M、ARM Cortex-R |
ARMv8 | ARM Cortex-A30、ARM Cortex-A50、ARM Cortex-A70 |
内核寄存器与外设寄存器:
内核寄存器与外设寄存器是完全不同的概念。内核寄存器是指 CPU 内部的寄存器,CPU处理所有指令数据需要用到这些寄存器保存处理数据;外设寄存器是指的 串口,SPI,GPIO口这些设备有关的寄存器。
在我的另一篇博文:FreeRTOS记录(三、FreeRTOS任务调度原理解析_Systick、PendSV、SVC)内核中断管理 章节讲到过Cortex-M的寄存器
的相关内容,这里我们再简单说明一下:
对于M3/M4而言:R13,栈指针(Stack Pointer)
R14 ,连接寄存器(Link Register)
R15,程序计数器(Program Count)
其中程序状态寄存器 XPSR:
程序状态寄存器,该寄存器由三个程序状态寄存器组成 应用PSR(APSR) :包含前一条指令执行后的条件标志,比较结果:大于等于,小于,进位等等; 中断PSR(IPSR ) :包含当前ISR的异常编号 执行PSR(EPSR) :包含Thumb状态位
对于 A7 而言:(上图取自原子教材,此图在官方文档《ARM Cortex-A(armV7)编程手册V4.0》中第3章.ARM Processor Modes and Registers 部分有英文原版,这里用中文版本更容易理解)
A7的 R13、R14、R15 的作用和 M3/4类似。
需要注意的一点就是,对于A7而言 R15,程序计数器(Program Count) :
A7内核的程序状态寄存器 CPSR:
因为ARM指令采用三级流水线机制,所以PC指针的值并不是当前执行的指令的地址值:
在文档《ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition》中对于 PC 的值有明确的说明:
M3/M4/M0:
PC的值 = 当前地址 + 4;
下面是一个 STM32F103 反汇编程序,找了一段有[pc,#0]的代码,方便判断:
A7:
PC的值 = 当前地址 + 8;
ARM芯片属于精简指令集计算机(RISC:Reduced Instruction Set Computing),具体说明在下面这篇博文5.4小结有过说明:
STM32的内存管理相关(内存架构,内存管理,map文件分析)
最初,ARM公司发布了两类指令集:
比如:MOV R0,R1
这条指令,可能是16位的,也可能是32位的
那么在汇编中是如何在 ARM 指令 和 Thumb 指令之间切换呢:
/*ARM指令 与 Thumb 指令 的切换*/
CODE16 ;(表示下面是 Thumb 指令)
...
...
;(调用下面的B函数)
bx B_addr;(B的地址B_addr的bit0 = 0,表示跳转过去执行 ARM 指令)
;A 函数
...
CODE32 ;(表示下面是 ARM 指令)
...
...
;B 函数
;(回到上面的A函数)
bx A_addr + 1 ;(A的地址A_addr的bit0 = 1,表示跳转过去执行 Thumb 指令)
...
/**********************/
对于A7、ARM7、ARM9 内核而言它们支持 16位的Thumb 指令集 和 32位的 ARM 指令集
对于M3、M4 内核而言它们支持的是 Thumb2 指令集,它支持16位、32位指令混合编程
对于内核来说使用的是 ARM指令集 还是 Thumb指令集,就是在 XPSR 和 CPSR
在M3/M4中, XPSR 寄存器的 T(bit24):1表示 Thumb指令集根据上面所述,M3是使用的 Thumb2 指令集,所以会有 T 总是 1.
在A7中 CPSR中的:T(bit5) :控制指令执行状态,表明本指令是 ARM 指令还是 Thumb 指令,通常和 J(bit24)一起表明指令类型
J(bit24) | T(bit5) | 指令集 |
---|---|---|
0 | 0 | ARM |
0 | 1 | Thumb |
1 | 1 | ThumbEE -- 提供从Thumb-2而来的一些扩充性,在所处的运行环境下,使得指令集能特别适用于运行阶段的编码产生(例如实时编译)。Thumb-2EE是专为一些语言如Limbo、Java、C#、Perl和Python,并能让实时编译器能够输出更小的编译码却不会影响到性能。 |
1 | 0 | Jazelle |
回到开始的指令 MOV R0,R1
code 16 ;(表示下面指令是16位的 Thumb 指令)
MOV R0,R1
code 32 ;(表示下面指令是32位的 ARM 指令)
MOV R0,R1
Thumb ;(编译器会根据指令自动识别是32位还是16位的 Thumb2)
MOV R0,R1
编码格式:
不同指令集的编码格式(以 LDR 为例),摘自《ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition》:以“数据处理”(其他的还有内存访问,分支跳转等)指令为例,UAL汇编格式为:Operation
表示各类汇编指令,比如 ADD、MOV;cond表示conditon,即该指令执行的条件,如 EQ,NE 等;S表示该指令执行后,是否会影响CPSR寄存器的值, 是否影响CPSR 寄存器的值,书写时影响CPSR,否则不影响;Rd
为目的寄存器,用来存储运算的结果;Rn 第一个操作数的寄存器Operand2第二个操作数 ,其可以有3种操作源:1-- 立即数 2-- 寄存器 3-- 寄存器移位
其指令编码格式如下(32位):|bit 31-28 |27-25 |24-21 |20 |19-16 | 15-12 |11-0 | |--|--|--|--|--|--|--|--|--| |cond | 001 |Operation |S |Rn |Rd | Operand2 |
举个例子:
...
CMP R0,R2 ;比较R0和R2的值
MOV EQ R0,R1 ;加上EQ,如果上面R0的值和R2的值相等的话,才执行此语句
...
对于“数据处理”处理指令中的Operation ,指令集如下:对于其中的条件cond ,如下:
在一条ARM数据处理指令中,除了要包含处理的数据值外,还要标识ARM命令名称,控制位,寄存器等其他信息。这样在一条ARM数据处理指令中,能用于表示要处理的数据值的位数只能小于32位;
在上面的ARM汇编格式中我们介绍过,ARM在指令格式中设定,只能用指令机器码32位中的低12位来表示要操作的常数。
那么对于指令MOV R0, #value
(把value的值存入R0寄存器)而言,value 的值也不能是任意的值,其值只能是符合某些规定的数,在官方文档中 value 的值需要满足如下条件:什么是立即数?
满足上图中条件的数我们称之为 立即数,立即数就是符合一定规矩的数。
立即数表示方式:每个立即数由一个8位的常数循环右移偶数位得到。其中循环右移的位数由一个4位二进制的两倍表示。
立即数 = 一个8位的常数 循环位移 偶数位
一个8bit常数循环右移(Y*2 = {0,2,4,6,8, ...,26, 28, 30})就得到一个立即数了;(为什么是0到30的偶数下面解释)
如果需要深入理解立即数,推荐一篇博文:深刻认识 -->> 立即数
ARM处理器是按32位来处理数据的,ARM处理器处理的数据是32位,为了扩展到32位,因此使用了构造的方法,在12位中用8位表示基本数据值,用4位表示位移值,通过用8位基本数据值往右循环移动4位位移值*2次,来表示要操作的常数。
这里要强调最终的循环次数是4位位移值乘以2得到的,所以得到的最终循环次数肯定是一个偶数,为什么要乘以2呢,实质还是因为范围不够,4位表示位移次数,最大才15次(移位0,等于没有循环),加上8位数据还是不够32位,这样只能通过ALU的内部结构设计将4位位移次数乘以2,这样就能用12位表示32位常数了。
所以 12bit 数据存放格式如下:|bit 11-8 |7-0 | |--|--|--|--|--|--|--|--|--| |移位 1111b (0~15) | 8bit常数 |
但是我们去判断一个数是否立即数,实在是太麻烦了,但是我们想把任意数值赋给 R0 寄存器,怎么办? 这就需要用到伪指令了,下面说一说什么是伪指令。
全部0条评论
快来发表一下你的评论吧 !